Optimized Gerber parser. Some minor improvements to Excellon parser.

This commit is contained in:
Juan Pablo Caram
2014-04-18 20:20:17 -04:00
parent 2ed0f73f87
commit e0d2daca6c
6 changed files with 326 additions and 237 deletions

View File

@@ -7,38 +7,22 @@
############################################################ ############################################################
import threading 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 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 sys
import urllib import urllib
import copy import copy
import random import random
from gi.repository import Gtk, GdkPixbuf
from matplotlib.backends.backend_gtk3agg import FigureCanvasGTK3Agg as FigureCanvas
from shapely import speedups from shapely import speedups
######################################## ########################################
## Imports part of FlatCAM ## ## Imports part of FlatCAM ##
######################################## ########################################
from camlib import *
from FlatCAMObj import * from FlatCAMObj import *
from FlatCAMWorker import Worker from FlatCAMWorker import Worker
from FlatCAMException import *
######################################## ########################################
@@ -909,26 +893,28 @@ class App:
# Further parsing # Further parsing
GLib.idle_add(lambda: app_obj.set_progress_bar(0.5, "Creating Geometry ...")) GLib.idle_add(lambda: app_obj.set_progress_bar(0.5, "Creating Geometry ..."))
#gerber_obj.create_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 ...")) GLib.idle_add(lambda: app_obj.set_progress_bar(0.6, "Plotting ..."))
# Object name # Object name
name = filename.split('/')[-1].split('\\')[-1] name = filename.split('/')[-1].split('\\')[-1]
self.new_object("gerber", name, obj_init)
# New object creation and file processing # New object creation and file processing
try: # try:
self.new_object("gerber", name, obj_init) # self.new_object("gerber", name, obj_init)
except: # except:
e = sys.exc_info() # e = sys.exc_info()
print "ERROR:", e[0] # print "ERROR:", e[0]
traceback.print_exc() # traceback.print_exc()
self.message_dialog("Failed to create Gerber Object", # self.message_dialog("Failed to create Gerber Object",
"Attempting to create a FlatCAM Gerber Object from " + # "Attempting to create a FlatCAM Gerber Object from " +
"Gerber file failed during processing:\n" + # "Gerber file failed during processing:\n" +
str(e[0]) + " " + str(e[1]), kind="error") # str(e[0]) + " " + str(e[1]), kind="error")
GLib.timeout_add_seconds(1, lambda: self.set_progress_bar(0.0, "Idle")) # GLib.timeout_add_seconds(1, lambda: self.set_progress_bar(0.0, "Idle"))
self.collection.delete_active() # self.collection.delete_active()
return # return
# Register recent file # Register recent file
self.register_recent("gerber", filename) self.register_recent("gerber", filename)
@@ -2848,7 +2834,7 @@ class ObjectCollection:
try: try:
model, treeiter = self.tree_selection.get_selected() model, treeiter = self.tree_selection.get_selected()
return model[treeiter][0] return model[treeiter][0]
except ValueError: except (TypeError, ValueError):
return None return None
def set_list_selection(self, name): def set_list_selection(self, name):

7
FlatCAMException.py Normal file
View File

@@ -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

View File

@@ -297,12 +297,13 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
if not FlatCAMObj.plot(self): if not FlatCAMObj.plot(self):
return return
if self.options["mergepolys"]: # if self.options["mergepolys"]:
geometry = self.solid_geometry # geometry = self.solid_geometry
else: # else:
geometry = self.buffered_paths + \ # geometry = self.buffered_paths + \
[poly['polygon'] for poly in self.regions] + \ # [poly['polygon'] for poly in self.regions] + \
self.flash_geometry # self.flash_geometry
geometry = self.solid_geometry
# Make sure geometry is iterable. # Make sure geometry is iterable.
try: try:
@@ -318,12 +319,16 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
if self.options["solid"]: if self.options["solid"]:
for poly in geometry: for poly in geometry:
# TODO: Too many things hardcoded. # TODO: Too many things hardcoded.
patch = PolygonPatch(poly, try:
facecolor="#BBF268", patch = PolygonPatch(poly,
edgecolor="#006E20", facecolor="#BBF268",
alpha=0.75, edgecolor="#006E20",
zorder=2) alpha=0.75,
self.axes.add_patch(patch) zorder=2)
self.axes.add_patch(patch)
except AssertionError:
print "WARNING: A geometry component was not a polygon:"
print poly
else: else:
for poly in geometry: for poly in geometry:
x, y = poly.exterior.xy x, y = poly.exterior.xy

463
camlib.py
View File

@@ -620,7 +620,9 @@ class Gerber (Geometry):
# Initialize parent # Initialize parent
Geometry.__init__(self) Geometry.__init__(self)
self.solid_geometry = Polygon()
# Number format # Number format
self.int_digits = 3 self.int_digits = 3
"""Number of integer digits in Gerber numbers. Used during parsing.""" """Number of integer digits in Gerber numbers. Used during parsing."""
@@ -635,27 +637,26 @@ class Gerber (Geometry):
self.apertures = {} self.apertures = {}
# Paths [{'linestring':LineString, 'aperture':str}] # Paths [{'linestring':LineString, 'aperture':str}]
self.paths = [] # self.paths = []
# Buffered Paths [Polygon] # Buffered Paths [Polygon]
# Paths transformed into Polygons by # Paths transformed into Polygons by
# offsetting the aperture size/2 # offsetting the aperture size/2
self.buffered_paths = [] # self.buffered_paths = []
# Polygon regions [{'polygon':Polygon, 'aperture':str}] # Polygon regions [{'polygon':Polygon, 'aperture':str}]
self.regions = [] # self.regions = []
# Flashes [{'loc':[float,float], 'aperture':str}] # Flashes [{'loc':[float,float], 'aperture':str}]
self.flashes = [] # self.flashes = []
# Geometry from flashes # Geometry from flashes
self.flash_geometry = [] # self.flash_geometry = []
# On-the-fly geometry. Initialized to an empty polygon # On-the-fly geometry. Initialized to an empty polygon
self.otf_geometry = Polygon() self.otf_geometry = Polygon()
# Aperture Macros # Aperture Macros
# TODO: Make sure these can be serialized
self.aperture_macros = {} self.aperture_macros = {}
# Attributes to be included in serialization # 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. # 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]))?\*$') 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])\*$') self.opcode_re = re.compile(r'^D0?([123])\*$')
# G02/3... - Circular interpolation with coordinates # G02/3... - Circular interpolation with coordinates
@@ -782,18 +783,18 @@ class Gerber (Geometry):
# for fl in self.flashes: # for fl in self.flashes:
# fl['loc'] = affinity.scale(fl['loc'], factor, factor, origin=(0, 0)) # fl['loc'] = affinity.scale(fl['loc'], factor, factor, origin=(0, 0))
## Regions # ## Regions
for reg in self.regions: # for reg in self.regions:
reg['polygon'] = affinity.scale(reg['polygon'], factor, factor, # reg['polygon'] = affinity.scale(reg['polygon'], factor, factor,
origin=(0, 0)) # origin=(0, 0))
#
## Flashes # ## Flashes
for flash in self.flash_geometry: # for flash in self.flash_geometry:
flash = affinity.scale(flash, factor, factor, origin=(0, 0)) # flash = affinity.scale(flash, factor, factor, origin=(0, 0))
#
## Buffered paths # ## Buffered paths
for bp in self.buffered_paths: # for bp in self.buffered_paths:
bp = affinity.scale(bp, factor, factor, origin=(0, 0)) # bp = affinity.scale(bp, factor, factor, origin=(0, 0))
## solid_geometry ??? ## solid_geometry ???
# It's a cascaded union of objects. # It's a cascaded union of objects.
@@ -834,18 +835,18 @@ class Gerber (Geometry):
# for fl in self.flashes: # for fl in self.flashes:
# fl['loc'] = affinity.translate(fl['loc'], xoff=dx, yoff=dy) # fl['loc'] = affinity.translate(fl['loc'], xoff=dx, yoff=dy)
## Regions # ## Regions
for reg in self.regions: # for reg in self.regions:
reg['polygon'] = affinity.translate(reg['polygon'], # reg['polygon'] = affinity.translate(reg['polygon'],
xoff=dx, yoff=dy) # xoff=dx, yoff=dy)
#
## Buffered paths # ## Buffered paths
for bp in self.buffered_paths: # for bp in self.buffered_paths:
bp = affinity.translate(bp, xoff=dx, yoff=dy) # bp = affinity.translate(bp, xoff=dx, yoff=dy)
#
## Flash geometry # ## Flash geometry
for fl in self.flash_geometry: # for fl in self.flash_geometry:
fl = affinity.translate(fl, xoff=dx, yoff=dy) # fl = affinity.translate(fl, xoff=dx, yoff=dy)
## Solid geometry ## Solid geometry
self.solid_geometry = affinity.translate(self.solid_geometry, xoff=dx, yoff=dy) self.solid_geometry = affinity.translate(self.solid_geometry, xoff=dx, yoff=dy)
@@ -887,18 +888,18 @@ class Gerber (Geometry):
# for fl in self.flashes: # for fl in self.flashes:
# fl['loc'] = affinity.scale(fl['loc'], xscale, yscale, origin=(px, py)) # fl['loc'] = affinity.scale(fl['loc'], xscale, yscale, origin=(px, py))
## Regions # ## Regions
for reg in self.regions: # for reg in self.regions:
reg['polygon'] = affinity.scale(reg['polygon'], xscale, yscale, # reg['polygon'] = affinity.scale(reg['polygon'], xscale, yscale,
origin=(px, py)) # origin=(px, py))
#
## Flashes # ## Flashes
for flash in self.flash_geometry: # for flash in self.flash_geometry:
flash = affinity.scale(flash, xscale, yscale, origin=(px, py)) # flash = affinity.scale(flash, xscale, yscale, origin=(px, py))
#
## Buffered paths # ## Buffered paths
for bp in self.buffered_paths: # for bp in self.buffered_paths:
bp = affinity.scale(bp, xscale, yscale, origin=(px, py)) # bp = affinity.scale(bp, xscale, yscale, origin=(px, py))
## solid_geometry ??? ## solid_geometry ???
# It's a cascaded union of objects. # It's a cascaded union of objects.
@@ -908,34 +909,34 @@ class Gerber (Geometry):
# # Now buffered_paths, flash_geometry and solid_geometry # # Now buffered_paths, flash_geometry and solid_geometry
# self.create_geometry() # self.create_geometry()
def fix_regions(self): # def fix_regions(self):
""" # """
Overwrites the region polygons with fixed # Overwrites the region polygons with fixed
versions if found to be invalid (according to Shapely). # versions if found to be invalid (according to Shapely).
#
:return: None # :return: None
""" # """
#
for region in self.regions: # for region in self.regions:
if not region['polygon'].is_valid: # if not region['polygon'].is_valid:
region['polygon'] = region['polygon'].buffer(0) # region['polygon'] = region['polygon'].buffer(0)
def buffer_paths(self): # def buffer_paths(self):
""" # """
This is part of the parsing process. "Thickens" the paths # This is part of the parsing process. "Thickens" the paths
by their appertures. This will only work for circular appertures. # by their appertures. This will only work for circular appertures.
#
:return: None # :return: None
""" # """
#
self.buffered_paths = [] # self.buffered_paths = []
for path in self.paths: # for path in self.paths:
try: # try:
width = self.apertures[path["aperture"]]["size"] # width = self.apertures[path["aperture"]]["size"]
self.buffered_paths.append(path["linestring"].buffer(width/2)) # self.buffered_paths.append(path["linestring"].buffer(width/2))
except KeyError: # except KeyError:
print "ERROR: Failed to buffer path: ", path # print "ERROR: Failed to buffer path: ", path
print "Apertures: ", self.apertures # print "Apertures: ", self.apertures
def aperture_parse(self, apertureId, apertureType, apParameters): def aperture_parse(self, apertureId, apertureType, apParameters):
""" """
@@ -1014,7 +1015,7 @@ class Gerber (Geometry):
gstr = gfile.readlines() gstr = gfile.readlines()
gfile.close() gfile.close()
self.parse_lines(gstr) self.parse_lines(gstr)
def parse_lines(self, glines): def parse_lines(self, glines):
""" """
Main Gerber parser. Reads Gerber and populates ``self.paths``, ``self.apertures``, Main Gerber parser. Reads Gerber and populates ``self.paths``, ``self.apertures``,
@@ -1027,7 +1028,14 @@ class Gerber (Geometry):
:rtype: None :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 last_path_aperture = None
current_aperture = None current_aperture = None
@@ -1065,6 +1073,9 @@ class Gerber (Geometry):
for gline in glines: for gline in glines:
line_num += 1 line_num += 1
### Cleanup
gline = gline.strip(' \r\n')
### Aperture Macros ### Aperture Macros
# Having this at the beggining will slow things down # Having this at the beggining will slow things down
# but macros can have complicated statements than could # but macros can have complicated statements than could
@@ -1128,6 +1139,19 @@ class Gerber (Geometry):
# "aperture": last_path_aperture}) # "aperture": last_path_aperture})
# --- OTF --- # --- 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: if making_region:
geo = Polygon(path) geo = Polygon(path)
else: else:
@@ -1135,10 +1159,7 @@ class Gerber (Geometry):
print "Warning: No aperture defined for curent path. (%d)" % line_num print "Warning: No aperture defined for curent path. (%d)" % line_num
width = self.apertures[last_path_aperture]["size"] width = self.apertures[last_path_aperture]["size"]
geo = LineString(path).buffer(width/2) geo = LineString(path).buffer(width/2)
if current_polarity == 'D': poly_buffer.append(geo)
self.otf_geometry = self.otf_geometry.union(geo)
else:
self.otf_geometry = self.otf_geometry.difference(geo)
path = [[current_x, current_y]] # Start new path path = [[current_x, current_y]] # Start new path
@@ -1148,12 +1169,17 @@ class Gerber (Geometry):
# "aperture": current_aperture}) # "aperture": current_aperture})
# --- OTF --- # --- 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]), flash = Gerber.create_flash_geometry(Point([current_x, current_y]),
self.apertures[current_aperture]) self.apertures[current_aperture])
if current_polarity == 'D': poly_buffer.append(flash)
self.otf_geometry = self.otf_geometry.union(flash)
else:
self.otf_geometry = self.otf_geometry.difference(flash)
continue continue
@@ -1206,12 +1232,17 @@ class Gerber (Geometry):
# "aperture": last_path_aperture}) # "aperture": last_path_aperture})
# --- OTF --- # --- 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"] width = self.apertures[last_path_aperture]["size"]
buffered = LineString(path).buffer(width/2) buffered = LineString(path).buffer(width/2)
if current_polarity == 'D': poly_buffer.append(buffered)
self.otf_geometry = self.otf_geometry.union(buffered)
else:
self.otf_geometry = self.otf_geometry.difference(buffered)
current_x = x current_x = x
current_y = y current_y = y
@@ -1252,12 +1283,20 @@ class Gerber (Geometry):
if match: if match:
current_operation_code = int(match.group(1)) current_operation_code = int(match.group(1))
if current_operation_code == 3: 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]), flash = Gerber.create_flash_geometry(Point(path[-1]),
self.apertures[current_aperture]) self.apertures[current_aperture])
if current_polarity == 'D': poly_buffer.append(flash)
self.otf_geometry = self.otf_geometry.union(flash)
else:
self.otf_geometry = self.otf_geometry.difference(flash)
continue continue
### G74/75* - Single or multiple quadrant arcs ### G74/75* - Single or multiple quadrant arcs
@@ -1273,12 +1312,20 @@ class Gerber (Geometry):
if self.regionon_re.search(gline): if self.regionon_re.search(gline):
if len(path) > 1: if len(path) > 1:
# Take care of what is left in the path # 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"] width = self.apertures[last_path_aperture]["size"]
geo = LineString(path).buffer(width/2) geo = LineString(path).buffer(width/2)
if current_polarity == 'D': poly_buffer.append(geo)
self.otf_geometry = self.otf_geometry.union(geo)
else:
self.otf_geometry = self.otf_geometry.difference(geo)
path = [path[-1]] path = [path[-1]]
making_region = True making_region = True
@@ -1304,13 +1351,19 @@ class Gerber (Geometry):
# "aperture": last_path_aperture}) # "aperture": last_path_aperture})
# --- OTF --- # --- 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) region = Polygon(path)
if not region.is_valid: if not region.is_valid:
region = region.buffer(0) region = region.buffer(0)
if current_polarity == 'D': poly_buffer.append(region)
self.otf_geometry = self.otf_geometry.union(region)
else:
self.otf_geometry = self.otf_geometry.difference(region)
path = [[current_x, current_y]] # Start new path path = [[current_x, current_y]] # Start new path
continue continue
@@ -1343,13 +1396,28 @@ class Gerber (Geometry):
if match: if match:
if len(path) > 1 and current_polarity != match.group(1): 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"] width = self.apertures[last_path_aperture]["size"]
geo = LineString(path).buffer(width/2) geo = LineString(path).buffer(width/2)
if current_polarity == 'D': poly_buffer.append(geo)
self.otf_geometry = self.otf_geometry.union(geo)
else:
self.otf_geometry = self.otf_geometry.difference(geo)
path = [path[-1]] 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) current_polarity = match.group(1)
continue continue
@@ -1401,12 +1469,24 @@ class Gerber (Geometry):
# self.paths.append({"linestring": LineString(path), # self.paths.append({"linestring": LineString(path),
# "aperture": last_path_aperture}) # "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"] width = self.apertures[last_path_aperture]["size"]
geo = LineString(path).buffer(width/2) geo = LineString(path).buffer(width/2)
if current_polarity == 'D': poly_buffer.append(geo)
self.otf_geometry = self.otf_geometry.union(geo)
else: # --- Apply buffer ---
self.otf_geometry = self.otf_geometry.difference(geo) 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 @staticmethod
def create_flash_geometry(location, aperture): def create_flash_geometry(location, aperture):
@@ -1464,79 +1544,79 @@ class Gerber (Geometry):
return None return None
def do_flashes(self): # def do_flashes(self):
""" # """
Creates geometry for Gerber flashes (aperture on a single point). # Creates geometry for Gerber flashes (aperture on a single point).
""" # """
#
self.flash_geometry = [] # self.flash_geometry = []
for flash in self.flashes: # for flash in self.flashes:
#
try: # try:
aperture = self.apertures[flash['aperture']] # aperture = self.apertures[flash['aperture']]
except KeyError: # except KeyError:
print "ERROR: Trying to flash with unknown aperture: ", flash['aperture'] # print "ERROR: Trying to flash with unknown aperture: ", flash['aperture']
continue # continue
#
if aperture['type'] == 'C': # Circles # if aperture['type'] == 'C': # Circles
#circle = Point(flash['loc']).buffer(aperture['size']/2) # #circle = Point(flash['loc']).buffer(aperture['size']/2)
circle = flash['loc'].buffer(aperture['size']/2) # circle = flash['loc'].buffer(aperture['size']/2)
self.flash_geometry.append(circle) # self.flash_geometry.append(circle)
continue # continue
#
if aperture['type'] == 'R': # Rectangles # if aperture['type'] == 'R': # Rectangles
loc = flash['loc'].coords[0] # loc = flash['loc'].coords[0]
width = aperture['width'] # width = aperture['width']
height = aperture['height'] # height = aperture['height']
minx = loc[0] - width/2 # minx = loc[0] - width/2
maxx = loc[0] + width/2 # maxx = loc[0] + width/2
miny = loc[1] - height/2 # miny = loc[1] - height/2
maxy = loc[1] + height/2 # maxy = loc[1] + height/2
rectangle = shply_box(minx, miny, maxx, maxy) # rectangle = shply_box(minx, miny, maxx, maxy)
self.flash_geometry.append(rectangle) # self.flash_geometry.append(rectangle)
continue # continue
#
if aperture['type'] == 'O': # Obround # if aperture['type'] == 'O': # Obround
loc = flash['loc'].coords[0] # loc = flash['loc'].coords[0]
width = aperture['width'] # width = aperture['width']
height = aperture['height'] # height = aperture['height']
if width > height: # if width > height:
p1 = Point(loc[0] + 0.5*(width-height), loc[1]) # p1 = Point(loc[0] + 0.5*(width-height), loc[1])
p2 = 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) # c1 = p1.buffer(height*0.5)
c2 = p2.buffer(height*0.5) # c2 = p2.buffer(height*0.5)
else: # else:
p1 = Point(loc[0], loc[1] + 0.5*(height-width)) # p1 = Point(loc[0], loc[1] + 0.5*(height-width))
p2 = 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) # c1 = p1.buffer(width*0.5)
c2 = p2.buffer(width*0.5) # c2 = p2.buffer(width*0.5)
obround = cascaded_union([c1, c2]).convex_hull # obround = cascaded_union([c1, c2]).convex_hull
self.flash_geometry.append(obround) # self.flash_geometry.append(obround)
continue # continue
#
if aperture['type'] == 'P': # Regular polygon # if aperture['type'] == 'P': # Regular polygon
loc = flash['loc'].coords[0] # loc = flash['loc'].coords[0]
diam = aperture['diam'] # diam = aperture['diam']
n_vertices = aperture['nVertices'] # n_vertices = aperture['nVertices']
points = [] # points = []
for i in range(0, n_vertices): # for i in range(0, n_vertices):
x = loc[0] + diam * (cos(2 * pi * i / n_vertices)) # x = loc[0] + diam * (cos(2 * pi * i / n_vertices))
y = loc[1] + diam * (sin(2 * pi * i / n_vertices)) # y = loc[1] + diam * (sin(2 * pi * i / n_vertices))
points.append((x, y)) # points.append((x, y))
ply = Polygon(points) # ply = Polygon(points)
if 'rotation' in aperture: # if 'rotation' in aperture:
ply = affinity.rotate(ply, aperture['rotation']) # ply = affinity.rotate(ply, aperture['rotation'])
self.flash_geometry.append(ply) # self.flash_geometry.append(ply)
continue # continue
#
if aperture['type'] == 'AM': # Aperture Macro # if aperture['type'] == 'AM': # Aperture Macro
loc = flash['loc'].coords[0] # loc = flash['loc'].coords[0]
flash_geo = aperture['macro'].make_geometry(aperture['modifiers']) # flash_geo = aperture['macro'].make_geometry(aperture['modifiers'])
flash_geo_final = affinity.translate(flash_geo, xoff=loc[0], yoff=loc[1]) # flash_geo_final = affinity.translate(flash_geo, xoff=loc[0], yoff=loc[1])
self.flash_geometry.append(flash_geo_final) # self.flash_geometry.append(flash_geo_final)
continue # continue
#
print "WARNING: Aperture type %s not implemented" % (aperture['type']) # print "WARNING: Aperture type %s not implemented" % (aperture['type'])
def create_geometry(self): def create_geometry(self):
""" """
@@ -1549,15 +1629,15 @@ class Gerber (Geometry):
:return: None :return: None
""" """
self.buffer_paths() # self.buffer_paths()
#
self.fix_regions() # self.fix_regions()
#
self.do_flashes() # self.do_flashes()
#
self.solid_geometry = cascaded_union(self.buffered_paths + # self.solid_geometry = cascaded_union(self.buffered_paths +
[poly['polygon'] for poly in self.regions] + # [poly['polygon'] for poly in self.regions] +
self.flash_geometry) # self.flash_geometry)
def get_bounding_box(self, margin=0.0, rounded=False): def get_bounding_box(self, margin=0.0, rounded=False):
""" """
@@ -1704,7 +1784,7 @@ class Excellon(Geometry):
estr = efile.readlines() estr = efile.readlines()
efile.close() efile.close()
self.parse_lines(estr) self.parse_lines(estr)
def parse_lines(self, elines): def parse_lines(self, elines):
""" """
Main Excellon parser. Main Excellon parser.
@@ -1720,9 +1800,12 @@ class Excellon(Geometry):
current_x = None current_x = None
current_y = None current_y = None
i = 0 # Line number line_num = 0 # Line number
for eline in elines: for eline in elines:
i += 1 line_num += 1
### Cleanup
eline = eline.strip(' \r\n')
## Header Begin/End ## ## Header Begin/End ##
if self.hbegin_re.search(eline): if self.hbegin_re.search(eline):

View File

@@ -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"}] [{"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"}]

View File

@@ -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')