summaryrefslogtreecommitdiff
path: root/gerbonara/gerber/aperture_macros
diff options
context:
space:
mode:
Diffstat (limited to 'gerbonara/gerber/aperture_macros')
-rw-r--r--gerbonara/gerber/aperture_macros/expression.py41
-rw-r--r--gerbonara/gerber/aperture_macros/parse.py14
-rw-r--r--gerbonara/gerber/aperture_macros/primitive.py29
3 files changed, 84 insertions, 0 deletions
diff --git a/gerbonara/gerber/aperture_macros/expression.py b/gerbonara/gerber/aperture_macros/expression.py
index 390b7b7..fb399d3 100644
--- a/gerbonara/gerber/aperture_macros/expression.py
+++ b/gerbonara/gerber/aperture_macros/expression.py
@@ -90,6 +90,47 @@ class UnitExpression(Expression):
else:
raise ValueError(f'invalid unit {unit}, must be "inch" or "mm".')
+ def __add__(self, other):
+ if not isinstance(other, UnitExpression):
+ raise ValueError('Unit mismatch: Can only add/subtract UnitExpression from UnitExpression, not scalar.')
+
+ if self.unit == other.unit or self.unit is None or other.unit is None:
+ return UnitExpression(self._expr + other._expr, self.unit)
+
+ if other.unit == 'mm': # -> and self.unit == 'inch'
+ return UnitExpression(self._expr + (other._expr / MILLIMETERS_PER_INCH), self.unit)
+ else: # other.unit == 'inch' and self.unit == 'mm'
+ return UnitExpression(self._expr + (other._expr * MILLIMETERS_PER_INCH), self.unit)
+
+ def __radd__(self, other):
+ # left hand side cannot have been an UnitExpression or __radd__ would not have been called
+ raise ValueError('Unit mismatch: Can only add/subtract UnitExpression from UnitExpression, not scalar.')
+
+ def __sub__(self, other):
+ return (self + (-other)).optimize()
+
+ def __rsub__(self, other):
+ # see __radd__ above
+ raise ValueError('Unit mismatch: Can only add/subtract UnitExpression from UnitExpression, not scalar.')
+
+ def __mul__(self, other):
+ return UnitExpression(self._expr * other, self.unit)
+
+ def __rmul__(self, other):
+ return UnitExpression(other * self._expr, self.unit)
+
+ def __truediv__(self, other):
+ return UnitExpression(self._expr / other, self.unit)
+
+ def __rtruediv__(self, other):
+ return UnitExpression(other / self._expr, self.unit)
+
+ def __neg__(self):
+ return UnitExpression(-self._expr, self.unit)
+
+ def __pos__(self):
+ return self
+
class ConstantExpression(Expression):
def __init__(self, value):
diff --git a/gerbonara/gerber/aperture_macros/parse.py b/gerbonara/gerber/aperture_macros/parse.py
index 86f2882..00227c6 100644
--- a/gerbonara/gerber/aperture_macros/parse.py
+++ b/gerbonara/gerber/aperture_macros/parse.py
@@ -98,6 +98,20 @@ class ApertureMacro:
def __hash__(self):
return hash(self.to_gerber())
+ def dilated(self, offset, unit='mm'):
+ dup = copy.deepcopy(self)
+ new_primitives = []
+ for primitive in dup.primitives:
+ try:
+ if primitive.exposure.calculate():
+ primitive.dilate(offset, unit)
+ new_primitives.append(primitive)
+ except IndexError:
+ warnings.warn('Cannot dilate aperture macro primitive with exposure value computed from macro variable.')
+ pass
+ dup.primitives = new_primitives
+ return dup
+
def to_gerber(self, unit=None):
comments = [ c.to_gerber() for c in self.comments ]
variable_defs = [ f'${var.to_gerber(unit)}={expr}' for var, expr in self.variables.items() ]
diff --git a/gerbonara/gerber/aperture_macros/primitive.py b/gerbonara/gerber/aperture_macros/primitive.py
index a587d7e..b28fdb5 100644
--- a/gerbonara/gerber/aperture_macros/primitive.py
+++ b/gerbonara/gerber/aperture_macros/primitive.py
@@ -4,6 +4,7 @@
# Copyright 2019 Hiroshi Murayama <opiopan@gmail.com>
# Copyright 2022 Jan Götte <gerbonara@jaseg.de>
+import warnings
import contextlib
import math
@@ -20,6 +21,13 @@ def point_distance(a, b):
def deg_to_rad(a):
return (a / 180) * math.pi
+def convert(value, src, dst):
+ if src == dst or src is None or dst is None or value is None:
+ return value
+ elif dst == 'mm':
+ return value * 25.4
+ else:
+ return value / 25.4
class Primitive:
def __init__(self, unit, args):
@@ -88,6 +96,9 @@ class Circle(Primitive):
x, y = x+offset[0], y+offset[1]
return [ gp.Circle(x, y, calc.r, polarity_dark=bool(calc.exposure)) ]
+ def dilate(self, offset, unit):
+ self.diameter += UnitExpression(offset, unit)
+
class VectorLine(Primitive):
code = 20
exposure : Expression
@@ -112,6 +123,9 @@ class VectorLine(Primitive):
return [ gp.Rectangle(center_x, center_y, length, calc.width, rotation=rotation,
polarity_dark=bool(calc.exposure)) ]
+ def dilate(self, offset, unit):
+ self.width += UnitExpression(2*offset, unit)
+
class CenterLine(Primitive):
code = 21
@@ -131,6 +145,9 @@ class CenterLine(Primitive):
w, h = calc.width, calc.height
return [ gp.Rectangle(x, y, w, h, rotation, polarity_dark=bool(calc.exposure)) ]
+
+ def dilate(self, offset, unit):
+ self.width += UnitExpression(2*offset, unit)
class Polygon(Primitive):
@@ -151,6 +168,9 @@ class Polygon(Primitive):
return [ gp.RegularPolygon(calc.x, calc.y, calc.diameter/2, calc.n_vertices, rotation,
polarity_dark=bool(calc.exposure)) ]
+ def dilate(self, offset, unit):
+ self.diameter += UnitExpression(2*offset, unit)
+
class Thermal(Primitive):
code = 7
@@ -178,6 +198,11 @@ class Thermal(Primitive):
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
@@ -220,6 +245,10 @@ class Outline(Primitive):
return gp.ArcPoly(bound_coords, bound_radii, polarity_dark=calc.exposure)
+ 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