From 5476da8aa3f4ee424f56f4f2491e7af1c4b7b758 Mon Sep 17 00:00:00 2001 From: Hamilton Kibbe Date: Thu, 21 Jan 2016 03:57:44 -0500 Subject: Fix a bunch of rendering bugs. - 'clear' polarity primitives no longer erase background - Added aperture macro support for polygons - Added aperture macro rendring support - Renderer now creates a new surface for each layer and merges them instead of working directly on a single surface - Updated examples accordingly --- gerber/excellon.py | 62 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 35 insertions(+), 27 deletions(-) (limited to 'gerber/excellon.py') diff --git a/gerber/excellon.py b/gerber/excellon.py index 3bb8611..b1b94df 100755 --- a/gerber/excellon.py +++ b/gerber/excellon.py @@ -76,6 +76,7 @@ def loads(data): class DrillHit(object): + def __init__(self, tool, position): self.tool = tool self.position = position @@ -118,6 +119,7 @@ class ExcellonFile(CamFile): either 'inch' or 'metric'. """ + def __init__(self, statements, tools, hits, settings, filename=None): super(ExcellonFile, self).__init__(statements=statements, settings=settings, @@ -127,8 +129,7 @@ class ExcellonFile(CamFile): @property def primitives(self): - return [Drill(hit.position, hit.tool.diameter,units=self.settings.units) for hit in self.hits] - + return [Drill(hit.position, hit.tool.diameter, units=self.settings.units) for hit in self.hits] @property def bounds(self): @@ -162,7 +163,8 @@ class ExcellonFile(CamFile): rprt += ' Code Size Hits Path Length\n' rprt += ' --------------------------------------\n' for tool in iter(self.tools.values()): - rprt += toolfmt.format(tool.number, tool.diameter, tool.hit_count, self.path_length(tool.number)) + rprt += toolfmt.format(tool.number, tool.diameter, + tool.hit_count, self.path_length(tool.number)) if filename is not None: with open(filename, 'w') as f: f.write(rprt) @@ -184,7 +186,8 @@ class ExcellonFile(CamFile): f.write(ToolSelectionStmt(tool.number).to_excellon(self.settings) + '\n') for hit in self.hits: if hit.tool.number == tool.number: - f.write(CoordinateStmt(*hit.position).to_excellon(self.settings) + '\n') + f.write(CoordinateStmt( + *hit.position).to_excellon(self.settings) + '\n') f.write(EndOfProgramStmt().to_excellon() + '\n') def to_inch(self): @@ -200,8 +203,7 @@ class ExcellonFile(CamFile): for primitive in self.primitives: primitive.to_inch() for hit in self.hits: - hit.position = tuple(map(inch, hit,position)) - + hit.position = tuple(map(inch, hit, position)) def to_metric(self): """ Convert units to metric @@ -223,7 +225,8 @@ 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.position = tuple(map(operator.add, hit.position, + (x_offset, y_offset))) def path_length(self, tool_number=None): """ Return the path length for a given tool @@ -233,9 +236,11 @@ class ExcellonFile(CamFile): for hit in self.hits: tool = hit.tool num = tool.number - positions[num] = (0, 0) if positions.get(num) is None else positions[num] + positions[num] = (0, 0) if positions.get( + num) is None else positions[num] lengths[num] = 0.0 if lengths.get(num) is None else lengths[num] - lengths[num] = lengths[num] + math.hypot(*tuple(map(operator.sub, positions[num], hit.position))) + lengths[num] = lengths[ + num] + math.hypot(*tuple(map(operator.sub, positions[num], hit.position))) positions[num] = hit.position if tool_number is None: @@ -244,13 +249,13 @@ class ExcellonFile(CamFile): return lengths.get(tool_number) def hit_count(self, tool_number=None): - counts = {} - for tool in iter(self.tools.values()): - counts[tool.number] = tool.hit_count - if tool_number is None: - return counts - else: - return counts.get(tool_number) + counts = {} + for tool in iter(self.tools.values()): + counts[tool.number] = tool.hit_count + if tool_number is None: + return counts + else: + return counts.get(tool_number) def update_tool(self, tool_number, **kwargs): """ Change parameters of a tool @@ -274,7 +279,6 @@ class ExcellonFile(CamFile): hit.tool = newtool - class ExcellonParser(object): """ Excellon File Parser @@ -283,6 +287,7 @@ class ExcellonParser(object): settings : FileSettings or dict-like Excellon file settings to use when interpreting the excellon file. """ + def __init__(self, settings=None): self.notation = 'absolute' self.units = 'inch' @@ -300,7 +305,6 @@ class ExcellonParser(object): self.notation = settings.notation self.format = settings.format - @property def coordinates(self): return [(stmt.x, stmt.y) for stmt in self.statements if isinstance(stmt, CoordinateStmt)] @@ -350,7 +354,8 @@ class ExcellonParser(object): # get format from altium comment if "FILE_FORMAT" in comment_stmt.comment: - detected_format = tuple([int(x) for x in comment_stmt.comment.split('=')[1].split(":")]) + detected_format = tuple( + [int(x) for x in comment_stmt.comment.split('=')[1].split(":")]) if detected_format: self.format = detected_format @@ -435,7 +440,7 @@ class ExcellonParser(object): self.zeros = stmt.zeros self.statements.append(stmt) - elif line[:3] == 'M71' or line [:3] == 'M72': + elif line[:3] == 'M71' or line[:3] == 'M72': stmt = MeasuringModeStmt.from_excellon(line) self.units = stmt.units self.statements.append(stmt) @@ -481,17 +486,20 @@ class ExcellonParser(object): # T0 is used as END marker, just ignore if stmt.tool != 0: - # FIXME: for weird files with no tools defined, original calc from gerbv + # FIXME: for weird files with no tools defined, original calc + # from gerbv if stmt.tool not in self.tools: if self._settings().units == "inch": - diameter = (16 + 8 * stmt.tool) / 1000.0; + diameter = (16 + 8 * stmt.tool) / 1000.0 else: - diameter = metric((16 + 8 * stmt.tool) / 1000.0); + diameter = metric((16 + 8 * stmt.tool) / 1000.0) - tool = ExcellonTool(self._settings(), number=stmt.tool, diameter=diameter) + tool = ExcellonTool( + self._settings(), number=stmt.tool, diameter=diameter) self.tools[tool.number] = tool - # FIXME: need to add this tool definition inside header to make sure it is properly written + # FIXME: need to add this tool definition inside header to + # make sure it is properly written for i, s in enumerate(self.statements): if isinstance(s, ToolSelectionStmt) or isinstance(s, ExcellonTool): self.statements.insert(i, tool) @@ -575,7 +583,7 @@ def detect_excellon_format(data=None, filename=None): and 'FILE_FORMAT' in stmt.comment] detected_format = (tuple([int(val) for val in - format_comment[0].split('=')[1].split(':')]) + format_comment[0].split('=')[1].split(':')]) if len(format_comment) == 1 else None) detected_zeros = zero_statements[0] if len(zero_statements) == 1 else None @@ -637,5 +645,5 @@ def _layer_size_score(size, hole_count, hole_area): board_area = size[0] * size[1] hole_percentage = hole_area / board_area hole_score = (hole_percentage - 0.25) ** 2 - size_score = (board_area - 8) **2 + size_score = (board_area - 8) ** 2 return hole_score * size_score -- cgit From b9f1b106c3006f1dddb1279ae9622630a29d18c7 Mon Sep 17 00:00:00 2001 From: Paulo Henrique Silva Date: Mon, 25 Jan 2016 12:42:12 -0200 Subject: Excellon format detection uses ExcelonFile.bounds now Long term we should have only one .bounds method. But ExcellonParser right now is not correct for cases with two drills in the same line (it will report one dimension being zero) --- gerber/excellon.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'gerber/excellon.py') diff --git a/gerber/excellon.py b/gerber/excellon.py index b1b94df..b29f7f0 100755 --- a/gerber/excellon.py +++ b/gerber/excellon.py @@ -604,8 +604,8 @@ def detect_excellon_format(data=None, filename=None): settings = FileSettings(zeros=zeros, format=fmt) try: p = ExcellonParser(settings) - p.parse_raw(data) - size = tuple([t[0] - t[1] for t in p.bounds]) + ef = p.parse_raw(data) + size = tuple([t[0] - t[1] for t in ef.bounds]) hole_area = 0.0 for hit in p.hits: tool = hit.tool -- cgit From 5df38c014fd09792995b2b12b1982c535c962c9a Mon Sep 17 00:00:00 2001 From: Hamilton Kibbe Date: Thu, 28 Jan 2016 12:19:03 -0500 Subject: Cleanup, rendering fixes. fixed rendering of tented vias fixed rendering of semi-transparent layers fixed file type detection issues added some examples --- gerber/excellon.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'gerber/excellon.py') diff --git a/gerber/excellon.py b/gerber/excellon.py index b29f7f0..24715d8 100755 --- a/gerber/excellon.py +++ b/gerber/excellon.py @@ -28,7 +28,7 @@ import operator try: from cStringIO import StringIO -except(ImportError): +except ImportError: from io import StringIO from .excellon_statements import * @@ -57,13 +57,16 @@ def read(filename): return ExcellonParser(settings).parse(filename) -def loads(data): +def loads(data, filename=None): """ Read data from string and return an ExcellonFile Parameters ---------- data : string string containing Excellon file contents + filename : string, optional + string containing the filename of the data source + Returns ------- file : :class:`gerber.excellon.ExcellonFile` @@ -72,7 +75,7 @@ def loads(data): """ # File object should use settings from source file by default. settings = FileSettings(**detect_excellon_format(data)) - return ExcellonParser(settings).parse_raw(data) + return ExcellonParser(settings).parse_raw(data, filename) class DrillHit(object): -- cgit