Remove unnecessary statement class indirection layer
This commit is contained in:
parent
9e86bf6b3e
commit
40286fc92f
6 changed files with 92 additions and 272 deletions
|
|
@ -26,7 +26,7 @@ from collections import Counter
|
|||
from .cam import CamFile, FileSettings
|
||||
from .graphic_objects import Flash, Line, Arc
|
||||
from .apertures import ExcellonTool
|
||||
from .utils import Inch, MM
|
||||
from .utils import Inch, MM, InterpMode
|
||||
|
||||
def parse(data, settings=None):
|
||||
return ExcellonFile.parse(data, settings=settings)
|
||||
|
|
@ -208,11 +208,6 @@ class ProgramState(Enum):
|
|||
ROUTING = 2
|
||||
FINISHED = 2
|
||||
|
||||
class InterpMode(Enum):
|
||||
LINEAR = 0
|
||||
CIRCULAR_CW = 1
|
||||
CIRCULAR_CCW = 2
|
||||
|
||||
|
||||
class ExcellonParser(object):
|
||||
def __init__(self, settings=None):
|
||||
|
|
|
|||
|
|
@ -1,218 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# copyright 2014 Hamilton Kibbe <ham@hamiltonkib.be>
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
"""
|
||||
Gerber (RS-274X) Statements
|
||||
===========================
|
||||
**Gerber RS-274X file statement classes**
|
||||
|
||||
"""
|
||||
|
||||
# FIXME make this entire file obsolete and just return strings from graphical objects directly instead
|
||||
|
||||
class Statement:
|
||||
pass
|
||||
|
||||
class ParamStmt(Statement):
|
||||
pass
|
||||
|
||||
class FormatSpecStmt(ParamStmt):
|
||||
""" FS - Gerber Format Specification Statement """
|
||||
|
||||
def to_gerber(self, settings):
|
||||
zeros = 'T' if settings.zeros == 'trailing' else 'L' # default to leading if "None" is specified
|
||||
notation = 'I' if settings.notation == 'incremental' else 'A' # default to absolute
|
||||
number_format = str(settings.number_format[0]) + str(settings.number_format[1])
|
||||
|
||||
return f'%FS{zeros}{notation}X{number_format}Y{number_format}*%'
|
||||
|
||||
def __str__(self):
|
||||
return '<FS Format Specification>'
|
||||
|
||||
|
||||
class UnitStmt(ParamStmt):
|
||||
""" MO - Coordinate unit mode statement """
|
||||
|
||||
def to_gerber(self, settings):
|
||||
return '%MOMM*%' if settings.unit == 'mm' else '%MOIN*%'
|
||||
|
||||
def __str__(self):
|
||||
return ('<MO Coordinate unit mode statement>' % mode_str)
|
||||
|
||||
|
||||
class LoadPolarityStmt(ParamStmt):
|
||||
""" LP - Gerber Load Polarity statement """
|
||||
|
||||
def __init__(self, dark):
|
||||
self.dark = dark
|
||||
|
||||
def to_gerber(self, settings):
|
||||
lp = 'D' if self.dark else 'C'
|
||||
return f'%LP{lp}*%'
|
||||
|
||||
def __str__(self):
|
||||
lp = 'dark' if self.dark else 'clear'
|
||||
return f'<LP Level Polarity: {lp}>'
|
||||
|
||||
|
||||
class ApertureDefStmt(ParamStmt):
|
||||
""" AD - Aperture Definition Statement """
|
||||
|
||||
def __init__(self, number, aperture):
|
||||
self.number = number
|
||||
self.aperture = aperture
|
||||
|
||||
def to_gerber(self, settings):
|
||||
return f'%ADD{self.number}{self.aperture.to_gerber(settings)}*%'
|
||||
|
||||
def __str__(self):
|
||||
return f'<AD aperture def for {str(self.aperture).strip("<>")}>'
|
||||
|
||||
def __repr__(self):
|
||||
return f'ApertureDefStmt({self.number}, {repr(self.aperture)})'
|
||||
|
||||
|
||||
class ApertureMacroStmt(ParamStmt):
|
||||
""" AM - Aperture Macro Statement """
|
||||
|
||||
def __init__(self, macro):
|
||||
self.macro = macro
|
||||
|
||||
def to_gerber(self, settings):
|
||||
return f'%AM{self.macro.name}*\n{self.macro.to_gerber(unit=settings.unit)}*\n%'
|
||||
|
||||
def __str__(self):
|
||||
return f'<AM Aperture Macro {self.macro.name}: {self.macro}>'
|
||||
|
||||
|
||||
class ImagePolarityStmt(ParamStmt):
|
||||
""" IP - Image Polarity Statement. (Deprecated) """
|
||||
|
||||
def to_gerber(self, settings):
|
||||
#ip = 'POS' if settings.image_polarity == 'positive' else 'NEG'
|
||||
return f'%IPPOS*%'
|
||||
|
||||
def __str__(self):
|
||||
return '<IP Image Polarity>'
|
||||
|
||||
|
||||
class CoordStmt(Statement):
|
||||
""" D01 - D03 operation statements """
|
||||
|
||||
def __init__(self, x, y, i=None, j=None, unit=None):
|
||||
self.x, self.y, self.i, self.j = x, y, i, j
|
||||
self.unit = unit
|
||||
|
||||
def to_gerber(self, settings):
|
||||
ret = ''
|
||||
for var in 'xyij':
|
||||
val = self.unit.convert_to(settings.unit, getattr(self, var))
|
||||
if val is not None:
|
||||
ret += var.upper() + settings.write_gerber_value(val)
|
||||
return ret + self.code + '*'
|
||||
|
||||
def __str__(self):
|
||||
if self.i is None:
|
||||
return f'<{self.__name__.strip()} x={self.x} y={self.y}>'
|
||||
else:
|
||||
return f'<{self.__name__.strip()} x={self.x} y={self.y} i={self.i} j={self.j}>'
|
||||
|
||||
class InterpolateStmt(CoordStmt):
|
||||
""" D01 Interpolation """
|
||||
code = 'D01'
|
||||
|
||||
class MoveStmt(CoordStmt):
|
||||
""" D02 Move """
|
||||
code = 'D02'
|
||||
|
||||
class FlashStmt(CoordStmt):
|
||||
""" D03 Flash """
|
||||
code = 'D03'
|
||||
|
||||
class InterpolationModeStmt(Statement):
|
||||
""" G01 / G02 / G03 interpolation mode statement """
|
||||
def to_gerber(self, settings):
|
||||
return self.code + '*'
|
||||
|
||||
def __str__(self):
|
||||
return f'<{self.__doc__.strip()}>'
|
||||
|
||||
class LinearModeStmt(InterpolationModeStmt):
|
||||
""" G01 linear interpolation mode statement """
|
||||
code = 'G01'
|
||||
|
||||
class CircularCWModeStmt(InterpolationModeStmt):
|
||||
""" G02 circular interpolation mode statement """
|
||||
code = 'G02'
|
||||
|
||||
class CircularCCWModeStmt(InterpolationModeStmt):
|
||||
""" G03 circular interpolation mode statement """
|
||||
code = 'G03'
|
||||
|
||||
class SingleQuadrantModeStmt(InterpolationModeStmt):
|
||||
""" G75 single-quadrant arc interpolation mode statement """
|
||||
code = 'G75'
|
||||
|
||||
class RegionStartStmt(InterpolationModeStmt):
|
||||
""" G36 Region Mode Start Statement. """
|
||||
code = 'G36'
|
||||
|
||||
class RegionEndStmt(InterpolationModeStmt):
|
||||
""" G37 Region Mode End Statement. """
|
||||
code = 'G37'
|
||||
|
||||
class ApertureStmt(Statement):
|
||||
def __init__(self, d):
|
||||
self.d = int(d)
|
||||
|
||||
def to_gerber(self, settings):
|
||||
return 'D{0}*'.format(self.d)
|
||||
|
||||
def __str__(self):
|
||||
return '<Aperture: %d>' % self.d
|
||||
|
||||
|
||||
class CommentStmt(Statement):
|
||||
""" G04 Comment Statment """
|
||||
|
||||
def __init__(self, comment):
|
||||
self.comment = comment if comment is not None else ""
|
||||
|
||||
def to_gerber(self, settings):
|
||||
return f'G04{self.comment}*'
|
||||
|
||||
def __str__(self):
|
||||
return f'<G04 Comment: {self.comment}>'
|
||||
|
||||
|
||||
class EofStmt(Statement):
|
||||
""" M02 EOF Statement """
|
||||
|
||||
def to_gerber(self, settings):
|
||||
return 'M02*'
|
||||
|
||||
def __str__(self):
|
||||
return '<M02 EOF Statement>'
|
||||
|
||||
class UnknownStmt(Statement):
|
||||
def __init__(self, line):
|
||||
self.line = line
|
||||
|
||||
def to_gerber(self, settings):
|
||||
return self.line
|
||||
|
||||
def __str__(self):
|
||||
return f'<Unknown Statement: "{self.line}">'
|
||||
|
|
@ -2,9 +2,8 @@
|
|||
import math
|
||||
from dataclasses import dataclass, KW_ONLY, astuple, replace, fields
|
||||
|
||||
from .utils import MM
|
||||
from .utils import MM, InterpMode
|
||||
from . import graphic_primitives as gp
|
||||
from .gerber_statements import *
|
||||
|
||||
|
||||
def convert(value, src, dst):
|
||||
|
|
@ -76,15 +75,21 @@ class Flash(GerberObject):
|
|||
def to_statements(self, gs):
|
||||
yield from gs.set_polarity(self.polarity_dark)
|
||||
yield from gs.set_aperture(self.aperture)
|
||||
yield FlashStmt(self.x, self.y, unit=self.unit)
|
||||
|
||||
x = gs.file_settings.write_gerber_value(self.x, self.unit)
|
||||
y = gs.file_settings.write_gerber_value(self.y, self.unit)
|
||||
yield f'D03X{x}Y{y}*'
|
||||
|
||||
gs.update_point(self.x, self.y, unit=self.unit)
|
||||
|
||||
def to_xnc(self, ctx):
|
||||
yield from ctx.select_tool(self.tool)
|
||||
yield from ctx.drill_mode()
|
||||
|
||||
x = ctx.settings.write_gerber_value(self.x, self.unit)
|
||||
y = ctx.settings.write_gerber_value(self.y, self.unit)
|
||||
yield f'X{x}Y{y}'
|
||||
|
||||
ctx.set_current_point(self.unit, self.x, self.y)
|
||||
|
||||
def curve_length(self, unit=MM):
|
||||
|
|
@ -143,24 +148,35 @@ class Region(GerberObject):
|
|||
|
||||
def to_statements(self, gs):
|
||||
yield from gs.set_polarity(self.polarity_dark)
|
||||
yield RegionStartStmt()
|
||||
yield 'G36*'
|
||||
|
||||
yield from gs.set_current_point(self.poly.outline[0], unit=self.unit)
|
||||
|
||||
for point, arc_center in zip(self.poly.outline[1:], self.poly.arc_centers):
|
||||
if arc_center is None:
|
||||
yield from gs.set_interpolation_mode(LinearModeStmt)
|
||||
yield InterpolateStmt(*point, unit=self.unit)
|
||||
yield from gs.set_interpolation_mode(InterpMode.LINEAR)
|
||||
|
||||
x = gs.file_settings.write_gerber_value(point[0], self.unit)
|
||||
y = gs.file_settings.write_gerber_value(point[1], self.unit)
|
||||
yield f'D01X{x}Y{y}*'
|
||||
|
||||
gs.update_point(*point, unit=self.unit)
|
||||
|
||||
else:
|
||||
clockwise, (cx, cy) = arc_center
|
||||
x2, y2 = point
|
||||
yield from gs.set_interpolation_mode(CircularCWModeStmt if clockwise else CircularCCWModeStmt)
|
||||
yield InterpolateStmt(x2, y2, cx-x2, cy-y2, unit=self.unit)
|
||||
yield from gs.set_interpolation_mode(InterpMode.CIRCULAR_CW if clockwise else InterpMode.CIRCULAR_CCW)
|
||||
|
||||
x = gs.file_settings.write_gerber_value(x2, self.unit)
|
||||
y = gs.file_settings.write_gerber_value(y2, self.unit)
|
||||
# TODO are these coordinates absolute or relative now?!
|
||||
i = gs.file_settings.write_gerber_value(cx-x2, self.unit)
|
||||
j = gs.file_settings.write_gerber_value(cy-y2, self.unit)
|
||||
yield f'D01X{x}Y{y}I{i}J{j}*'
|
||||
|
||||
gs.update_point(x2, y2, unit=self.unit)
|
||||
|
||||
yield RegionEndStmt()
|
||||
yield 'G37*'
|
||||
|
||||
|
||||
@dataclass
|
||||
|
|
@ -207,15 +223,23 @@ class Line(GerberObject):
|
|||
def to_statements(self, gs):
|
||||
yield from gs.set_polarity(self.polarity_dark)
|
||||
yield from gs.set_aperture(self.aperture)
|
||||
yield from gs.set_interpolation_mode(LinearModeStmt)
|
||||
yield from gs.set_interpolation_mode(InterpMode.LINEAR)
|
||||
yield from gs.set_current_point(self.p1, unit=self.unit)
|
||||
yield InterpolateStmt(*self.p2, unit=self.unit)
|
||||
|
||||
x = gs.file_settings.write_gerber_value(self.x2, self.unit)
|
||||
y = gs.file_settings.write_gerber_value(self.y2, self.unit)
|
||||
yield f'D01X{x}Y{y}*'
|
||||
|
||||
gs.update_point(*self.p2, unit=self.unit)
|
||||
|
||||
def to_xnc(self, ctx):
|
||||
yield from ctx.select_tool(self.tool)
|
||||
yield from ctx.route_mode(self.unit, *self.p1)
|
||||
yield 'G01' + 'X' + ctx.settings.write_gerber_value(self.p2[0], self.unit) + 'Y' + ctx.settings.write_gerber_value(self.p2[1], self.unit)
|
||||
|
||||
x = ctx.settings.write_gerber_value(self.x2, self.unit)
|
||||
y = ctx.settings.write_gerber_value(self.y2, self.unit)
|
||||
yield f'G01X{x}Y{y}'
|
||||
|
||||
ctx.set_current_point(self.unit, *self.p2)
|
||||
|
||||
def curve_length(self, unit=MM):
|
||||
|
|
@ -280,20 +304,29 @@ class Arc(GerberObject):
|
|||
def to_statements(self, gs):
|
||||
yield from gs.set_polarity(self.polarity_dark)
|
||||
yield from gs.set_aperture(self.aperture)
|
||||
yield from gs.set_interpolation_mode(CircularCCWModeStmt)
|
||||
# TODO is the following line correct?
|
||||
yield from gs.set_interpolation_mode(InterpMode.CIRCULAR_CW if self.clockwise else InterpMode.CIRCULAR_CCW)
|
||||
yield from gs.set_current_point(self.p1, unit=self.unit)
|
||||
yield InterpolateStmt(self.x2, self.y2, self.cx, self.cy, unit=self.unit)
|
||||
|
||||
x = gs.file_settings.write_gerber_value(self.x2, self.unit)
|
||||
y = gs.file_settings.write_gerber_value(self.y2, self.unit)
|
||||
i = gs.file_settings.write_gerber_value(self.cx, self.unit)
|
||||
j = gs.file_settings.write_gerber_value(self.cy, self.unit)
|
||||
yield f'D01X{x}Y{y}I{i}J{j}*'
|
||||
|
||||
gs.update_point(*self.p2, unit=self.unit)
|
||||
|
||||
def to_xnc(self, ctx):
|
||||
yield from ctx.select_tool(self.tool)
|
||||
yield from ctx.route_mode(self.unit, self.x1, self.y1)
|
||||
code = 'G02' if self.clockwise else 'G03'
|
||||
|
||||
x = ctx.settings.write_gerber_value(self.x2, self.unit)
|
||||
y = ctx.settings.write_gerber_value(self.y2, self.unit)
|
||||
i = ctx.settings.write_gerber_value(self.cx - self.x1, self.unit)
|
||||
j = ctx.settings.write_gerber_value(self.cy - self.y1, self.unit)
|
||||
i = ctx.settings.write_gerber_value(self.cx, self.unit)
|
||||
j = ctx.settings.write_gerber_value(self.cy, self.unit)
|
||||
yield f'{code}X{x}Y{y}I{i}J{j}'
|
||||
|
||||
ctx.set_current_point(self.unit, self.x2, self.y2)
|
||||
|
||||
def curve_length(self, unit=MM):
|
||||
|
|
|
|||
|
|
@ -4,8 +4,6 @@ import itertools
|
|||
|
||||
from dataclasses import dataclass, KW_ONLY, replace
|
||||
|
||||
from .gerber_statements import *
|
||||
|
||||
|
||||
@dataclass
|
||||
class GraphicPrimitive:
|
||||
|
|
|
|||
|
|
@ -33,9 +33,8 @@ from itertools import count, chain
|
|||
from io import StringIO
|
||||
import textwrap
|
||||
|
||||
from .gerber_statements import *
|
||||
from .cam import CamFile, FileSettings
|
||||
from .utils import sq_distance, rotate_point, MM, Inch, units
|
||||
from .utils import sq_distance, rotate_point, MM, Inch, units, InterpMode
|
||||
from .aperture_macros.parse import ApertureMacro, GenericMacros
|
||||
from . import graphic_primitives as gp
|
||||
from . import graphic_objects as go
|
||||
|
|
@ -215,25 +214,28 @@ class GerberFile(CamFile):
|
|||
|
||||
return ((min_x, min_y), (max_x, max_y))
|
||||
|
||||
def generate_statements(self, drop_comments=True):
|
||||
yield UnitStmt()
|
||||
yield FormatSpecStmt()
|
||||
yield ImagePolarityStmt()
|
||||
yield SingleQuadrantModeStmt()
|
||||
yield LoadPolarityStmt(True)
|
||||
def generate_statements(self, settings, drop_comments=True):
|
||||
yield '%MOMM*%' if (settings.unit == 'mm') else '%MOIN*%'
|
||||
|
||||
zeros = 'T' if settings.zeros == 'trailing' else 'L' # default to leading if "None" is specified
|
||||
notation = 'I' if settings.notation == 'incremental' else 'A' # default to absolute
|
||||
number_format = str(settings.number_format[0]) + str(settings.number_format[1])
|
||||
yield f'%FS{zeros}{notation}X{number_format}Y{number_format}*%'
|
||||
yield '%IPPOS*%'
|
||||
yield 'G75'
|
||||
yield '%LPD*%'
|
||||
|
||||
if not drop_comments:
|
||||
yield CommentStmt('File processed by Gerbonara. Original comments:')
|
||||
yield 'G04 File processed by Gerbonara. Original comments:'
|
||||
for cmt in self.comments:
|
||||
yield CommentStmt(cmt)
|
||||
yield f'G04{cmt}'
|
||||
|
||||
# Always emit gerbonara's generic, rotation-capable aperture macro replacements for the standard C/R/O/P shapes.
|
||||
# Unconditionally emitting these here is easier than first trying to figure out if we need them later,
|
||||
# and they are only a few bytes anyway.
|
||||
yield ApertureMacroStmt(GenericMacros.circle)
|
||||
yield ApertureMacroStmt(GenericMacros.rect)
|
||||
yield ApertureMacroStmt(GenericMacros.obround)
|
||||
yield ApertureMacroStmt(GenericMacros.polygon)
|
||||
am_stmt = lambda macro: f'%AM{macro.name}*\n{macro.to_gerber(unit=settings.unit)}*\n%'
|
||||
for macro in [ GenericMacros.circle, GenericMacros.rect, GenericMacros.obround, GenericMacros.polygon ]:
|
||||
yield am_stmt(macro)
|
||||
|
||||
processed_macros = set()
|
||||
aperture_map = {}
|
||||
|
|
@ -243,17 +245,17 @@ class GerberFile(CamFile):
|
|||
macro_grb = aperture._rotated().macro.to_gerber() # use native unit to compare macros
|
||||
if macro_grb not in processed_macros:
|
||||
processed_macros.add(macro_grb)
|
||||
yield ApertureMacroStmt(aperture._rotated().macro)
|
||||
yield am_stmt(aperture._rotated().macro)
|
||||
|
||||
yield ApertureDefStmt(number, aperture)
|
||||
yield f'%ADD{number}{aperture.to_gerber(settings)}*%'
|
||||
|
||||
aperture_map[id(aperture)] = number
|
||||
|
||||
gs = GraphicsState(aperture_map=aperture_map)
|
||||
gs = GraphicsState(aperture_map=aperture_map, file_settings=settings)
|
||||
for primitive in self.objects:
|
||||
yield from primitive.to_statements(gs)
|
||||
|
||||
yield EofStmt()
|
||||
yield 'M02*'
|
||||
|
||||
def __str__(self):
|
||||
return f'<GerberFile with {len(self.apertures)} apertures, {len(self.objects)} objects>'
|
||||
|
|
@ -269,7 +271,7 @@ class GerberFile(CamFile):
|
|||
settings = self.import_settings.copy() or FileSettings()
|
||||
settings.zeros = None
|
||||
settings.number_format = (5,6)
|
||||
return '\n'.join(stmt.to_gerber(settings) for stmt in self.generate_statements())
|
||||
return '\n'.join(self.generate_statements(settings))
|
||||
|
||||
def offset(self, dx=0, dy=0, unit=MM):
|
||||
# TODO round offset to file resolution
|
||||
|
|
@ -308,7 +310,7 @@ class GraphicsState:
|
|||
point : tuple = None
|
||||
aperture : apertures.Aperture = None
|
||||
file_settings : FileSettings = None
|
||||
interpolation_mode : InterpolationModeStmt = LinearModeStmt
|
||||
interpolation_mode : InterpMode = InterpMode.LINEAR
|
||||
multi_quadrant_mode : bool = None # used only for syntax checking
|
||||
aperture_mirroring = (False, False) # LM mirroring (x, y)
|
||||
aperture_rotation = 0 # LR rotation in degree, ccw
|
||||
|
|
@ -411,7 +413,7 @@ class GraphicsState:
|
|||
'pass through the created objects here. Note that these will not show up in e.g. SVG output since '
|
||||
'their line width is zero.', SyntaxWarning)
|
||||
|
||||
if self.interpolation_mode == LinearModeStmt:
|
||||
if self.interpolation_mode == InterpMode.LINEAR:
|
||||
if i is not None or j is not None:
|
||||
raise SyntaxError("i/j coordinates given for linear D01 operation (which doesn't take i/j)")
|
||||
|
||||
|
|
@ -437,7 +439,7 @@ class GraphicsState:
|
|||
polarity_dark=self.polarity_dark, unit=self.file_settings.unit)
|
||||
|
||||
def _create_arc(self, old_point, new_point, control_point, aperture=True):
|
||||
clockwise = self.interpolation_mode == CircularCWModeStmt
|
||||
clockwise = self.interpolation_mode == InterpMode.CIRCULAR_CW
|
||||
return go.Arc(*old_point, *new_point, *self.map_coord(*control_point, relative=True),
|
||||
clockwise=clockwise, aperture=(self.aperture if aperture else None),
|
||||
polarity_dark=self.polarity_dark, unit=self.file_settings.unit)
|
||||
|
|
@ -458,12 +460,12 @@ class GraphicsState:
|
|||
def set_polarity(self, polarity_dark):
|
||||
if self.polarity_dark != polarity_dark:
|
||||
self.polarity_dark = polarity_dark
|
||||
yield LoadPolarityStmt(polarity_dark)
|
||||
yield '%LPD*%' if polarity_dark else '%LPC*%'
|
||||
|
||||
def set_aperture(self, aperture):
|
||||
if self.aperture != aperture:
|
||||
self.aperture = aperture
|
||||
yield ApertureStmt(self.aperture_map[id(aperture)])
|
||||
yield f'D{self.aperture_map[id(aperture)]}*'
|
||||
|
||||
def set_current_point(self, point, unit=None):
|
||||
point_mm = MM(point[0], unit), MM(point[1], unit)
|
||||
|
|
@ -471,12 +473,14 @@ class GraphicsState:
|
|||
|
||||
if not points_close(self.point, point_mm):
|
||||
self.point = point_mm
|
||||
yield MoveStmt(*point, unit=unit)
|
||||
x = self.file_settings.write_gerber_value(point[0], unit=unit)
|
||||
y = self.file_settings.write_gerber_value(point[1], unit=unit)
|
||||
yield f'D02X{x}Y{y}*'
|
||||
|
||||
def set_interpolation_mode(self, mode):
|
||||
if self.interpolation_mode != mode:
|
||||
self.interpolation_mode = mode
|
||||
yield mode()
|
||||
yield {InterpMode.LINEAR: 'G01', InterpMode.CIRCULAR_CW: 'G02', InterpMode.CIRCULAR_CCW: 'G03'}[mode]
|
||||
|
||||
|
||||
class GerberParser:
|
||||
|
|
@ -591,11 +595,11 @@ class GerberParser:
|
|||
|
||||
def _parse_interpolation_mode(self, match):
|
||||
if match['code'] == 'G01':
|
||||
self.graphics_state.interpolation_mode = LinearModeStmt
|
||||
self.graphics_state.interpolation_mode = InterpMode.LINEAR
|
||||
elif match['code'] == 'G02':
|
||||
self.graphics_state.interpolation_mode = CircularCWModeStmt
|
||||
self.graphics_state.interpolation_mode = InterpMode.CIRCULAR_CW
|
||||
elif match['code'] == 'G03':
|
||||
self.graphics_state.interpolation_mode = CircularCCWModeStmt
|
||||
self.graphics_state.interpolation_mode = InterpMode.CIRCULAR_CCW
|
||||
elif match['code'] == 'G74':
|
||||
self.multi_quadrant_mode = True # used only for syntax checking
|
||||
elif match['code'] == 'G75':
|
||||
|
|
@ -620,7 +624,7 @@ class GerberParser:
|
|||
self.last_operation = op
|
||||
|
||||
if op in ('D1', 'D01'):
|
||||
if self.graphics_state.interpolation_mode != LinearModeStmt:
|
||||
if self.graphics_state.interpolation_mode != InterpMode.LINEAR:
|
||||
if self.multi_quadrant_mode is None:
|
||||
warnings.warn('Circular arc interpolation without explicit G75 Single-Quadrant mode statement. '\
|
||||
'This can cause problems with older gerber interpreters.', SyntaxWarning)
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ files.
|
|||
"""
|
||||
|
||||
import os
|
||||
from enum import Enum
|
||||
from math import radians, sin, cos, sqrt, atan2, pi
|
||||
|
||||
|
||||
|
|
@ -75,6 +76,12 @@ units = {'inch': Inch, 'mm': MM, None: None}
|
|||
to_unit = lambda name: units[name]
|
||||
|
||||
|
||||
class InterpMode(Enum):
|
||||
LINEAR = 0
|
||||
CIRCULAR_CW = 1
|
||||
CIRCULAR_CCW = 2
|
||||
|
||||
|
||||
def decimal_string(value, precision=6, padding=False):
|
||||
""" Convert float to string with limited precision
|
||||
|
||||
|
|
@ -161,3 +168,4 @@ def sq_distance(point1, point2):
|
|||
diff2 = point1[1] - point2[1]
|
||||
return diff1 * diff1 + diff2 * diff2
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue