From 7415f9a5848853fb7a2b5b91bbe438d85728e33a Mon Sep 17 00:00:00 2001 From: jaseg Date: Thu, 11 Nov 2021 12:10:56 +0100 Subject: Aperture macro parser works --- gerbonara/gerber/aperture_macros/primitive.py | 172 ++++++++++++++++++++++++++ 1 file changed, 172 insertions(+) create mode 100644 gerbonara/gerber/aperture_macros/primitive.py (limited to 'gerbonara/gerber/aperture_macros/primitive.py') diff --git a/gerbonara/gerber/aperture_macros/primitive.py b/gerbonara/gerber/aperture_macros/primitive.py new file mode 100644 index 0000000..88552c5 --- /dev/null +++ b/gerbonara/gerber/aperture_macros/primitive.py @@ -0,0 +1,172 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright 2019 Hiroshi Murayama + +from dataclasses import dataclass, fields + +from .utils import * +from .am_statements import * +from .am_expression import * +from .am_opcode import OpCode + +class Primitive: + def __init__(self, unit, args): + self.unit = unit + + if len(args) > len(type(self).__annotations__): + raise ValueError(f'Too many arguments ({len(args)}) for aperture macro primitive {self.code} ({type(self)})') + + for arg, (name, fieldtype) in zip(args, type(self).__annotations__.items()): + if fieldtype == UnitExpression: + setattr(self, name, UnitExpression(arg, unit)) + else: + setattr(self, name, arg) + + for name, _type in type(self).__annotations__.items(): + if not hasattr(self, name): + raise ValueError(f'Too few arguments ({len(args)}) for aperture macro primitive {self.code} ({type(self)})') + + def to_gerber(self, unit=None): + return self.code + ',' + ','.join( + getattr(self, name).to_gerber(unit) for name, _type in type(self).__annotations__.items()) + '*' + +class CommentPrimitive(Primitive): + code = 0 + comment : str + +class CirclePrimitive(Primitive): + code = 1 + exposure : Expression + diameter : UnitExpression + center_x : UnitExpression + center_y : UnitExpression + rotation : Expression = ConstantExpression(0.0) + +class VectorLinePrimitive(Primitive): + code = 20 + exposure : Expression + width : UnitExpression + start_x : UnitExpression + start_y : UnitExpression + end_x : UnitExpression + end_y : UnitExpression + rotation : Expression + +class CenterLinePrimitive(Primitive): + code = 21 + exposure : Expression + width : UnitExpression + height : UnitExpression + x : UnitExpression + y : UnitExpression + rotation : Expression + + +class PolygonPrimitive(Primitive): + code = 5 + exposure : Expression + n_vertices : Expression + center_x : UnitExpression + center_y : UnitExpression + diameter : UnitExpression + rotation : Expression + + +class ThermalPrimitive(Primitive): + code = 7 + center_x : UnitExpression + center_y : UnitExpression + d_outer : UnitExpression + d_inner : UnitExpression + gap_w : UnitExpression + rotation : Expression + + +class OutlinePrimitive(Primitive): + code = 4 + + def __init__(self, code, unit, args): + if len(args) < 11: + raise ValueError(f'Invalid aperture macro outline primitive, not enough parameters ({len(args)}).') + if len(args) > 5004: + raise ValueError(f'Invalid aperture macro outline primitive, too many points ({len(args)//2-2}).') + + self.exposure = args[0] + + if args[1] != len(args)//2 - 2: + raise ValueError(f'Invalid aperture macro outline primitive, given size does not match length of coordinate list({len(args)}).') + + if len(args) % 1 != 1: + self.rotation = args.pop() + else: + self.rotation = ConstantExpression(0.0) + + if args[2] != args[-2] or args[3] != args[-1]: + raise ValueError(f'Invalid aperture macro outline primitive, polygon is not closed {args[2:4], args[-3:-1]}') + + self.coords = [UnitExpression(arg, unit) for arg in args[1:]] + + def to_gerber(self, unit=None): + coords = ','.join(coord.to_gerber(unit) for coord in self.coords) + return f'{self.code},{self.exposure.to_gerber()},{len(self.coords)//2-1},{coords},{self.rotation.to_gerber()}' + + +class VariableDef(object): + def __init__(self, number, value): + self.number = number + self.value = value + + def to_gerber(self, _unit=None): + return '$%d=%s*' % (self.number, self.value.to_gerber(settings)) + +PRIMITIVE_CLASSES = { + **{cls.code: cls for cls in [ + CommentPrimitive, + CirclePrimitive, + VectorLinePrimitive, + CenterLinePrimitive, + OutlinePrimitive, + PolygonPrimitive, + ThermalPrimitive, + ], + # alternative codes + 2: VectorLinePrimitive, +} + +def eval_macro(instructions, unit): + stack = [] + for opcode, argument in instructions: + if opcode == OpCode.PUSH: + stack.append(ConstantExpression(argument)) + + elif opcode == OpCode.LOAD: + stack.append(VariableExpression(argument)) + + elif opcode == OpCode.STORE: + yield VariableDef(code, stack.pop()) + + elif opcode == OpCode.ADD: + op1 = stack.pop() + op2 = stack.pop() + stack.append(OperatorExpression(OperatorExpression.ADD, op2, op1)) + + elif opcode == OpCode.SUB: + op1 = stack.pop() + op2 = stack.pop() + stack.append(OperatorExpression(OperatorExpression.SUB, op2, op1)) + + elif opcode == OpCode.MUL: + op1 = stack.pop() + op2 = stack.pop() + stack.append(OperatorExpression(OperatorExpression.MUL, op2, op1)) + + elif opcode == OpCode.DIV: + op1 = stack.pop() + op2 = stack.pop() + stack.append(OperatorExpression(OperatorExpression.DIV, op2, op1)) + + elif opcode == OpCode.PRIM: + yield PRIMITIVE_CLASSES[argument](unit=unit, args=stack) + stack = [] + -- cgit