Optimized Gerber parser. Some minor improvements to Excellon parser.
This commit is contained in:
parent
2ed0f73f87
commit
e0d2daca6c
54
FlatCAM.py
54
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):
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
463
camlib.py
463
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):
|
||||
|
|
|
@ -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"}]
|
|
@ -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')
|
||||
|
Loading…
Reference in New Issue