summaryrefslogtreecommitdiff
path: root/gerber/render/rs274x_backend.py
diff options
context:
space:
mode:
authorGarret Fick <garret@ficksworkshop.com>2016-02-27 15:24:36 +0800
committerGarret Fick <garret@ficksworkshop.com>2016-02-27 15:24:36 +0800
commit29c0d82bf53907030d11df9eb09471b716a0be2e (patch)
treee172ad05e29c9f6a82c862badd0d2242dd567be1 /gerber/render/rs274x_backend.py
parent02dbc6a51e2ef417f2bd41d6159ba53cc736535d (diff)
downloadgerbonara-29c0d82bf53907030d11df9eb09471b716a0be2e.tar.gz
gerbonara-29c0d82bf53907030d11df9eb09471b716a0be2e.tar.bz2
gerbonara-29c0d82bf53907030d11df9eb09471b716a0be2e.zip
RS274X backend for rendering. Incompelte still
Diffstat (limited to 'gerber/render/rs274x_backend.py')
-rw-r--r--gerber/render/rs274x_backend.py290
1 files changed, 290 insertions, 0 deletions
diff --git a/gerber/render/rs274x_backend.py b/gerber/render/rs274x_backend.py
new file mode 100644
index 0000000..0094192
--- /dev/null
+++ b/gerber/render/rs274x_backend.py
@@ -0,0 +1,290 @@
+
+from .render import GerberContext
+from ..gerber_statements import *
+from ..primitives import AMGroup, Arc, Circle, Line, Rectangle
+
+class Rs274xContext(GerberContext):
+
+ def __init__(self, settings):
+ GerberContext.__init__(self)
+ self.header = []
+ self.body = []
+ self.end = [EofStmt()]
+
+ # Current values so we know if we have to execute
+ # moves, levey changes before anything else
+ self._level_polarity = None
+ self._pos = (None, None)
+ self._func = None
+ self._quadrant_mode = None
+ self._dcode = None
+
+ self._next_dcode = 10
+ self._rects = {}
+ self._circles = {}
+ self._macros = {}
+
+ self._i_none = 0
+ self._j_none = 0
+
+ self._define_dcodes()
+
+
+ def _define_dcodes(self):
+
+ self._get_circle(.1575, 10)
+ self._get_circle(.035, 17)
+ self._get_rectangle(0.1575, 0.1181, 15)
+ self._get_rectangle(0.0492, 0.0118, 16)
+ self._get_circle(.0197, 11)
+ self._get_rectangle(0.0236, 0.0591, 12)
+ self._get_circle(.005, 18)
+ self._get_circle(.008, 19)
+ self._get_circle(.009, 20)
+ self._get_circle(.01, 21)
+ self._get_circle(.02, 22)
+ self._get_circle(.006, 23)
+ self._get_circle(.015, 24)
+ self._get_rectangle(0.1678, 0.1284, 26)
+ self._get_rectangle(0.0338, 0.0694, 25)
+
+ def _simplify_point(self, point):
+ return (point[0] if point[0] != self._pos[0] else None, point[1] if point[1] != self._pos[1] else None)
+
+ def _simplify_offset(self, point, offset):
+
+ if point[0] != offset[0]:
+ xoffset = point[0] - offset[0]
+ else:
+ xoffset = self._i_none
+
+ if point[1] != offset[1]:
+ yoffset = point[1] - offset[1]
+ else:
+ yoffset = self._j_none
+
+ return (xoffset, yoffset)
+
+ @property
+ def statements(self):
+ return self.header + self.body + self.end
+
+ def set_bounds(self, bounds):
+ pass
+
+ def _paint_background(self):
+ pass
+
+ def _select_aperture(self, aperture):
+
+ # Select the right aperture if not already selected
+ if aperture:
+ if isinstance(aperture, Circle):
+ aper = self._get_circle(aperture.diameter)
+ elif isinstance(aperture, Rectangle):
+ aper = self._get_rectangle(aperture.width, aperture.height)
+ else:
+ raise NotImplementedError('Line with invalid aperture type')
+
+ if aper.d != self._dcode:
+ self.body.append(ApertureStmt(aper.d))
+ self._dcode = aper.d
+
+ def _render_line(self, line, color):
+
+ self._select_aperture(line.aperture)
+
+ # Get the right function
+ if self._func != CoordStmt.FUNC_LINEAR:
+ func = CoordStmt.FUNC_LINEAR
+ else:
+ func = None
+ self._func = CoordStmt.FUNC_LINEAR
+
+ if self._pos != line.start:
+ self.body.append(CoordStmt.move(func, self._simplify_point(line.start)))
+ self._pos = line.start
+ # We already set the function, so the next command doesn't require that
+ func = None
+
+ self.body.append(CoordStmt.line(func, self._simplify_point(line.end)))
+ self._pos = line.end
+
+ def _render_arc(self, arc, color):
+
+ # Optionally set the quadrant mode if it has changed:
+ if arc.quadrant_mode != self._quadrant_mode:
+
+ if arc.quadrant_mode != 'multi-quadrant':
+ self.body.append(QuadrantModeStmt.single())
+ else:
+ self.body.append(QuadrantModeStmt.multi())
+
+ self._quadrant_mode = arc.quadrant_mode
+
+ # Select the right aperture if not already selected
+ self._select_aperture(arc.aperture)
+
+ # Find the right movement mode. Always set to be sure it is really right
+ dir = arc.direction
+ if dir == 'clockwise':
+ func = CoordStmt.FUNC_ARC_CW
+ self._func = CoordStmt.FUNC_ARC_CW
+ elif dir == 'counterclockwise':
+ func = CoordStmt.FUNC_ARC_CCW
+ self._func = CoordStmt.FUNC_ARC_CCW
+ else:
+ raise ValueError('Invalid circular interpolation mode')
+
+ if self._pos != arc.start:
+ # TODO I'm not sure if this is right
+ self.body.append(CoordStmt.move(CoordStmt.FUNC_LINEAR, self._simplify_point(arc.start)))
+ self._pos = arc.start
+
+ center = self._simplify_offset(arc.center, arc.start)
+ end = self._simplify_point(arc.end)
+ self.body.append(CoordStmt.arc(func, end, center))
+ self._pos = arc.end
+
+ def _render_region(self, region, color):
+
+ self._render_level_polarity(region)
+
+ self.body.append(RegionModeStmt.on())
+
+ for p in region.primitives:
+
+ if isinstance(p, Line):
+ self._render_line(p, color)
+ else:
+ self._render_arc(p, color)
+
+
+ self.body.append(RegionModeStmt.off())
+
+ def _render_level_polarity(self, region):
+ if region.level_polarity != self._level_polarity:
+ self._level_polarity = region.level_polarity
+ self.body.append(LPParamStmt.from_region(region))
+
+ def _render_flash(self, primitive, aperture):
+
+ if aperture.d != self._dcode:
+ self.body.append(ApertureStmt(aperture.d))
+ self._dcode = aperture.d
+
+ self.body.append(CoordStmt.flash( self._simplify_point(primitive.position)))
+ self._pos = primitive.position
+
+ def _get_circle(self, diameter, dcode = None):
+ '''Define a circlar aperture'''
+
+ aper = self._circles.get(diameter, 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.circle(dcode, diameter)
+ self._circles[diameter] = aper
+ self.header.append(aper)
+
+ return aper
+
+ def _render_circle(self, circle, color):
+
+ aper = self._get_circle(circle.diameter)
+ self._render_flash(circle, aper)
+
+ def _get_rectangle(self, width, height, dcode = None):
+ '''Get a rectanglar aperture. If it isn't defined, create it'''
+
+ key = (width, height)
+ aper = self._rects.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.rect(dcode, width, height)
+ self._rects[(width, height)] = aper
+ self.header.append(aper)
+
+ return aper
+
+ def _render_rectangle(self, rectangle, color):
+
+ aper = self._get_rectangle(rectangle.width, rectangle.height)
+ self._render_flash(rectangle, aper)
+
+ def _render_obround(self, obround, color):
+ pass
+
+ def _render_polygon(self, polygon, color):
+ pass
+
+ def _render_drill(self, circle, color):
+ pass
+
+ def _hash_amacro(self, amgroup):
+ '''Calculate a very quick hash code for deciding if we should even check AM groups for comparision'''
+
+ hash = ''
+ for primitive in amgroup.primitives:
+
+ hash += primitive.__class__.__name__[0]
+ if hasattr(primitive, 'primitives'):
+ hash += str(len(primitive.primitives))
+
+ return hash
+
+ def _get_amacro(self, amgroup, dcode = None):
+ # Macros are a little special since we don't have a good way to compare them quickly
+ # but in most cases, this should work
+
+ hash = self._hash_amacro(amgroup)
+ macro = self._macros.get(hash, None)
+
+ if not macro:
+ # This is a new macro, so define it
+ if not dcode:
+ dcode = self._next_dcode
+ self._next_dcode += 1
+ else:
+ self._next_dcode = max(dcode + 1, self._next_dcode)
+
+ # Create the statements
+ # TODO
+ statements = []
+ aperdef = ADParamStmt.macro(dcode, hash)
+
+ # Store the dcode and the original so we can check if it really is the same
+ macro = (aperdef, amgroup)
+ self._macros[hash] = macro
+
+ else:
+ # We hae a definition, but check that the groups actually are the same
+ offset = (amgroup.position[0] - macro[1].position[0], amgroup.position[1] - macro[1].position[1])
+ if not amgroup.equivalent(macro[1], offset):
+ raise ValueError('Two AMGroup have the same hash but are not equivalent')
+
+ return macro[0]
+
+ def _render_amgroup(self, amgroup, color):
+
+ aper = self._get_amacro(amgroup)
+ self._render_flash(amgroup, aper)
+
+ def _render_inverted_layer(self):
+ pass
+
+ def post_render_primitives(self):
+ '''No more primitives, so set the end marker'''
+
+ self.body.append() \ No newline at end of file