From ccb6eb7a766bd6edf314978f3ec4fc0dcd61652d Mon Sep 17 00:00:00 2001 From: Garret Fick Date: Sat, 25 Jun 2016 16:46:44 +0800 Subject: Add support for polygon apertures --- gerber/am_statements.py | 4 ++-- gerber/gerber_statements.py | 7 ++++++- gerber/primitives.py | 7 ++++--- gerber/render/cairo_backend.py | 15 +++++++++++++++ gerber/render/rs274x_backend.py | 26 ++++++++++++++++++++++---- gerber/rs274x.py | 14 ++++++++++++-- 6 files changed, 61 insertions(+), 12 deletions(-) diff --git a/gerber/am_statements.py b/gerber/am_statements.py index a58f1dd..0d92a8c 100644 --- a/gerber/am_statements.py +++ b/gerber/am_statements.py @@ -523,7 +523,7 @@ class AMPolygonPrimitive(AMPrimitive): return fmt.format(**data) def to_primitive(self, units): - return Polygon(self.position, self.vertices, self.diameter / 2.0, rotation=math.radians(self.rotation), units=units, level_polarity=self._level_polarity) + return Polygon(self.position, self.vertices, self.diameter / 2.0, hole_radius=0, rotation=self.rotation, units=units, level_polarity=self._level_polarity) class AMMoirePrimitive(AMPrimitive): @@ -897,7 +897,7 @@ class AMCenterLinePrimitive(AMPrimitive): return fmt.format(**data) def to_primitive(self, units): - return Rectangle(self.center, self.width, self.height, rotation=math.radians(self.rotation), units=units, level_polarity=self._level_polarity) + return Rectangle(self.center, self.width, self.height, rotation=self.rotation, units=units, level_polarity=self._level_polarity) class AMLowerLeftLinePrimitive(AMPrimitive): diff --git a/gerber/gerber_statements.py b/gerber/gerber_statements.py index 725febf..234952e 100644 --- a/gerber/gerber_statements.py +++ b/gerber/gerber_statements.py @@ -285,9 +285,14 @@ class ADParamStmt(ParamStmt): @classmethod def obround(cls, dcode, width, height): - '''Create an obrou d aperture definition statement''' + '''Create an obround aperture definition statement''' return cls('AD', dcode, 'O', ([width, height],)) + @classmethod + def polygon(cls, dcode, diameter, num_vertices, rotation, hole_diameter): + '''Create a polygon aperture definition statement''' + return cls('AD', dcode, 'P', ([diameter, num_vertices, rotation, hole_diameter],)) + @classmethod def macro(cls, dcode, name): return cls('AD', dcode, name, '') diff --git a/gerber/primitives.py b/gerber/primitives.py index aa6e661..211acb8 100644 --- a/gerber/primitives.py +++ b/gerber/primitives.py @@ -721,14 +721,15 @@ class Obround(Primitive): class Polygon(Primitive): """ - Polygon flash defined by a set number of sized. + Polygon flash defined by a set number of sides. """ - def __init__(self, position, sides, radius, **kwargs): + def __init__(self, position, sides, radius, hole_radius, **kwargs): super(Polygon, self).__init__(**kwargs) validate_coordinates(position) self.position = position self.sides = sides self.radius = radius + self.hole_radius = hole_radius self._to_convert = ['position', 'radius'] @property @@ -753,7 +754,7 @@ class Polygon(Primitive): @property def vertices(self): - offset = math.degrees(self.rotation) + offset = self.rotation da = 360.0 / self.sides points = [] diff --git a/gerber/render/cairo_backend.py b/gerber/render/cairo_backend.py index c1bd60c..2b7c2ff 100644 --- a/gerber/render/cairo_backend.py +++ b/gerber/render/cairo_backend.py @@ -159,6 +159,9 @@ class GerberCairoContext(GerberContext): self._render_rectangle(obround.subshapes['rectangle'], color) def _render_polygon(self, polygon, color): + if polygon.hole_radius > 0: + self.ctx.push_group() + vertices = polygon.vertices self.ctx.set_source_rgba(color[0], color[1], color[2], self.alpha) @@ -172,6 +175,18 @@ class GerberCairoContext(GerberContext): self.ctx.line_to(*map(mul, v, self.scale)) self.ctx.fill() + + if polygon.hole_radius > 0: + # Render the center clear + center = tuple(map(mul, polygon.position, self.scale)) + self.ctx.set_source_rgba(color[0], color[1], color[2], self.alpha) + self.ctx.set_operator(cairo.OPERATOR_CLEAR) + self.ctx.set_line_width(0) + self.ctx.arc(center[0], center[1], polygon.hole_radius * self.scale[0], 0, 2 * math.pi) + self.ctx.fill() + + self.ctx.pop_group_to_source() + self.ctx.paint_with_alpha(1) def _render_drill(self, circle, color): self._render_circle(circle, color) diff --git a/gerber/render/rs274x_backend.py b/gerber/render/rs274x_backend.py index bb784b1..c37529e 100644 --- a/gerber/render/rs274x_backend.py +++ b/gerber/render/rs274x_backend.py @@ -310,7 +310,7 @@ class Rs274xContext(GerberContext): self._next_dcode = max(dcode + 1, self._next_dcode) aper = ADParamStmt.obround(dcode, width, height) - self._obrounds[(width, height)] = aper + self._obrounds[key] = aper self.header.append(aper) return aper @@ -320,10 +320,28 @@ class Rs274xContext(GerberContext): aper = self._get_obround(obround.width, obround.height) self._render_flash(obround, aper) - pass - def _render_polygon(self, polygon, color): - raise ValueError('Polygons can only exist in the context of aperture macro') + + aper = self._get_polygon(polygon.radius, polygon.sides, polygon.rotation, polygon.hole_radius) + self._render_flash(polygon, aper) + + def _get_polygon(self, radius, num_vertices, rotation, hole_radius, dcode = None): + + key = (radius, num_vertices, rotation, hole_radius) + aper = self._polygons.get(key, None) + + if not aper: + if not dcode: + dcode = self._next_dcode + self._next_dcode += 1 + else: + self._next_dcode = max(dcode + 1, self._next_dcode) + + aper = ADParamStmt.polygon(dcode, radius * 2, num_vertices, rotation, hole_radius * 2) + self._polygons[key] = aper + self.header.append(aper) + + return aper def _render_drill(self, drill, color): raise ValueError('Drills are not valid in RS274X files') diff --git a/gerber/rs274x.py b/gerber/rs274x.py index 7eba1c2..ffac66d 100644 --- a/gerber/rs274x.py +++ b/gerber/rs274x.py @@ -483,8 +483,18 @@ class GerberParser(object): height = modifiers[0][1] aperture = Obround(position=None, width=width, height=height, units=self.settings.units) elif shape == 'P': - # FIXME: not supported yet? - pass + outer_diameter = modifiers[0][0] + number_vertices = int(modifiers[0][1]) + if len(modifiers[0]) > 2: + rotation = modifiers[0][2] + else: + rotation = 0 + + if len(modifiers[0]) > 3: + hole_diameter = modifiers[0][3] + else: + hole_diameter = 0 + aperture = Polygon(position=None, sides=number_vertices, radius=outer_diameter/2.0, hole_radius=hole_diameter/2.0, rotation=rotation) else: aperture = self.macros[shape].build(modifiers) -- cgit