diff options
Diffstat (limited to 'gerber')
-rwxr-xr-x | gerber/excellon.py | 10 | ||||
-rw-r--r-- | gerber/gerber_statements.py | 4 | ||||
-rw-r--r-- | gerber/render/render.py | 2 | ||||
-rw-r--r-- | gerber/render/rs274x_backend.py | 2 | ||||
-rw-r--r-- | gerber/rs274x.py | 33 | ||||
-rw-r--r-- | gerber/utils.py | 6 |
6 files changed, 54 insertions, 3 deletions
diff --git a/gerber/excellon.py b/gerber/excellon.py index 09636aa..9a69042 100755 --- a/gerber/excellon.py +++ b/gerber/excellon.py @@ -100,7 +100,9 @@ class DrillHit(object): min_y = position[1] - radius
max_y = position[1] + radius
return ((min_x, max_x), (min_y, max_y))
-
+
+ def offset(self, x_offset, y_offset):
+ self.position = tuple(map(operator.add, self.position, (x_offset, y_offset)))
class DrillSlot(object):
"""
@@ -134,6 +136,10 @@ class DrillSlot(object): min_y = min(start[1], end[1]) - radius
max_y = max(start[1], end[1]) + radius
return ((min_x, max_x), (min_y, max_y))
+
+ def offset(self, x_offset, y_offset):
+ self.start = tuple(map(operator.add, self.start, (x_offset, y_offset)))
+ self.end = tuple(map(operator.add, self.end, (x_offset, y_offset)))
class ExcellonFile(CamFile):
@@ -268,7 +274,7 @@ class ExcellonFile(CamFile): for primitive in self.primitives:
primitive.offset(x_offset, y_offset)
for hit in self. hits:
- hit.position = tuple(map(operator.add, hit.position, (x_offset, y_offset)))
+ hit.offset(x_offset, y_offset)
def path_length(self, tool_number=None):
""" Return the path length for a given tool
diff --git a/gerber/gerber_statements.py b/gerber/gerber_statements.py index aa25d0a..119df9d 100644 --- a/gerber/gerber_statements.py +++ b/gerber/gerber_statements.py @@ -888,6 +888,10 @@ class CoordStmt(Statement): return cls(func, point[0], point[1], None, None, CoordStmt.OP_DRAW, None) @classmethod + def mode(cls, func): + return cls(func, None, None, None, None, None, None) + + @classmethod def arc(cls, func, point, center): return cls(func, point[0], point[1], center[0], center[1], CoordStmt.OP_DRAW, None) diff --git a/gerber/render/render.py b/gerber/render/render.py index a5ae38e..41b632c 100644 --- a/gerber/render/render.py +++ b/gerber/render/render.py @@ -132,6 +132,8 @@ class GerberContext(object): self._invert = invert def render(self, primitive): + if not primitive: + return color = (self.color if primitive.level_polarity == 'dark' else self.background_color) if isinstance(primitive, Line): diff --git a/gerber/render/rs274x_backend.py b/gerber/render/rs274x_backend.py index 48a55e7..3dc8c1a 100644 --- a/gerber/render/rs274x_backend.py +++ b/gerber/render/rs274x_backend.py @@ -169,6 +169,8 @@ class Rs274xContext(GerberContext): if point[0] != None or point[1] != None: self.body.append(CoordStmt.line(func, self._simplify_point(line.end))) self._pos = line.end + elif func: + self.body.append(CoordStmt.mode(func)) def _render_arc(self, arc, color): diff --git a/gerber/rs274x.py b/gerber/rs274x.py index 7b3a3b9..86785bc 100644 --- a/gerber/rs274x.py +++ b/gerber/rs274x.py @@ -21,6 +21,7 @@ import copy import json import re +import sys try: from cStringIO import StringIO @@ -30,6 +31,7 @@ except(ImportError): from .gerber_statements import * from .primitives import * from .cam import CamFile, FileSettings +from .utils import sq_distance def read(filename): @@ -548,7 +550,7 @@ class GerberParser(object): else: i = 0 if stmt.i is None else stmt.i j = 0 if stmt.j is None else stmt.j - center = (start[0] + i, start[1] + j) + center = self._find_center(start, end, (i, j)) if self.region_mode == 'off': self.primitives.append(Arc(start, end, center, self.direction, self.apertures[self.aperture], quadrant_mode=self.quadrant_mode, level_polarity=self.level_polarity, units=self.settings.units)) else: @@ -579,6 +581,35 @@ class GerberParser(object): self.primitives.append(primitive) self.x, self.y = x, y + + def _find_center(self, start, end, offsets): + """ + In single quadrant mode, the offsets are always positive, which means there are 4 possible centers. + The correct center is the only one that results in an arc with sweep angle of less than or equal to 90 degrees + """ + + if self.quadrant_mode == 'single-quadrant': + + # The Gerber spec says single quadrant only has one possible center, and you can detect + # based on the angle. But for real files, this seems to work better - there is usually + # only one option that makes sense for the center (since the distance should be the same + # from start and end). Find the center that makes the most sense + sqdist_diff_min = sys.maxint + center = None + for factors in [(1, 1), (1, -1), (-1, 1), (-1, -1)]: + + test_center = (start[0] + offsets[0] * factors[0], start[1] + offsets[1] * factors[1]) + + sqdist_start = sq_distance(start, test_center) + sqdist_end = sq_distance(end, test_center) + + if abs(sqdist_start - sqdist_end) < sqdist_diff_min: + center = test_center + sqdist_diff_min = abs(sqdist_start - sqdist_end) + + return center + else: + return (start[0] + offsets[0], start[1] + offsets[1]) def _evaluate_aperture(self, stmt): self.aperture = stmt.d diff --git a/gerber/utils.py b/gerber/utils.py index 72bf2d1..41d264a 100644 --- a/gerber/utils.py +++ b/gerber/utils.py @@ -297,3 +297,9 @@ def nearly_equal(point1, point2, ndigits = 6): '''Are the points nearly equal''' return round(point1[0] - point2[0], ndigits) == 0 and round(point1[1] - point2[1], ndigits) == 0 + +def sq_distance(point1, point2): + + diff1 = point1[0] - point2[0] + diff2 = point1[1] - point2[1] + return diff1 * diff1 + diff2 * diff2 |