summaryrefslogtreecommitdiff
path: root/gerber/rs274x.py
diff options
context:
space:
mode:
authorHamilton Kibbe <hamilton.kibbe@gmail.com>2016-11-18 08:05:57 -0500
committerHamilton Kibbe <hamilton.kibbe@gmail.com>2016-11-18 08:05:57 -0500
commita7f1f6ef0fdd9c792b3234931754dac5d81b15e5 (patch)
treeb6f94cadd7e8dace43bb7cae894b61973ae10980 /gerber/rs274x.py
parent6b672e98ff36b25e289c0d9e2ccc28337baa3c27 (diff)
downloadgerbonara-a7f1f6ef0fdd9c792b3234931754dac5d81b15e5.tar.gz
gerbonara-a7f1f6ef0fdd9c792b3234931754dac5d81b15e5.tar.bz2
gerbonara-a7f1f6ef0fdd9c792b3234931754dac5d81b15e5.zip
Finish adding square hole support, fix some primitive calculations, etc.
Diffstat (limited to 'gerber/rs274x.py')
-rw-r--r--gerber/rs274x.py115
1 files changed, 85 insertions, 30 deletions
diff --git a/gerber/rs274x.py b/gerber/rs274x.py
index ff8addd..5191fb7 100644
--- a/gerber/rs274x.py
+++ b/gerber/rs274x.py
@@ -514,32 +514,51 @@ class GerberParser(object):
if shape == 'C':
diameter = modifiers[0][0]
- if len(modifiers[0]) >= 2:
+ hole_diameter = 0
+ rectangular_hole = (0, 0)
+ if len(modifiers[0]) == 2:
hole_diameter = modifiers[0][1]
- else:
- hole_diameter = None
+ elif len(modifiers[0]) == 3:
+ rectangular_hole = modifiers[0][1:3]
+
+ aperture = Circle(position=None, diameter=diameter,
+ hole_diameter=hole_diameter,
+ hole_width=rectangular_hole[0],
+ hole_height=rectangular_hole[1],
+ units=self.settings.units)
- aperture = Circle(position=None, diameter=diameter, hole_diameter=hole_diameter, units=self.settings.units)
elif shape == 'R':
width = modifiers[0][0]
height = modifiers[0][1]
- if len(modifiers[0]) >= 3:
+ hole_diameter = 0
+ rectangular_hole = (0, 0)
+ if len(modifiers[0]) == 3:
hole_diameter = modifiers[0][2]
- else:
- hole_diameter = None
-
- aperture = Rectangle(position=None, width=width, height=height, hole_diameter=hole_diameter, units=self.settings.units)
+ elif len(modifiers[0]) == 4:
+ rectangular_hole = modifiers[0][2:4]
+
+ aperture = Rectangle(position=None, width=width, height=height,
+ hole_diameter=hole_diameter,
+ hole_width=rectangular_hole[0],
+ hole_height=rectangular_hole[1],
+ units=self.settings.units)
elif shape == 'O':
width = modifiers[0][0]
height = modifiers[0][1]
- if len(modifiers[0]) >= 3:
+ hole_diameter = 0
+ rectangular_hole = (0, 0)
+ if len(modifiers[0]) == 3:
hole_diameter = modifiers[0][2]
- else:
- hole_diameter = None
-
- aperture = Obround(position=None, width=width, height=height, hole_diameter=hole_diameter, units=self.settings.units)
+ elif len(modifiers[0]) == 4:
+ rectangular_hole = modifiers[0][2:4]
+
+ aperture = Obround(position=None, width=width, height=height,
+ hole_diameter=hole_diameter,
+ hole_width=rectangular_hole[0],
+ hole_height=rectangular_hole[1],
+ units=self.settings.units)
elif shape == 'P':
outer_diameter = modifiers[0][0]
number_vertices = int(modifiers[0][1])
@@ -548,11 +567,19 @@ class GerberParser(object):
else:
rotation = 0
- if len(modifiers[0]) > 3:
+ hole_diameter = 0
+ rectangular_hole = (0, 0)
+ if len(modifiers[0]) == 4:
hole_diameter = modifiers[0][3]
- else:
- hole_diameter = None
- aperture = Polygon(position=None, sides=number_vertices, radius=outer_diameter/2.0, hole_diameter=hole_diameter, rotation=rotation)
+ elif len(modifiers[0]) >= 5:
+ rectangular_hole = modifiers[0][3:5]
+
+ aperture = Polygon(position=None, sides=number_vertices,
+ radius=outer_diameter/2.0,
+ hole_diameter=hole_diameter,
+ hole_width=rectangular_hole[0],
+ hole_height=rectangular_hole[1],
+ rotation=rotation)
else:
aperture = self.macros[shape].build(modifiers)
@@ -663,13 +690,18 @@ class GerberParser(object):
quadrant_mode=self.quadrant_mode,
level_polarity=self.level_polarity,
units=self.settings.units))
+ # Gerbv seems to reset interpolation mode in regions..
+ # TODO: Make sure this is right.
+ self.interpolation = 'linear'
elif self.op == "D02" or self.op == "D2":
if self.region_mode == "on":
# D02 in the middle of a region finishes that region and starts a new one
if self.current_region and len(self.current_region) > 1:
- self.primitives.append(Region(self.current_region, level_polarity=self.level_polarity, units=self.settings.units))
+ self.primitives.append(Region(self.current_region,
+ level_polarity=self.level_polarity,
+ units=self.settings.units))
self.current_region = None
elif self.op == "D03" or self.op == "D3":
@@ -694,29 +726,53 @@ class GerberParser(object):
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
+ 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
+ in the specified direction
"""
-
+ two_pi = 2 * math.pi
if self.quadrant_mode == 'single-quadrant':
+ # The Gerber spec says single quadrant only has one possible center,
+ # and you can detect it 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). We select the center with the least error in
+ # radius from all the options with a valid sweep angle.
- # 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])
+ test_center = (start[0] + offsets[0] * factors[0],
+ start[1] + offsets[1] * factors[1])
+
+ # Find angle from center to start and end points
+ start_angle = math.atan2(*reversed([_start - _center for _start, _center in zip(start, test_center)]))
+ end_angle = math.atan2(*reversed([_end - _center for _end, _center in zip(end, test_center)]))
+ # Clamp angles to 0, 2pi
+ theta0 = (start_angle + two_pi) % two_pi
+ theta1 = (end_angle + two_pi) % two_pi
+
+ # Determine sweep angle in the current arc direction
+ if self.direction == 'counterclockwise':
+ sweep_angle = abs(theta1 - theta0)
+ else:
+ theta0 += two_pi
+ sweep_angle = abs(theta0 - theta1) % two_pi
+
+ # Calculate the radius error
sqdist_start = sq_distance(start, test_center)
sqdist_end = sq_distance(end, test_center)
- if abs(sqdist_start - sqdist_end) < sqdist_diff_min:
+ # Take the option with the lowest radius error from the set of
+ # options with a valid sweep angle
+ if ((abs(sqdist_start - sqdist_end) < sqdist_diff_min)
+ and (sweep_angle >= 0)
+ and (sweep_angle <= math.pi / 2.0)):
center = test_center
sqdist_diff_min = abs(sqdist_start - sqdist_end)
-
return center
else:
return (start[0] + offsets[0], start[1] + offsets[1])
@@ -724,7 +780,6 @@ class GerberParser(object):
def _evaluate_aperture(self, stmt):
self.aperture = stmt.d
-
def _match_one(expr, data):
match = expr.match(data)
if match is None: