summaryrefslogtreecommitdiff
path: root/gerbonara/gerber/aperture_macros/primitive.py
diff options
context:
space:
mode:
Diffstat (limited to 'gerbonara/gerber/aperture_macros/primitive.py')
-rw-r--r--gerbonara/gerber/aperture_macros/primitive.py270
1 files changed, 0 insertions, 270 deletions
diff --git a/gerbonara/gerber/aperture_macros/primitive.py b/gerbonara/gerber/aperture_macros/primitive.py
deleted file mode 100644
index 8732520..0000000
--- a/gerbonara/gerber/aperture_macros/primitive.py
+++ /dev/null
@@ -1,270 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-# Copyright 2019 Hiroshi Murayama <opiopan@gmail.com>
-# Copyright 2022 Jan Götte <gerbonara@jaseg.de>
-
-import warnings
-import contextlib
-import math
-
-from .expression import Expression, UnitExpression, ConstantExpression, expr
-
-from .. import graphic_primitives as gp
-
-
-def point_distance(a, b):
- x1, y1 = a
- x2, y2 = b
- return math.sqrt((x2 - x1)**2 + (y2 - y1)**2)
-
-def deg_to_rad(a):
- return (a / 180) * math.pi
-
-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()):
- arg = expr(arg) # convert int/float to Expression object
-
- if fieldtype == UnitExpression:
- setattr(self, name, UnitExpression(arg, unit))
- else:
- setattr(self, name, arg)
-
- for name in type(self).__annotations__:
- 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 f'{self.code},' + ','.join(
- getattr(self, name).to_gerber(unit) for name in type(self).__annotations__)
-
- def __str__(self):
- attrs = ','.join(str(getattr(self, name)).strip('<>') for name in type(self).__annotations__)
- return f'<{type(self).__name__} {attrs}>'
-
- def __repr__(self):
- return str(self)
-
- class Calculator:
- def __init__(self, instance, variable_binding={}, unit=None):
- self.instance = instance
- self.variable_binding = variable_binding
- self.unit = unit
-
- def __enter__(self):
- return self
-
- def __exit__(self, _type, _value, _traceback):
- pass
-
- def __getattr__(self, name):
- return getattr(self.instance, name).calculate(self.variable_binding, self.unit)
-
- def __call__(self, expr):
- return expr.calculate(self.variable_binding, self.unit)
-
-
-class Circle(Primitive):
- code = 1
- exposure : Expression
- diameter : UnitExpression
- # center x/y
- x : UnitExpression
- y : UnitExpression
- rotation : Expression = None
-
- def __init__(self, unit, args):
- super().__init__(unit, args)
- if self.rotation is None:
- self.rotation = ConstantExpression(0)
-
- def to_graphic_primitives(self, offset, rotation, variable_binding={}, unit=None, polarity_dark=True):
- with self.Calculator(self, variable_binding, unit) as calc:
- x, y = gp.rotate_point(calc.x, calc.y, deg_to_rad(calc.rotation) + rotation, 0, 0)
- x, y = x+offset[0], y+offset[1]
- return [ gp.Circle(x, y, calc.diameter/2, polarity_dark=(bool(calc.exposure) == polarity_dark)) ]
-
- def dilate(self, offset, unit):
- self.diameter += UnitExpression(offset, unit)
-
-class VectorLine(Primitive):
- code = 20
- exposure : Expression
- width : UnitExpression
- start_x : UnitExpression
- start_y : UnitExpression
- end_x : UnitExpression
- end_y : UnitExpression
- rotation : Expression
-
- def to_graphic_primitives(self, offset, rotation, variable_binding={}, unit=None, polarity_dark=True):
- with self.Calculator(self, variable_binding, unit) as calc:
- center_x = (calc.end_x + calc.start_x) / 2
- center_y = (calc.end_y + calc.start_y) / 2
- delta_x = calc.end_x - calc.start_x
- delta_y = calc.end_y - calc.start_y
- length = point_distance((calc.start_x, calc.start_y), (calc.end_x, calc.end_y))
-
- center_x, center_y = center_x+offset[0], center_y+offset[1]
- rotation += deg_to_rad(calc.rotation) + math.atan2(delta_y, delta_x)
-
- return [ gp.Rectangle(center_x, center_y, length, calc.width, rotation=rotation,
- polarity_dark=(bool(calc.exposure) == polarity_dark)) ]
-
- def dilate(self, offset, unit):
- self.width += UnitExpression(2*offset, unit)
-
-
-class CenterLine(Primitive):
- code = 21
- exposure : Expression
- width : UnitExpression
- height : UnitExpression
- # center x/y
- x : UnitExpression
- y : UnitExpression
- rotation : Expression
-
- def to_graphic_primitives(self, offset, rotation, variable_binding={}, unit=None, polarity_dark=True):
- with self.Calculator(self, variable_binding, unit) as calc:
- rotation += deg_to_rad(calc.rotation)
- x, y = gp.rotate_point(calc.x, calc.y, rotation, 0, 0)
- x, y = x+offset[0], y+offset[1]
- w, h = calc.width, calc.height
-
- return [ gp.Rectangle(x, y, w, h, rotation, polarity_dark=(bool(calc.exposure) == polarity_dark)) ]
-
- def dilate(self, offset, unit):
- self.width += UnitExpression(2*offset, unit)
-
-
-class Polygon(Primitive):
- code = 5
- exposure : Expression
- n_vertices : Expression
- # center x/y
- x : UnitExpression
- y : UnitExpression
- diameter : UnitExpression
- rotation : Expression
-
- def to_graphic_primitives(self, offset, rotation, variable_binding={}, unit=None, polarity_dark=True):
- with self.Calculator(self, variable_binding, unit) as calc:
- rotation += deg_to_rad(calc.rotation)
- x, y = gp.rotate_point(calc.x, calc.y, rotation, 0, 0)
- x, y = x+offset[0], y+offset[1]
- return [ gp.RegularPolygon(calc.x, calc.y, calc.diameter/2, calc.n_vertices, rotation,
- polarity_dark=(bool(calc.exposure) == polarity_dark)) ]
-
- def dilate(self, offset, unit):
- self.diameter += UnitExpression(2*offset, unit)
-
-
-class Thermal(Primitive):
- code = 7
- exposure : Expression
- # center x/y
- x : UnitExpression
- y : UnitExpression
- d_outer : UnitExpression
- d_inner : UnitExpression
- gap_w : UnitExpression
- rotation : Expression
-
- def to_graphic_primitives(self, offset, rotation, variable_binding={}, unit=None, polarity_dark=True):
- with self.Calculator(self, variable_binding, unit) as calc:
- rotation += deg_to_rad(calc.rotation)
- x, y = gp.rotate_point(calc.x, calc.y, rotation, 0, 0)
- x, y = x+offset[0], y+offset[1]
-
- dark = (bool(calc.exposure) == polarity_dark)
-
- return [
- gp.Circle(x, y, calc.d_outer/2, polarity_dark=dark),
- gp.Circle(x, y, calc.d_inner/2, polarity_dark=not dark),
- gp.Rectangle(x, y, d_outer, gap_w, rotation=rotation, polarity_dark=not dark),
- gp.Rectangle(x, y, gap_w, d_outer, rotation=rotation, polarity_dark=not dark),
- ]
-
- def dilate(self, offset, unit):
- # I'd rather print a warning and produce graphically slightly incorrect output in these few cases here than
- # producing macros that may evaluate to primitives with negative values.
- warnings.warn('Attempted dilation of macro aperture thermal primitive. This is not supported.')
-
-
-class Outline(Primitive):
- code = 4
-
- def __init__(self, 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.pop(0)
-
- # length arg must not contain variables (that would not make sense)
- length_arg = args.pop(0).calculate()
-
- if length_arg != len(args)//2-1:
- raise ValueError(f'Invalid aperture macro outline primitive, given size {length_arg} does not match length of coordinate list({len(args)//2-1}).')
-
- if len(args) % 2 == 1:
- self.rotation = args.pop()
- else:
- self.rotation = ConstantExpression(0.0)
-
- if args[0] != args[-2] or args[1] != args[-1]:
- raise ValueError(f'Invalid aperture macro outline primitive, polygon is not closed {args[2:4], args[-3:-1]}')
-
- self.coords = [(UnitExpression(x, unit), UnitExpression(y, unit)) for x, y in zip(args[0::2], args[1::2])]
-
- def __str__(self):
- return f'<Outline {len(self.coords)} points>'
-
- def to_gerber(self, unit=None):
- coords = ','.join(coord.to_gerber(unit) for xy in self.coords for coord in xy)
- return f'{self.code},{self.exposure.to_gerber()},{len(self.coords)-1},{coords},{self.rotation.to_gerber()}'
-
- def to_graphic_primitives(self, offset, rotation, variable_binding={}, unit=None, polarity_dark=True):
- with self.Calculator(self, variable_binding, unit) as calc:
- rotation += deg_to_rad(calc.rotation)
- bound_coords = [ gp.rotate_point(calc(x), calc(y), rotation, 0, 0) for x, y in self.coords ]
- bound_coords = [ (x+offset[0], y+offset[1]) for x, y in bound_coords ]
- bound_radii = [None] * len(bound_coords)
- return [gp.ArcPoly(bound_coords, bound_radii, polarity_dark=(bool(calc.exposure) == polarity_dark))]
-
- def dilate(self, offset, unit):
- # we would need a whole polygon offset/clipping library here
- warnings.warn('Attempted dilation of macro aperture outline primitive. This is not supported.')
-
-
-class Comment:
- code = 0
-
- def __init__(self, comment):
- self.comment = comment
-
- def to_gerber(self, unit=None):
- return f'0 {self.comment}'
-
-PRIMITIVE_CLASSES = {
- **{cls.code: cls for cls in [
- Comment,
- Circle,
- VectorLine,
- CenterLine,
- Outline,
- Polygon,
- Thermal,
- ]},
- # alternative codes
- 2: VectorLine,
-}
-