diff options
Diffstat (limited to 'gerber/rs274x.py')
-rw-r--r-- | gerber/rs274x.py | 66 |
1 files changed, 36 insertions, 30 deletions
diff --git a/gerber/rs274x.py b/gerber/rs274x.py index e84c161..5d64597 100644 --- a/gerber/rs274x.py +++ b/gerber/rs274x.py @@ -50,7 +50,7 @@ def read(filename): return GerberParser().parse(filename) -def loads(data): +def loads(data, filename=None): """ Generate a GerberFile object from rs274x data in memory Parameters @@ -58,12 +58,15 @@ def loads(data): data : string string containing gerber file contents + filename : string, optional + string containing the filename of the data source + Returns ------- file : :class:`gerber.rs274x.GerberFile` A GerberFile created from the specified file. """ - return GerberParser().parse_raw(data) + return GerberParser().parse_raw(data, filename) class GerberFile(CamFile): @@ -98,7 +101,7 @@ class GerberFile(CamFile): def __init__(self, statements, settings, primitives, apertures, filename=None): super(GerberFile, self).__init__(statements, settings, primitives, filename) - + self.apertures = apertures @property @@ -115,15 +118,18 @@ class GerberFile(CamFile): def bounds(self): min_x = min_y = 1000000 max_x = max_y = -1000000 + for stmt in [stmt for stmt in self.statements if isinstance(stmt, CoordStmt)]: if stmt.x is not None: min_x = min(stmt.x, min_x) max_x = max(stmt.x, max_x) + if stmt.y is not None: min_y = min(stmt.y, min_y) max_y = max(stmt.y, max_y) + return ((min_x, max_x), (min_y, max_y)) - + @property def bounding_box(self): min_x = min_y = 1000000 @@ -258,7 +264,7 @@ class GerberParser(object): stmt.units = self.settings.units return GerberFile(self.statements, self.settings, self.primitives, self.apertures.values(), filename) - + def _split_commands(self, data): """ Split the data into commands. Commands end with * (and also newline to help with some badly formatted files) @@ -267,24 +273,24 @@ class GerberParser(object): length = len(data) start = 0 in_header = True - + for cur in range(0, length): val = data[cur] - + if val == '%' and start == cur: in_header = True continue - + if val == '\r' or val == '\n': if start != cur: yield data[start:cur] start = cur + 1 - + elif not in_header and val == '*': yield data[start:cur + 1] start = cur + 1 - + elif in_header and val == '%': yield data[start:cur + 1] start = cur + 1 @@ -318,13 +324,13 @@ class GerberParser(object): did_something = True # make sure we do at least one loop while did_something and len(line) > 0: did_something = False - + # consume empty data blocks if line[0] == '*': line = line[1:] did_something = True continue - + # coord (coord, r) = _match_one(self.COORD_STMT, line) if coord: @@ -332,7 +338,7 @@ class GerberParser(object): line = r did_something = True continue - + # aperture selection (aperture, r) = _match_one(self.APERTURE_STMT, line) if aperture: @@ -485,32 +491,32 @@ class GerberParser(object): aperture = None if shape == 'C': diameter = modifiers[0][0] - + if len(modifiers[0]) >= 2: hole_diameter = modifiers[0][1] else: hole_diameter = None - + 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 = modifiers[0][2] else: hole_diameter = None - + aperture = Rectangle(position=None, width=width, height=height, hole_diameter=hole_diameter, units=self.settings.units) elif shape == 'O': width = modifiers[0][0] height = modifiers[0][1] - + 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 shape == 'P': outer_diameter = modifiers[0][0] @@ -519,7 +525,7 @@ class GerberParser(object): rotation = modifiers[0][2] else: rotation = 0 - + if len(modifiers[0]) > 3: hole_diameter = modifiers[0][3] else: @@ -528,6 +534,7 @@ class GerberParser(object): else: aperture = self.macros[shape].build(modifiers) + aperture.units = self.settings.units self.apertures[d] = aperture def _evaluate_mode(self, stmt): @@ -636,7 +643,7 @@ class GerberParser(object): units=self.settings.units)) 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: @@ -646,7 +653,6 @@ class GerberParser(object): elif self.op == "D03" or self.op == "D3": primitive = copy.deepcopy(self.apertures[self.aperture]) - if primitive is not None: if not isinstance(primitive, AMParamStmt): @@ -663,32 +669,32 @@ class GerberParser(object): if renderable is not None: self.primitives.append(renderable) 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 + + # 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]) |