summaryrefslogtreecommitdiff
path: root/gerber
diff options
context:
space:
mode:
Diffstat (limited to 'gerber')
-rwxr-xr-xgerber/excellon.py10
-rw-r--r--gerber/gerber_statements.py4
-rw-r--r--gerber/render/render.py2
-rw-r--r--gerber/render/rs274x_backend.py2
-rw-r--r--gerber/rs274x.py33
-rw-r--r--gerber/utils.py6
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