From e0d2daca6c7f23f016a4f9de31cd266b54d67531 Mon Sep 17 00:00:00 2001 From: Juan Pablo Caram Date: Fri, 18 Apr 2014 20:20:17 -0400 Subject: [PATCH] Optimized Gerber parser. Some minor improvements to Excellon parser. --- FlatCAM.py | 54 ++-- FlatCAMException.py | 7 + FlatCAMObj.py | 29 ++- camlib.py | 463 +++++++++++++++++++-------------- recent.json | 2 +- tests/profile_gerber_parser.py | 8 + 6 files changed, 326 insertions(+), 237 deletions(-) create mode 100644 FlatCAMException.py create mode 100644 tests/profile_gerber_parser.py diff --git a/FlatCAM.py b/FlatCAM.py index ca98ff52..5810fef1 100644 --- a/FlatCAM.py +++ b/FlatCAM.py @@ -7,38 +7,22 @@ ############################################################ import threading - -# TODO: Bundle together. This is just for debugging. -from docutils.nodes import image -from gi.repository import Gtk -from gi.repository import Gdk -from gi.repository import GdkPixbuf -from gi.repository import GLib -from gi.repository import GObject -import simplejson as json import traceback - -import matplotlib -from matplotlib.figure import Figure -from numpy import arange, sin, pi -from matplotlib.backends.backend_gtk3agg import FigureCanvasGTK3Agg as FigureCanvas -#from mpl_toolkits.axes_grid.anchored_artists import AnchoredText - - import sys import urllib import copy import random +from gi.repository import Gtk, GdkPixbuf +from matplotlib.backends.backend_gtk3agg import FigureCanvasGTK3Agg as FigureCanvas from shapely import speedups + ######################################## ## Imports part of FlatCAM ## ######################################## -from camlib import * from FlatCAMObj import * from FlatCAMWorker import Worker -from FlatCAMException import * ######################################## @@ -909,26 +893,28 @@ class App: # Further parsing GLib.idle_add(lambda: app_obj.set_progress_bar(0.5, "Creating Geometry ...")) #gerber_obj.create_geometry() - gerber_obj.solid_geometry = gerber_obj.otf_geometry + #gerber_obj.solid_geometry = gerber_obj.otf_geometry GLib.idle_add(lambda: app_obj.set_progress_bar(0.6, "Plotting ...")) # Object name name = filename.split('/')[-1].split('\\')[-1] + self.new_object("gerber", name, obj_init) + # New object creation and file processing - try: - self.new_object("gerber", name, obj_init) - except: - e = sys.exc_info() - print "ERROR:", e[0] - traceback.print_exc() - self.message_dialog("Failed to create Gerber Object", - "Attempting to create a FlatCAM Gerber Object from " + - "Gerber file failed during processing:\n" + - str(e[0]) + " " + str(e[1]), kind="error") - GLib.timeout_add_seconds(1, lambda: self.set_progress_bar(0.0, "Idle")) - self.collection.delete_active() - return + # try: + # self.new_object("gerber", name, obj_init) + # except: + # e = sys.exc_info() + # print "ERROR:", e[0] + # traceback.print_exc() + # self.message_dialog("Failed to create Gerber Object", + # "Attempting to create a FlatCAM Gerber Object from " + + # "Gerber file failed during processing:\n" + + # str(e[0]) + " " + str(e[1]), kind="error") + # GLib.timeout_add_seconds(1, lambda: self.set_progress_bar(0.0, "Idle")) + # self.collection.delete_active() + # return # Register recent file self.register_recent("gerber", filename) @@ -2848,7 +2834,7 @@ class ObjectCollection: try: model, treeiter = self.tree_selection.get_selected() return model[treeiter][0] - except ValueError: + except (TypeError, ValueError): return None def set_list_selection(self, name): diff --git a/FlatCAMException.py b/FlatCAMException.py new file mode 100644 index 00000000..3fa10b5b --- /dev/null +++ b/FlatCAMException.py @@ -0,0 +1,7 @@ +class FlatCAMException(Exception): + def __init__(self, message="An error occurred", detail=""): + self.message = message + self.detail = detail + + def __str__(self): + return "FlatCAM ERROR:", self.message \ No newline at end of file diff --git a/FlatCAMObj.py b/FlatCAMObj.py index 1ba91a0a..1500fffa 100644 --- a/FlatCAMObj.py +++ b/FlatCAMObj.py @@ -297,12 +297,13 @@ class FlatCAMGerber(FlatCAMObj, Gerber): if not FlatCAMObj.plot(self): return - if self.options["mergepolys"]: - geometry = self.solid_geometry - else: - geometry = self.buffered_paths + \ - [poly['polygon'] for poly in self.regions] + \ - self.flash_geometry + # if self.options["mergepolys"]: + # geometry = self.solid_geometry + # else: + # geometry = self.buffered_paths + \ + # [poly['polygon'] for poly in self.regions] + \ + # self.flash_geometry + geometry = self.solid_geometry # Make sure geometry is iterable. try: @@ -318,12 +319,16 @@ class FlatCAMGerber(FlatCAMObj, Gerber): if self.options["solid"]: for poly in geometry: # TODO: Too many things hardcoded. - patch = PolygonPatch(poly, - facecolor="#BBF268", - edgecolor="#006E20", - alpha=0.75, - zorder=2) - self.axes.add_patch(patch) + try: + patch = PolygonPatch(poly, + facecolor="#BBF268", + edgecolor="#006E20", + alpha=0.75, + zorder=2) + self.axes.add_patch(patch) + except AssertionError: + print "WARNING: A geometry component was not a polygon:" + print poly else: for poly in geometry: x, y = poly.exterior.xy diff --git a/camlib.py b/camlib.py index d9ea6af4..711abd6f 100644 --- a/camlib.py +++ b/camlib.py @@ -620,7 +620,9 @@ class Gerber (Geometry): # Initialize parent Geometry.__init__(self) - + + self.solid_geometry = Polygon() + # Number format self.int_digits = 3 """Number of integer digits in Gerber numbers. Used during parsing.""" @@ -635,27 +637,26 @@ class Gerber (Geometry): self.apertures = {} # Paths [{'linestring':LineString, 'aperture':str}] - self.paths = [] + # self.paths = [] # Buffered Paths [Polygon] # Paths transformed into Polygons by # offsetting the aperture size/2 - self.buffered_paths = [] + # self.buffered_paths = [] # Polygon regions [{'polygon':Polygon, 'aperture':str}] - self.regions = [] + # self.regions = [] # Flashes [{'loc':[float,float], 'aperture':str}] - self.flashes = [] + # self.flashes = [] # Geometry from flashes - self.flash_geometry = [] + # self.flash_geometry = [] # On-the-fly geometry. Initialized to an empty polygon self.otf_geometry = Polygon() # Aperture Macros - # TODO: Make sure these can be serialized self.aperture_macros = {} # Attributes to be included in serialization @@ -693,7 +694,7 @@ class Gerber (Geometry): # Operation code (D0x) missing is deprecated... oh well I will support it. self.lin_re = re.compile(r'^(?:G0?(1))?(?=.*X(-?\d+))?(?=.*Y(-?\d+))?[XY][^DIJ]*(?:D0?([123]))?\*$') - # + # Operation code alone, usually just D03 (Flash) self.opcode_re = re.compile(r'^D0?([123])\*$') # G02/3... - Circular interpolation with coordinates @@ -782,18 +783,18 @@ class Gerber (Geometry): # for fl in self.flashes: # fl['loc'] = affinity.scale(fl['loc'], factor, factor, origin=(0, 0)) - ## Regions - for reg in self.regions: - reg['polygon'] = affinity.scale(reg['polygon'], factor, factor, - origin=(0, 0)) - - ## Flashes - for flash in self.flash_geometry: - flash = affinity.scale(flash, factor, factor, origin=(0, 0)) - - ## Buffered paths - for bp in self.buffered_paths: - bp = affinity.scale(bp, factor, factor, origin=(0, 0)) + # ## Regions + # for reg in self.regions: + # reg['polygon'] = affinity.scale(reg['polygon'], factor, factor, + # origin=(0, 0)) + # + # ## Flashes + # for flash in self.flash_geometry: + # flash = affinity.scale(flash, factor, factor, origin=(0, 0)) + # + # ## Buffered paths + # for bp in self.buffered_paths: + # bp = affinity.scale(bp, factor, factor, origin=(0, 0)) ## solid_geometry ??? # It's a cascaded union of objects. @@ -834,18 +835,18 @@ class Gerber (Geometry): # for fl in self.flashes: # fl['loc'] = affinity.translate(fl['loc'], xoff=dx, yoff=dy) - ## Regions - for reg in self.regions: - reg['polygon'] = affinity.translate(reg['polygon'], - xoff=dx, yoff=dy) - - ## Buffered paths - for bp in self.buffered_paths: - bp = affinity.translate(bp, xoff=dx, yoff=dy) - - ## Flash geometry - for fl in self.flash_geometry: - fl = affinity.translate(fl, xoff=dx, yoff=dy) + # ## Regions + # for reg in self.regions: + # reg['polygon'] = affinity.translate(reg['polygon'], + # xoff=dx, yoff=dy) + # + # ## Buffered paths + # for bp in self.buffered_paths: + # bp = affinity.translate(bp, xoff=dx, yoff=dy) + # + # ## Flash geometry + # for fl in self.flash_geometry: + # fl = affinity.translate(fl, xoff=dx, yoff=dy) ## Solid geometry self.solid_geometry = affinity.translate(self.solid_geometry, xoff=dx, yoff=dy) @@ -887,18 +888,18 @@ class Gerber (Geometry): # for fl in self.flashes: # fl['loc'] = affinity.scale(fl['loc'], xscale, yscale, origin=(px, py)) - ## Regions - for reg in self.regions: - reg['polygon'] = affinity.scale(reg['polygon'], xscale, yscale, - origin=(px, py)) - - ## Flashes - for flash in self.flash_geometry: - flash = affinity.scale(flash, xscale, yscale, origin=(px, py)) - - ## Buffered paths - for bp in self.buffered_paths: - bp = affinity.scale(bp, xscale, yscale, origin=(px, py)) + # ## Regions + # for reg in self.regions: + # reg['polygon'] = affinity.scale(reg['polygon'], xscale, yscale, + # origin=(px, py)) + # + # ## Flashes + # for flash in self.flash_geometry: + # flash = affinity.scale(flash, xscale, yscale, origin=(px, py)) + # + # ## Buffered paths + # for bp in self.buffered_paths: + # bp = affinity.scale(bp, xscale, yscale, origin=(px, py)) ## solid_geometry ??? # It's a cascaded union of objects. @@ -908,34 +909,34 @@ class Gerber (Geometry): # # Now buffered_paths, flash_geometry and solid_geometry # self.create_geometry() - def fix_regions(self): - """ - Overwrites the region polygons with fixed - versions if found to be invalid (according to Shapely). - - :return: None - """ - - for region in self.regions: - if not region['polygon'].is_valid: - region['polygon'] = region['polygon'].buffer(0) + # def fix_regions(self): + # """ + # Overwrites the region polygons with fixed + # versions if found to be invalid (according to Shapely). + # + # :return: None + # """ + # + # for region in self.regions: + # if not region['polygon'].is_valid: + # region['polygon'] = region['polygon'].buffer(0) - def buffer_paths(self): - """ - This is part of the parsing process. "Thickens" the paths - by their appertures. This will only work for circular appertures. - - :return: None - """ - - self.buffered_paths = [] - for path in self.paths: - try: - width = self.apertures[path["aperture"]]["size"] - self.buffered_paths.append(path["linestring"].buffer(width/2)) - except KeyError: - print "ERROR: Failed to buffer path: ", path - print "Apertures: ", self.apertures + # def buffer_paths(self): + # """ + # This is part of the parsing process. "Thickens" the paths + # by their appertures. This will only work for circular appertures. + # + # :return: None + # """ + # + # self.buffered_paths = [] + # for path in self.paths: + # try: + # width = self.apertures[path["aperture"]]["size"] + # self.buffered_paths.append(path["linestring"].buffer(width/2)) + # except KeyError: + # print "ERROR: Failed to buffer path: ", path + # print "Apertures: ", self.apertures def aperture_parse(self, apertureId, apertureType, apParameters): """ @@ -1014,7 +1015,7 @@ class Gerber (Geometry): gstr = gfile.readlines() gfile.close() self.parse_lines(gstr) - + def parse_lines(self, glines): """ Main Gerber parser. Reads Gerber and populates ``self.paths``, ``self.apertures``, @@ -1027,7 +1028,14 @@ class Gerber (Geometry): :rtype: None """ - path = [] # Coordinates of the current path, each is [x, y] + # Coordinates of the current path, each is [x, y] + path = [] + + # Polygons are stored here until there is a change in polarity. + # Only then they are combined via cascaded_union and added or + # subtracted from solid_geometry. This is ~100 times faster than + # applyng a union for every new polygon. + poly_buffer = [] last_path_aperture = None current_aperture = None @@ -1065,6 +1073,9 @@ class Gerber (Geometry): for gline in glines: line_num += 1 + ### Cleanup + gline = gline.strip(' \r\n') + ### Aperture Macros # Having this at the beggining will slow things down # but macros can have complicated statements than could @@ -1128,6 +1139,19 @@ class Gerber (Geometry): # "aperture": last_path_aperture}) # --- OTF --- + # if making_region: + # geo = Polygon(path) + # else: + # if last_path_aperture is None: + # print "Warning: No aperture defined for curent path. (%d)" % line_num + # width = self.apertures[last_path_aperture]["size"] + # geo = LineString(path).buffer(width/2) + # if current_polarity == 'D': + # self.otf_geometry = self.otf_geometry.union(geo) + # else: + # self.otf_geometry = self.otf_geometry.difference(geo) + + ## --- BUFFERED --- if making_region: geo = Polygon(path) else: @@ -1135,10 +1159,7 @@ class Gerber (Geometry): print "Warning: No aperture defined for curent path. (%d)" % line_num width = self.apertures[last_path_aperture]["size"] geo = LineString(path).buffer(width/2) - if current_polarity == 'D': - self.otf_geometry = self.otf_geometry.union(geo) - else: - self.otf_geometry = self.otf_geometry.difference(geo) + poly_buffer.append(geo) path = [[current_x, current_y]] # Start new path @@ -1148,12 +1169,17 @@ class Gerber (Geometry): # "aperture": current_aperture}) # --- OTF --- + # flash = Gerber.create_flash_geometry(Point([current_x, current_y]), + # self.apertures[current_aperture]) + # if current_polarity == 'D': + # self.otf_geometry = self.otf_geometry.union(flash) + # else: + # self.otf_geometry = self.otf_geometry.difference(flash) + + # --- BUFFERED --- flash = Gerber.create_flash_geometry(Point([current_x, current_y]), self.apertures[current_aperture]) - if current_polarity == 'D': - self.otf_geometry = self.otf_geometry.union(flash) - else: - self.otf_geometry = self.otf_geometry.difference(flash) + poly_buffer.append(flash) continue @@ -1206,12 +1232,17 @@ class Gerber (Geometry): # "aperture": last_path_aperture}) # --- OTF --- + # width = self.apertures[last_path_aperture]["size"] + # buffered = LineString(path).buffer(width/2) + # if current_polarity == 'D': + # self.otf_geometry = self.otf_geometry.union(buffered) + # else: + # self.otf_geometry = self.otf_geometry.difference(buffered) + + # --- BUFFERED --- width = self.apertures[last_path_aperture]["size"] buffered = LineString(path).buffer(width/2) - if current_polarity == 'D': - self.otf_geometry = self.otf_geometry.union(buffered) - else: - self.otf_geometry = self.otf_geometry.difference(buffered) + poly_buffer.append(buffered) current_x = x current_y = y @@ -1252,12 +1283,20 @@ class Gerber (Geometry): if match: current_operation_code = int(match.group(1)) if current_operation_code == 3: + + ## --- OTF --- + # flash = Gerber.create_flash_geometry(Point(path[-1]), + # self.apertures[current_aperture]) + # if current_polarity == 'D': + # self.otf_geometry = self.otf_geometry.union(flash) + # else: + # self.otf_geometry = self.otf_geometry.difference(flash) + + ## --- Buffered --- flash = Gerber.create_flash_geometry(Point(path[-1]), self.apertures[current_aperture]) - if current_polarity == 'D': - self.otf_geometry = self.otf_geometry.union(flash) - else: - self.otf_geometry = self.otf_geometry.difference(flash) + poly_buffer.append(flash) + continue ### G74/75* - Single or multiple quadrant arcs @@ -1273,12 +1312,20 @@ class Gerber (Geometry): if self.regionon_re.search(gline): if len(path) > 1: # Take care of what is left in the path + + ## --- OTF --- + # width = self.apertures[last_path_aperture]["size"] + # geo = LineString(path).buffer(width/2) + # if current_polarity == 'D': + # self.otf_geometry = self.otf_geometry.union(geo) + # else: + # self.otf_geometry = self.otf_geometry.difference(geo) + + ## --- Buffered --- width = self.apertures[last_path_aperture]["size"] geo = LineString(path).buffer(width/2) - if current_polarity == 'D': - self.otf_geometry = self.otf_geometry.union(geo) - else: - self.otf_geometry = self.otf_geometry.difference(geo) + poly_buffer.append(geo) + path = [path[-1]] making_region = True @@ -1304,13 +1351,19 @@ class Gerber (Geometry): # "aperture": last_path_aperture}) # --- OTF --- + # region = Polygon(path) + # if not region.is_valid: + # region = region.buffer(0) + # if current_polarity == 'D': + # self.otf_geometry = self.otf_geometry.union(region) + # else: + # self.otf_geometry = self.otf_geometry.difference(region) + + # --- Buffered --- region = Polygon(path) if not region.is_valid: region = region.buffer(0) - if current_polarity == 'D': - self.otf_geometry = self.otf_geometry.union(region) - else: - self.otf_geometry = self.otf_geometry.difference(region) + poly_buffer.append(region) path = [[current_x, current_y]] # Start new path continue @@ -1343,13 +1396,28 @@ class Gerber (Geometry): if match: if len(path) > 1 and current_polarity != match.group(1): + # --- OTF --- + # width = self.apertures[last_path_aperture]["size"] + # geo = LineString(path).buffer(width/2) + # if current_polarity == 'D': + # self.otf_geometry = self.otf_geometry.union(geo) + # else: + # self.otf_geometry = self.otf_geometry.difference(geo) + + # --- Buffered ---- width = self.apertures[last_path_aperture]["size"] geo = LineString(path).buffer(width/2) - if current_polarity == 'D': - self.otf_geometry = self.otf_geometry.union(geo) - else: - self.otf_geometry = self.otf_geometry.difference(geo) + poly_buffer.append(geo) + path = [path[-1]] + + # --- Apply buffer --- + if current_polarity == 'D': + self.solid_geometry = self.solid_geometry.union(cascaded_union(poly_buffer)) + else: + self.solid_geometry = self.solid_geometry.difference(cascaded_union(poly_buffer)) + poly_buffer = [] + current_polarity = match.group(1) continue @@ -1401,12 +1469,24 @@ class Gerber (Geometry): # self.paths.append({"linestring": LineString(path), # "aperture": last_path_aperture}) + ## --- OTF --- + # width = self.apertures[last_path_aperture]["size"] + # geo = LineString(path).buffer(width/2) + # if current_polarity == 'D': + # self.otf_geometry = self.otf_geometry.union(geo) + # else: + # self.otf_geometry = self.otf_geometry.difference(geo) + + ## --- Buffered --- width = self.apertures[last_path_aperture]["size"] geo = LineString(path).buffer(width/2) - if current_polarity == 'D': - self.otf_geometry = self.otf_geometry.union(geo) - else: - self.otf_geometry = self.otf_geometry.difference(geo) + poly_buffer.append(geo) + + # --- Apply buffer --- + if current_polarity == 'D': + self.solid_geometry = self.solid_geometry.union(cascaded_union(poly_buffer)) + else: + self.solid_geometry = self.solid_geometry.difference(cascaded_union(poly_buffer)) @staticmethod def create_flash_geometry(location, aperture): @@ -1464,79 +1544,79 @@ class Gerber (Geometry): return None - def do_flashes(self): - """ - Creates geometry for Gerber flashes (aperture on a single point). - """ - - self.flash_geometry = [] - for flash in self.flashes: - - try: - aperture = self.apertures[flash['aperture']] - except KeyError: - print "ERROR: Trying to flash with unknown aperture: ", flash['aperture'] - continue - - if aperture['type'] == 'C': # Circles - #circle = Point(flash['loc']).buffer(aperture['size']/2) - circle = flash['loc'].buffer(aperture['size']/2) - self.flash_geometry.append(circle) - continue - - if aperture['type'] == 'R': # Rectangles - loc = flash['loc'].coords[0] - width = aperture['width'] - height = aperture['height'] - minx = loc[0] - width/2 - maxx = loc[0] + width/2 - miny = loc[1] - height/2 - maxy = loc[1] + height/2 - rectangle = shply_box(minx, miny, maxx, maxy) - self.flash_geometry.append(rectangle) - continue - - if aperture['type'] == 'O': # Obround - loc = flash['loc'].coords[0] - width = aperture['width'] - height = aperture['height'] - if width > height: - p1 = Point(loc[0] + 0.5*(width-height), loc[1]) - p2 = Point(loc[0] - 0.5*(width-height), loc[1]) - c1 = p1.buffer(height*0.5) - c2 = p2.buffer(height*0.5) - else: - p1 = Point(loc[0], loc[1] + 0.5*(height-width)) - p2 = Point(loc[0], loc[1] - 0.5*(height-width)) - c1 = p1.buffer(width*0.5) - c2 = p2.buffer(width*0.5) - obround = cascaded_union([c1, c2]).convex_hull - self.flash_geometry.append(obround) - continue - - if aperture['type'] == 'P': # Regular polygon - loc = flash['loc'].coords[0] - diam = aperture['diam'] - n_vertices = aperture['nVertices'] - points = [] - for i in range(0, n_vertices): - x = loc[0] + diam * (cos(2 * pi * i / n_vertices)) - y = loc[1] + diam * (sin(2 * pi * i / n_vertices)) - points.append((x, y)) - ply = Polygon(points) - if 'rotation' in aperture: - ply = affinity.rotate(ply, aperture['rotation']) - self.flash_geometry.append(ply) - continue - - if aperture['type'] == 'AM': # Aperture Macro - loc = flash['loc'].coords[0] - flash_geo = aperture['macro'].make_geometry(aperture['modifiers']) - flash_geo_final = affinity.translate(flash_geo, xoff=loc[0], yoff=loc[1]) - self.flash_geometry.append(flash_geo_final) - continue - - print "WARNING: Aperture type %s not implemented" % (aperture['type']) + # def do_flashes(self): + # """ + # Creates geometry for Gerber flashes (aperture on a single point). + # """ + # + # self.flash_geometry = [] + # for flash in self.flashes: + # + # try: + # aperture = self.apertures[flash['aperture']] + # except KeyError: + # print "ERROR: Trying to flash with unknown aperture: ", flash['aperture'] + # continue + # + # if aperture['type'] == 'C': # Circles + # #circle = Point(flash['loc']).buffer(aperture['size']/2) + # circle = flash['loc'].buffer(aperture['size']/2) + # self.flash_geometry.append(circle) + # continue + # + # if aperture['type'] == 'R': # Rectangles + # loc = flash['loc'].coords[0] + # width = aperture['width'] + # height = aperture['height'] + # minx = loc[0] - width/2 + # maxx = loc[0] + width/2 + # miny = loc[1] - height/2 + # maxy = loc[1] + height/2 + # rectangle = shply_box(minx, miny, maxx, maxy) + # self.flash_geometry.append(rectangle) + # continue + # + # if aperture['type'] == 'O': # Obround + # loc = flash['loc'].coords[0] + # width = aperture['width'] + # height = aperture['height'] + # if width > height: + # p1 = Point(loc[0] + 0.5*(width-height), loc[1]) + # p2 = Point(loc[0] - 0.5*(width-height), loc[1]) + # c1 = p1.buffer(height*0.5) + # c2 = p2.buffer(height*0.5) + # else: + # p1 = Point(loc[0], loc[1] + 0.5*(height-width)) + # p2 = Point(loc[0], loc[1] - 0.5*(height-width)) + # c1 = p1.buffer(width*0.5) + # c2 = p2.buffer(width*0.5) + # obround = cascaded_union([c1, c2]).convex_hull + # self.flash_geometry.append(obround) + # continue + # + # if aperture['type'] == 'P': # Regular polygon + # loc = flash['loc'].coords[0] + # diam = aperture['diam'] + # n_vertices = aperture['nVertices'] + # points = [] + # for i in range(0, n_vertices): + # x = loc[0] + diam * (cos(2 * pi * i / n_vertices)) + # y = loc[1] + diam * (sin(2 * pi * i / n_vertices)) + # points.append((x, y)) + # ply = Polygon(points) + # if 'rotation' in aperture: + # ply = affinity.rotate(ply, aperture['rotation']) + # self.flash_geometry.append(ply) + # continue + # + # if aperture['type'] == 'AM': # Aperture Macro + # loc = flash['loc'].coords[0] + # flash_geo = aperture['macro'].make_geometry(aperture['modifiers']) + # flash_geo_final = affinity.translate(flash_geo, xoff=loc[0], yoff=loc[1]) + # self.flash_geometry.append(flash_geo_final) + # continue + # + # print "WARNING: Aperture type %s not implemented" % (aperture['type']) def create_geometry(self): """ @@ -1549,15 +1629,15 @@ class Gerber (Geometry): :return: None """ - self.buffer_paths() - - self.fix_regions() - - self.do_flashes() - - self.solid_geometry = cascaded_union(self.buffered_paths + - [poly['polygon'] for poly in self.regions] + - self.flash_geometry) + # self.buffer_paths() + # + # self.fix_regions() + # + # self.do_flashes() + # + # self.solid_geometry = cascaded_union(self.buffered_paths + + # [poly['polygon'] for poly in self.regions] + + # self.flash_geometry) def get_bounding_box(self, margin=0.0, rounded=False): """ @@ -1704,7 +1784,7 @@ class Excellon(Geometry): estr = efile.readlines() efile.close() self.parse_lines(estr) - + def parse_lines(self, elines): """ Main Excellon parser. @@ -1720,9 +1800,12 @@ class Excellon(Geometry): current_x = None current_y = None - i = 0 # Line number + line_num = 0 # Line number for eline in elines: - i += 1 + line_num += 1 + + ### Cleanup + eline = eline.strip(' \r\n') ## Header Begin/End ## if self.hbegin_re.search(eline): diff --git a/recent.json b/recent.json index 6a0e5a92..32a8fe91 100644 --- a/recent.json +++ b/recent.json @@ -1 +1 @@ -[{"kind": "gerber", "filename": "C:\\Users\\jpcaram\\Dropbox\\CNC\\pcbcam\\test_files\\Example1_copper_bottom_Gndplane_modified.gbr"}, {"kind": "gerber", "filename": "C:\\Users\\jpcaram\\Dropbox\\PhD\\Kenney\\Project Outputs for AnalogPredistortion1\\apd.GTL"}, {"kind": "gerber", "filename": "C:\\Users\\jpcaram\\Dropbox\\CNC\\pcbcam\\test_files\\WindMills - Bottom Copper 2.gbr"}, {"kind": "gerber", "filename": "C:\\Users\\jpcaram\\Dropbox\\CNC\\pcbcam\\test_files\\CC_LOAD_7000164-00_REV_A_copper_top.gbr"}, {"kind": "gerber", "filename": "C:\\Users\\jpcaram\\Dropbox\\CNC\\pcbcam\\test_files\\Example1_copper_bottom_Gndplane.gbr"}, {"kind": "excellon", "filename": "C:\\Users\\jpcaram\\Dropbox\\CNC\\pcbcam\\test_files\\TFTadapter.drl"}, {"kind": "gerber", "filename": "C:\\Users\\jpcaram\\Dropbox\\CNC\\pcbcam\\test_files\\PlacaReles-F_Cu.gtl"}, {"kind": "excellon", "filename": "C:\\Users\\jpcaram\\Dropbox\\CNC\\pcbcam\\test_files\\PlacaReles.drl"}, {"kind": "excellon", "filename": "C:\\Users\\jpcaram\\Dropbox\\CNC\\pcbcam\\test_files\\BLDC2003Through.drl"}, {"kind": "gerber", "filename": "C:\\Users\\jpcaram\\Dropbox\\PhD\\PLLs\\RTWO\\Project Outputs for RTWO1\\PCB1.GTL"}] \ No newline at end of file +[{"kind": "gerber", "filename": "C:\\Users\\jpcaram\\Dropbox\\CNC\\pcbcam\\test_files\\WindMills - Bottom Copper 2.gbr"}, {"kind": "gerber", "filename": "C:\\Users\\jpcaram\\Dropbox\\CNC\\pcbcam\\test_files\\Example1_copper_bottom_Gndplane_modified.gbr"}, {"kind": "gerber", "filename": "C:\\Users\\jpcaram\\Dropbox\\CNC\\pcbcam\\test_files\\Example1_copper_bottom_Gndplane.gbr"}, {"kind": "gerber", "filename": "C:\\Users\\jpcaram\\Dropbox\\CNC\\pcbcam\\test_files\\CC_LOAD_7000164-00_REV_A_copper_top.gbr"}, {"kind": "gerber", "filename": "C:\\Users\\jpcaram\\Dropbox\\CNC\\pcbcam\\test_files\\bedini 7 coils capacitor discharge.gbr"}, {"kind": "gerber", "filename": "C:\\Users\\jpcaram\\Dropbox\\CNC\\pcbcam\\test_files\\Gerbers\\AVR_Transistor_Tester_copper_bottom.GBL"}, {"kind": "gerber", "filename": "C:\\Users\\jpcaram\\Dropbox\\CNC\\pcbcam\\test_files\\Gerbers\\AVR_Transistor_Tester_copper_top.GTL"}, {"kind": "gerber", "filename": "C:\\Users\\jpcaram\\Dropbox\\CNC\\pcbcam\\test_files\\PlacaReles-F_Cu.gtl"}, {"kind": "gerber", "filename": "C:\\Users\\jpcaram\\Dropbox\\CNC\\pcbcam\\test_files\\maitest.gtl"}, {"kind": "gerber", "filename": "C:\\Users\\jpcaram\\Dropbox\\CNC\\pcbcam\\test_files\\Example1_copper_bottom.gbr"}] \ No newline at end of file diff --git a/tests/profile_gerber_parser.py b/tests/profile_gerber_parser.py new file mode 100644 index 00000000..ddd1ae16 --- /dev/null +++ b/tests/profile_gerber_parser.py @@ -0,0 +1,8 @@ +import os +os.chdir('../') + +from camlib import * + +g = Gerber() +g.parse_file(r'C:\Users\jpcaram\Dropbox\CNC\pcbcam\test_files\PlacaReles-F_Cu.gtl') +