Fixes to gerber parser related to aperture macros and aperture definitions allowed characters in names.
This commit is contained in:
639
camlib.py
639
camlib.py
@@ -6,6 +6,8 @@
|
|||||||
# MIT Licence #
|
# MIT Licence #
|
||||||
############################################################
|
############################################################
|
||||||
|
|
||||||
|
import traceback
|
||||||
|
|
||||||
from numpy import arctan2, Inf, array, sqrt, pi, ceil, sin, cos
|
from numpy import arctan2, Inf, array, sqrt, pi, ceil, sin, cos
|
||||||
from matplotlib.figure import Figure
|
from matplotlib.figure import Figure
|
||||||
import re
|
import re
|
||||||
@@ -31,7 +33,8 @@ import logging
|
|||||||
|
|
||||||
log = logging.getLogger('base2')
|
log = logging.getLogger('base2')
|
||||||
#log.setLevel(logging.DEBUG)
|
#log.setLevel(logging.DEBUG)
|
||||||
log.setLevel(logging.WARNING)
|
#log.setLevel(logging.WARNING)
|
||||||
|
log.setLevel(logging.INFO)
|
||||||
formatter = logging.Formatter('[%(levelname)s] %(message)s')
|
formatter = logging.Formatter('[%(levelname)s] %(message)s')
|
||||||
handler = logging.StreamHandler()
|
handler = logging.StreamHandler()
|
||||||
handler.setFormatter(formatter)
|
handler.setFormatter(formatter)
|
||||||
@@ -191,6 +194,16 @@ class Geometry(object):
|
|||||||
|
|
||||||
|
|
||||||
class ApertureMacro:
|
class ApertureMacro:
|
||||||
|
"""
|
||||||
|
Syntax of aperture macros.
|
||||||
|
|
||||||
|
<AM command>: AM<Aperture macro name>*<Macro content>
|
||||||
|
<Macro content>: {{<Variable definition>*}{<Primitive>*}}
|
||||||
|
<Variable definition>: $K=<Arithmetic expression>
|
||||||
|
<Primitive>: <Primitive code>,<Modifier>{,<Modifier>}|<Comment>
|
||||||
|
<Modifier>: $M|< Arithmetic expression>
|
||||||
|
<Comment>: 0 <Text>
|
||||||
|
"""
|
||||||
|
|
||||||
## Regular expressions
|
## Regular expressions
|
||||||
am1_re = re.compile(r'^%AM([^\*]+)\*(.+)?(%)?$')
|
am1_re = re.compile(r'^%AM([^\*]+)\*(.+)?(%)?$')
|
||||||
@@ -635,11 +648,11 @@ class Gerber (Geometry):
|
|||||||
self.comm_re = re.compile(r'^G0?4(.*)$')
|
self.comm_re = re.compile(r'^G0?4(.*)$')
|
||||||
|
|
||||||
# AD - Aperture definition
|
# AD - Aperture definition
|
||||||
self.ad_re = re.compile(r'^%ADD(\d\d+)([a-zA-Z0-9]*)(?:,(.*))?\*%$')
|
self.ad_re = re.compile(r'^%ADD(\d\d+)([a-zA-Z_$\.][a-zA-Z0-9_$\.]*)(?:,(.*))?\*%$')
|
||||||
|
|
||||||
# AM - Aperture Macro
|
# AM - Aperture Macro
|
||||||
# Beginning of macro (Ends with *%):
|
# Beginning of macro (Ends with *%):
|
||||||
self.am_re = re.compile(r'^%AM([a-zA-Z0-9]*)\*')
|
#self.am_re = re.compile(r'^%AM([a-zA-Z0-9]*)\*')
|
||||||
|
|
||||||
# Tool change
|
# Tool change
|
||||||
# May begin with G54 but that is deprecated
|
# May begin with G54 but that is deprecated
|
||||||
@@ -694,7 +707,7 @@ class Gerber (Geometry):
|
|||||||
self.absrel_re = re.compile(r'^G9([01])\*$')
|
self.absrel_re = re.compile(r'^G9([01])\*$')
|
||||||
|
|
||||||
# Aperture macros
|
# Aperture macros
|
||||||
self.am1_re = re.compile(r'^%AM([^\*]+)\*(.+)?(%)?$')
|
self.am1_re = re.compile(r'^%AM([^\*]+)\*([^%]+)?(%)?$')
|
||||||
self.am2_re = re.compile(r'(.*)%$')
|
self.am2_re = re.compile(r'(.*)%$')
|
||||||
|
|
||||||
# TODO: This is bad.
|
# TODO: This is bad.
|
||||||
@@ -915,344 +928,356 @@ class Gerber (Geometry):
|
|||||||
|
|
||||||
#### Parsing starts here ####
|
#### Parsing starts here ####
|
||||||
line_num = 0
|
line_num = 0
|
||||||
for gline in glines:
|
gline = ""
|
||||||
line_num += 1
|
try:
|
||||||
|
for gline in glines:
|
||||||
|
line_num += 1
|
||||||
|
|
||||||
### Cleanup
|
### Cleanup
|
||||||
gline = gline.strip(' \r\n')
|
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
|
||||||
# be caught by other ptterns.
|
# be caught by other ptterns.
|
||||||
if current_macro is None: # No macro started yet
|
if current_macro is None: # No macro started yet
|
||||||
match = self.am1_re.search(gline)
|
match = self.am1_re.search(gline)
|
||||||
# Start macro if match, else not an AM, carry on.
|
# Start macro if match, else not an AM, carry on.
|
||||||
if match:
|
if match:
|
||||||
current_macro = match.group(1)
|
log.info("Starting macro. Line %d: %s" % (line_num, gline))
|
||||||
self.aperture_macros[current_macro] = ApertureMacro(name=current_macro)
|
current_macro = match.group(1)
|
||||||
if match.group(2): # Append
|
self.aperture_macros[current_macro] = ApertureMacro(name=current_macro)
|
||||||
self.aperture_macros[current_macro].append(match.group(2))
|
if match.group(2): # Append
|
||||||
if match.group(3): # Finish macro
|
self.aperture_macros[current_macro].append(match.group(2))
|
||||||
|
if match.group(3): # Finish macro
|
||||||
|
#self.aperture_macros[current_macro].parse_content()
|
||||||
|
current_macro = None
|
||||||
|
log.info("Macro complete in 1 line.")
|
||||||
|
continue
|
||||||
|
else: # Continue macro
|
||||||
|
log.info("Continuing macro. Line %d." % line_num)
|
||||||
|
match = self.am2_re.search(gline)
|
||||||
|
if match: # Finish macro
|
||||||
|
log.info("End of macro. Line %d." % line_num)
|
||||||
|
self.aperture_macros[current_macro].append(match.group(1))
|
||||||
#self.aperture_macros[current_macro].parse_content()
|
#self.aperture_macros[current_macro].parse_content()
|
||||||
current_macro = None
|
current_macro = None
|
||||||
continue
|
else: # Append
|
||||||
else: # Continue macro
|
self.aperture_macros[current_macro].append(gline)
|
||||||
match = self.am2_re.search(gline)
|
|
||||||
if match: # Finish macro
|
|
||||||
self.aperture_macros[current_macro].append(match.group(1))
|
|
||||||
#self.aperture_macros[current_macro].parse_content()
|
|
||||||
current_macro = None
|
|
||||||
else: # Append
|
|
||||||
self.aperture_macros[current_macro].append(gline)
|
|
||||||
continue
|
|
||||||
|
|
||||||
### G01 - Linear interpolation plus flashes
|
|
||||||
# Operation code (D0x) missing is deprecated... oh well I will support it.
|
|
||||||
# REGEX: r'^(?:G0?(1))?(?:X(-?\d+))?(?:Y(-?\d+))?(?:D0([123]))?\*$'
|
|
||||||
match = self.lin_re.search(gline)
|
|
||||||
if match:
|
|
||||||
# Dxx alone?
|
|
||||||
# if match.group(1) is None and match.group(2) is None and match.group(3) is None:
|
|
||||||
# try:
|
|
||||||
# current_operation_code = int(match.group(4))
|
|
||||||
# except:
|
|
||||||
# pass # A line with just * will match too.
|
|
||||||
# continue
|
|
||||||
# NOTE: Letting it continue allows it to react to the
|
|
||||||
# operation code.
|
|
||||||
|
|
||||||
# Parse coordinates
|
|
||||||
if match.group(2) is not None:
|
|
||||||
current_x = parse_gerber_number(match.group(2), self.frac_digits)
|
|
||||||
if match.group(3) is not None:
|
|
||||||
current_y = parse_gerber_number(match.group(3), self.frac_digits)
|
|
||||||
|
|
||||||
# Parse operation code
|
|
||||||
if match.group(4) is not None:
|
|
||||||
current_operation_code = int(match.group(4))
|
|
||||||
|
|
||||||
# Pen down: add segment
|
|
||||||
if current_operation_code == 1:
|
|
||||||
path.append([current_x, current_y])
|
|
||||||
last_path_aperture = current_aperture
|
|
||||||
|
|
||||||
elif current_operation_code == 2:
|
|
||||||
if len(path) > 1:
|
|
||||||
|
|
||||||
## --- BUFFERED ---
|
|
||||||
if making_region:
|
|
||||||
geo = Polygon(path)
|
|
||||||
else:
|
|
||||||
if last_path_aperture is None:
|
|
||||||
log.warning("No aperture defined for curent path. (%d)" % line_num)
|
|
||||||
width = self.apertures[last_path_aperture]["size"]
|
|
||||||
geo = LineString(path).buffer(width/2)
|
|
||||||
poly_buffer.append(geo)
|
|
||||||
|
|
||||||
path = [[current_x, current_y]] # Start new path
|
|
||||||
|
|
||||||
# Flash
|
|
||||||
elif current_operation_code == 3:
|
|
||||||
|
|
||||||
# --- BUFFERED ---
|
|
||||||
flash = Gerber.create_flash_geometry(Point([current_x, current_y]),
|
|
||||||
self.apertures[current_aperture])
|
|
||||||
poly_buffer.append(flash)
|
|
||||||
|
|
||||||
continue
|
|
||||||
|
|
||||||
### G02/3 - Circular interpolation
|
|
||||||
# 2-clockwise, 3-counterclockwise
|
|
||||||
match = self.circ_re.search(gline)
|
|
||||||
if match:
|
|
||||||
|
|
||||||
mode, x, y, i, j, d = match.groups()
|
|
||||||
try:
|
|
||||||
x = parse_gerber_number(x, self.frac_digits)
|
|
||||||
except:
|
|
||||||
x = current_x
|
|
||||||
try:
|
|
||||||
y = parse_gerber_number(y, self.frac_digits)
|
|
||||||
except:
|
|
||||||
y = current_y
|
|
||||||
try:
|
|
||||||
i = parse_gerber_number(i, self.frac_digits)
|
|
||||||
except:
|
|
||||||
i = 0
|
|
||||||
try:
|
|
||||||
j = parse_gerber_number(j, self.frac_digits)
|
|
||||||
except:
|
|
||||||
j = 0
|
|
||||||
|
|
||||||
if quadrant_mode is None:
|
|
||||||
log.error("Found arc without preceding quadrant specification G74 or G75. (%d)" % line_num)
|
|
||||||
log.error(gline)
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if mode is None and current_interpolation_mode not in [2, 3]:
|
### G01 - Linear interpolation plus flashes
|
||||||
log.error("Found arc without circular interpolation mode defined. (%d)" % line_num)
|
# Operation code (D0x) missing is deprecated... oh well I will support it.
|
||||||
log.error(gline)
|
# REGEX: r'^(?:G0?(1))?(?:X(-?\d+))?(?:Y(-?\d+))?(?:D0([123]))?\*$'
|
||||||
continue
|
match = self.lin_re.search(gline)
|
||||||
elif mode is not None:
|
if match:
|
||||||
current_interpolation_mode = int(mode)
|
# Dxx alone?
|
||||||
|
# if match.group(1) is None and match.group(2) is None and match.group(3) is None:
|
||||||
|
# try:
|
||||||
|
# current_operation_code = int(match.group(4))
|
||||||
|
# except:
|
||||||
|
# pass # A line with just * will match too.
|
||||||
|
# continue
|
||||||
|
# NOTE: Letting it continue allows it to react to the
|
||||||
|
# operation code.
|
||||||
|
|
||||||
# Set operation code if provided
|
# Parse coordinates
|
||||||
if d is not None:
|
if match.group(2) is not None:
|
||||||
current_operation_code = int(d)
|
current_x = parse_gerber_number(match.group(2), self.frac_digits)
|
||||||
|
if match.group(3) is not None:
|
||||||
|
current_y = parse_gerber_number(match.group(3), self.frac_digits)
|
||||||
|
|
||||||
# Nothing created! Pen Up.
|
# Parse operation code
|
||||||
if current_operation_code == 2:
|
if match.group(4) is not None:
|
||||||
log.warning("Arc with D2. (%d)" % line_num)
|
current_operation_code = int(match.group(4))
|
||||||
if len(path) > 1:
|
|
||||||
if last_path_aperture is None:
|
# Pen down: add segment
|
||||||
log.warning("No aperture defined for curent path. (%d)" % line_num)
|
if current_operation_code == 1:
|
||||||
|
path.append([current_x, current_y])
|
||||||
|
last_path_aperture = current_aperture
|
||||||
|
|
||||||
|
elif current_operation_code == 2:
|
||||||
|
if len(path) > 1:
|
||||||
|
|
||||||
|
## --- BUFFERED ---
|
||||||
|
if making_region:
|
||||||
|
geo = Polygon(path)
|
||||||
|
else:
|
||||||
|
if last_path_aperture is None:
|
||||||
|
log.warning("No aperture defined for curent path. (%d)" % line_num)
|
||||||
|
width = self.apertures[last_path_aperture]["size"]
|
||||||
|
geo = LineString(path).buffer(width/2)
|
||||||
|
poly_buffer.append(geo)
|
||||||
|
|
||||||
|
path = [[current_x, current_y]] # Start new path
|
||||||
|
|
||||||
|
# Flash
|
||||||
|
elif current_operation_code == 3:
|
||||||
|
|
||||||
# --- BUFFERED ---
|
# --- BUFFERED ---
|
||||||
width = self.apertures[last_path_aperture]["size"]
|
flash = Gerber.create_flash_geometry(Point([current_x, current_y]),
|
||||||
buffered = LineString(path).buffer(width/2)
|
self.apertures[current_aperture])
|
||||||
poly_buffer.append(buffered)
|
poly_buffer.append(flash)
|
||||||
|
|
||||||
|
continue
|
||||||
|
|
||||||
|
### G02/3 - Circular interpolation
|
||||||
|
# 2-clockwise, 3-counterclockwise
|
||||||
|
match = self.circ_re.search(gline)
|
||||||
|
if match:
|
||||||
|
|
||||||
|
mode, x, y, i, j, d = match.groups()
|
||||||
|
try:
|
||||||
|
x = parse_gerber_number(x, self.frac_digits)
|
||||||
|
except:
|
||||||
|
x = current_x
|
||||||
|
try:
|
||||||
|
y = parse_gerber_number(y, self.frac_digits)
|
||||||
|
except:
|
||||||
|
y = current_y
|
||||||
|
try:
|
||||||
|
i = parse_gerber_number(i, self.frac_digits)
|
||||||
|
except:
|
||||||
|
i = 0
|
||||||
|
try:
|
||||||
|
j = parse_gerber_number(j, self.frac_digits)
|
||||||
|
except:
|
||||||
|
j = 0
|
||||||
|
|
||||||
|
if quadrant_mode is None:
|
||||||
|
log.error("Found arc without preceding quadrant specification G74 or G75. (%d)" % line_num)
|
||||||
|
log.error(gline)
|
||||||
|
continue
|
||||||
|
|
||||||
|
if mode is None and current_interpolation_mode not in [2, 3]:
|
||||||
|
log.error("Found arc without circular interpolation mode defined. (%d)" % line_num)
|
||||||
|
log.error(gline)
|
||||||
|
continue
|
||||||
|
elif mode is not None:
|
||||||
|
current_interpolation_mode = int(mode)
|
||||||
|
|
||||||
|
# Set operation code if provided
|
||||||
|
if d is not None:
|
||||||
|
current_operation_code = int(d)
|
||||||
|
|
||||||
|
# Nothing created! Pen Up.
|
||||||
|
if current_operation_code == 2:
|
||||||
|
log.warning("Arc with D2. (%d)" % line_num)
|
||||||
|
if len(path) > 1:
|
||||||
|
if last_path_aperture is None:
|
||||||
|
log.warning("No aperture defined for curent path. (%d)" % line_num)
|
||||||
|
|
||||||
|
# --- BUFFERED ---
|
||||||
|
width = self.apertures[last_path_aperture]["size"]
|
||||||
|
buffered = LineString(path).buffer(width/2)
|
||||||
|
poly_buffer.append(buffered)
|
||||||
|
|
||||||
|
current_x = x
|
||||||
|
current_y = y
|
||||||
|
path = [[current_x, current_y]] # Start new path
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Flash should not happen here
|
||||||
|
if current_operation_code == 3:
|
||||||
|
log.error("Trying to flash within arc. (%d)" % line_num)
|
||||||
|
continue
|
||||||
|
|
||||||
|
if quadrant_mode == 'MULTI':
|
||||||
|
center = [i + current_x, j + current_y]
|
||||||
|
radius = sqrt(i**2 + j**2)
|
||||||
|
start = arctan2(-j, -i)
|
||||||
|
stop = arctan2(-center[1] + y, -center[0] + x)
|
||||||
|
arcdir = [None, None, "cw", "ccw"]
|
||||||
|
this_arc = arc(center, radius, start, stop,
|
||||||
|
arcdir[current_interpolation_mode],
|
||||||
|
self.steps_per_circ)
|
||||||
|
|
||||||
|
# Last point in path is current point
|
||||||
|
current_x = this_arc[-1][0]
|
||||||
|
current_y = this_arc[-1][1]
|
||||||
|
|
||||||
|
# Append
|
||||||
|
path += this_arc
|
||||||
|
|
||||||
|
last_path_aperture = current_aperture
|
||||||
|
|
||||||
|
continue
|
||||||
|
|
||||||
|
if quadrant_mode == 'SINGLE':
|
||||||
|
log.warning("Single quadrant arc are not implemented yet. (%d)" % line_num)
|
||||||
|
|
||||||
|
### Operation code alone
|
||||||
|
match = self.opcode_re.search(gline)
|
||||||
|
if match:
|
||||||
|
current_operation_code = int(match.group(1))
|
||||||
|
if current_operation_code == 3:
|
||||||
|
|
||||||
|
## --- Buffered ---
|
||||||
|
flash = Gerber.create_flash_geometry(Point(path[-1]),
|
||||||
|
self.apertures[current_aperture])
|
||||||
|
poly_buffer.append(flash)
|
||||||
|
|
||||||
|
continue
|
||||||
|
|
||||||
|
### G74/75* - Single or multiple quadrant arcs
|
||||||
|
match = self.quad_re.search(gline)
|
||||||
|
if match:
|
||||||
|
if match.group(1) == '4':
|
||||||
|
quadrant_mode = 'SINGLE'
|
||||||
|
else:
|
||||||
|
quadrant_mode = 'MULTI'
|
||||||
|
continue
|
||||||
|
|
||||||
|
### G36* - Begin region
|
||||||
|
if self.regionon_re.search(gline):
|
||||||
|
if len(path) > 1:
|
||||||
|
# Take care of what is left in the path
|
||||||
|
|
||||||
|
## --- Buffered ---
|
||||||
|
width = self.apertures[last_path_aperture]["size"]
|
||||||
|
geo = LineString(path).buffer(width/2)
|
||||||
|
poly_buffer.append(geo)
|
||||||
|
|
||||||
|
path = [path[-1]]
|
||||||
|
|
||||||
|
making_region = True
|
||||||
|
continue
|
||||||
|
|
||||||
|
### G37* - End region
|
||||||
|
if self.regionoff_re.search(gline):
|
||||||
|
making_region = False
|
||||||
|
|
||||||
|
# Only one path defines region?
|
||||||
|
# This can happen if D02 happened before G37 and
|
||||||
|
# is not and error.
|
||||||
|
if len(path) < 3:
|
||||||
|
# print "ERROR: Path contains less than 3 points:"
|
||||||
|
# print path
|
||||||
|
# print "Line (%d): " % line_num, gline
|
||||||
|
# path = []
|
||||||
|
#path = [[current_x, current_y]]
|
||||||
|
continue
|
||||||
|
|
||||||
|
# For regions we may ignore an aperture that is None
|
||||||
|
# self.regions.append({"polygon": Polygon(path),
|
||||||
|
# "aperture": last_path_aperture})
|
||||||
|
|
||||||
|
# --- Buffered ---
|
||||||
|
region = Polygon(path)
|
||||||
|
if not region.is_valid:
|
||||||
|
region = region.buffer(0)
|
||||||
|
poly_buffer.append(region)
|
||||||
|
|
||||||
current_x = x
|
|
||||||
current_y = y
|
|
||||||
path = [[current_x, current_y]] # Start new path
|
path = [[current_x, current_y]] # Start new path
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Flash should not happen here
|
### Aperture definitions %ADD...
|
||||||
if current_operation_code == 3:
|
match = self.ad_re.search(gline)
|
||||||
log.error("Trying to flash within arc. (%d)" % line_num)
|
if match:
|
||||||
|
log.info("Found aperture definition. Line %d: %s" % (line_num, gline))
|
||||||
|
self.aperture_parse(match.group(1), match.group(2), match.group(3))
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if quadrant_mode == 'MULTI':
|
### G01/2/3* - Interpolation mode change
|
||||||
center = [i + current_x, j + current_y]
|
# Can occur along with coordinates and operation code but
|
||||||
radius = sqrt(i**2 + j**2)
|
# sometimes by itself (handled here).
|
||||||
start = arctan2(-j, -i)
|
# Example: G01*
|
||||||
stop = arctan2(-center[1] + y, -center[0] + x)
|
match = self.interp_re.search(gline)
|
||||||
arcdir = [None, None, "cw", "ccw"]
|
if match:
|
||||||
this_arc = arc(center, radius, start, stop,
|
current_interpolation_mode = int(match.group(1))
|
||||||
arcdir[current_interpolation_mode],
|
|
||||||
self.steps_per_circ)
|
|
||||||
|
|
||||||
# Last point in path is current point
|
|
||||||
current_x = this_arc[-1][0]
|
|
||||||
current_y = this_arc[-1][1]
|
|
||||||
|
|
||||||
# Append
|
|
||||||
path += this_arc
|
|
||||||
|
|
||||||
last_path_aperture = current_aperture
|
|
||||||
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if quadrant_mode == 'SINGLE':
|
### Tool/aperture change
|
||||||
log.warning("Single quadrant arc are not implemented yet. (%d)" % line_num)
|
# Example: D12*
|
||||||
|
match = self.tool_re.search(gline)
|
||||||
### Operation code alone
|
if match:
|
||||||
match = self.opcode_re.search(gline)
|
current_aperture = match.group(1)
|
||||||
if match:
|
|
||||||
current_operation_code = int(match.group(1))
|
|
||||||
if current_operation_code == 3:
|
|
||||||
|
|
||||||
## --- Buffered ---
|
|
||||||
flash = Gerber.create_flash_geometry(Point(path[-1]),
|
|
||||||
self.apertures[current_aperture])
|
|
||||||
poly_buffer.append(flash)
|
|
||||||
|
|
||||||
continue
|
|
||||||
|
|
||||||
### G74/75* - Single or multiple quadrant arcs
|
|
||||||
match = self.quad_re.search(gline)
|
|
||||||
if match:
|
|
||||||
if match.group(1) == '4':
|
|
||||||
quadrant_mode = 'SINGLE'
|
|
||||||
else:
|
|
||||||
quadrant_mode = 'MULTI'
|
|
||||||
continue
|
|
||||||
|
|
||||||
### G36* - Begin region
|
|
||||||
if self.regionon_re.search(gline):
|
|
||||||
if len(path) > 1:
|
|
||||||
# Take care of what is left in the path
|
|
||||||
|
|
||||||
## --- Buffered ---
|
|
||||||
width = self.apertures[last_path_aperture]["size"]
|
|
||||||
geo = LineString(path).buffer(width/2)
|
|
||||||
poly_buffer.append(geo)
|
|
||||||
|
|
||||||
path = [path[-1]]
|
|
||||||
|
|
||||||
making_region = True
|
|
||||||
continue
|
|
||||||
|
|
||||||
### G37* - End region
|
|
||||||
if self.regionoff_re.search(gline):
|
|
||||||
making_region = False
|
|
||||||
|
|
||||||
# Only one path defines region?
|
|
||||||
# This can happen if D02 happened before G37 and
|
|
||||||
# is not and error.
|
|
||||||
if len(path) < 3:
|
|
||||||
# print "ERROR: Path contains less than 3 points:"
|
|
||||||
# print path
|
|
||||||
# print "Line (%d): " % line_num, gline
|
|
||||||
# path = []
|
|
||||||
#path = [[current_x, current_y]]
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# For regions we may ignore an aperture that is None
|
### Polarity change
|
||||||
# self.regions.append({"polygon": Polygon(path),
|
# Example: %LPD*% or %LPC*%
|
||||||
# "aperture": last_path_aperture})
|
match = self.lpol_re.search(gline)
|
||||||
|
if match:
|
||||||
|
if len(path) > 1 and current_polarity != match.group(1):
|
||||||
|
|
||||||
# --- Buffered ---
|
# --- Buffered ----
|
||||||
region = Polygon(path)
|
width = self.apertures[last_path_aperture]["size"]
|
||||||
if not region.is_valid:
|
geo = LineString(path).buffer(width/2)
|
||||||
region = region.buffer(0)
|
poly_buffer.append(geo)
|
||||||
poly_buffer.append(region)
|
|
||||||
|
|
||||||
path = [[current_x, current_y]] # Start new path
|
path = [path[-1]]
|
||||||
continue
|
|
||||||
|
|
||||||
### Aperture definitions %ADD...
|
|
||||||
match = self.ad_re.search(gline)
|
|
||||||
if match:
|
|
||||||
self.aperture_parse(match.group(1), match.group(2), match.group(3))
|
|
||||||
continue
|
|
||||||
|
|
||||||
### G01/2/3* - Interpolation mode change
|
# --- Apply buffer ---
|
||||||
# Can occur along with coordinates and operation code but
|
if current_polarity == 'D':
|
||||||
# sometimes by itself (handled here).
|
self.solid_geometry = self.solid_geometry.union(cascaded_union(poly_buffer))
|
||||||
# Example: G01*
|
else:
|
||||||
match = self.interp_re.search(gline)
|
self.solid_geometry = self.solid_geometry.difference(cascaded_union(poly_buffer))
|
||||||
if match:
|
poly_buffer = []
|
||||||
current_interpolation_mode = int(match.group(1))
|
|
||||||
continue
|
|
||||||
|
|
||||||
### Tool/aperture change
|
current_polarity = match.group(1)
|
||||||
# Example: D12*
|
continue
|
||||||
match = self.tool_re.search(gline)
|
|
||||||
if match:
|
|
||||||
current_aperture = match.group(1)
|
|
||||||
continue
|
|
||||||
|
|
||||||
### Polarity change
|
### Number format
|
||||||
# Example: %LPD*% or %LPC*%
|
# Example: %FSLAX24Y24*%
|
||||||
match = self.lpol_re.search(gline)
|
# TODO: This is ignoring most of the format. Implement the rest.
|
||||||
if match:
|
match = self.fmt_re.search(gline)
|
||||||
if len(path) > 1 and current_polarity != match.group(1):
|
if match:
|
||||||
|
absolute = {'A': True, 'I': False}
|
||||||
|
self.int_digits = int(match.group(3))
|
||||||
|
self.frac_digits = int(match.group(4))
|
||||||
|
continue
|
||||||
|
|
||||||
# --- Buffered ----
|
### Mode (IN/MM)
|
||||||
width = self.apertures[last_path_aperture]["size"]
|
# Example: %MOIN*%
|
||||||
geo = LineString(path).buffer(width/2)
|
match = self.mode_re.search(gline)
|
||||||
poly_buffer.append(geo)
|
if match:
|
||||||
|
self.units = match.group(1)
|
||||||
|
continue
|
||||||
|
|
||||||
path = [path[-1]]
|
### Units (G70/1) OBSOLETE
|
||||||
|
match = self.units_re.search(gline)
|
||||||
|
if match:
|
||||||
|
self.units = {'0': 'IN', '1': 'MM'}[match.group(1)]
|
||||||
|
continue
|
||||||
|
|
||||||
# --- Apply buffer ---
|
### Absolute/relative coordinates G90/1 OBSOLETE
|
||||||
if current_polarity == 'D':
|
match = self.absrel_re.search(gline)
|
||||||
self.solid_geometry = self.solid_geometry.union(cascaded_union(poly_buffer))
|
if match:
|
||||||
else:
|
absolute = {'0': True, '1': False}[match.group(1)]
|
||||||
self.solid_geometry = self.solid_geometry.difference(cascaded_union(poly_buffer))
|
continue
|
||||||
poly_buffer = []
|
|
||||||
|
|
||||||
current_polarity = match.group(1)
|
#### Ignored lines
|
||||||
continue
|
## Comments
|
||||||
|
match = self.comm_re.search(gline)
|
||||||
|
if match:
|
||||||
|
continue
|
||||||
|
|
||||||
### Number format
|
## EOF
|
||||||
# Example: %FSLAX24Y24*%
|
match = self.eof_re.search(gline)
|
||||||
# TODO: This is ignoring most of the format. Implement the rest.
|
if match:
|
||||||
match = self.fmt_re.search(gline)
|
continue
|
||||||
if match:
|
|
||||||
absolute = {'A': True, 'I': False}
|
|
||||||
self.int_digits = int(match.group(3))
|
|
||||||
self.frac_digits = int(match.group(4))
|
|
||||||
continue
|
|
||||||
|
|
||||||
### Mode (IN/MM)
|
### Line did not match any pattern. Warn user.
|
||||||
# Example: %MOIN*%
|
log.warning("Line ignored (%d): %s" % (line_num, gline))
|
||||||
match = self.mode_re.search(gline)
|
|
||||||
if match:
|
|
||||||
self.units = match.group(1)
|
|
||||||
continue
|
|
||||||
|
|
||||||
### Units (G70/1) OBSOLETE
|
if len(path) > 1:
|
||||||
match = self.units_re.search(gline)
|
# EOF, create shapely LineString if something still in path
|
||||||
if match:
|
|
||||||
self.units = {'0': 'IN', '1': 'MM'}[match.group(1)]
|
|
||||||
continue
|
|
||||||
|
|
||||||
### Absolute/relative coordinates G90/1 OBSOLETE
|
## --- Buffered ---
|
||||||
match = self.absrel_re.search(gline)
|
width = self.apertures[last_path_aperture]["size"]
|
||||||
if match:
|
geo = LineString(path).buffer(width/2)
|
||||||
absolute = {'0': True, '1': False}[match.group(1)]
|
poly_buffer.append(geo)
|
||||||
continue
|
|
||||||
|
|
||||||
#### Ignored lines
|
# --- Apply buffer ---
|
||||||
## Comments
|
if current_polarity == 'D':
|
||||||
match = self.comm_re.search(gline)
|
self.solid_geometry = self.solid_geometry.union(cascaded_union(poly_buffer))
|
||||||
if match:
|
else:
|
||||||
continue
|
self.solid_geometry = self.solid_geometry.difference(cascaded_union(poly_buffer))
|
||||||
|
|
||||||
## EOF
|
except Exception, err:
|
||||||
match = self.eof_re.search(gline)
|
#print traceback.format_exc()
|
||||||
if match:
|
log.error("PARSING FAILED. Line %d: %s" % (line_num, gline))
|
||||||
continue
|
raise
|
||||||
|
|
||||||
### Line did not match any pattern. Warn user.
|
|
||||||
log.warning("Line ignored (%d): %s" % (line_num, gline))
|
|
||||||
|
|
||||||
if len(path) > 1:
|
|
||||||
# EOF, create shapely LineString if something still in path
|
|
||||||
|
|
||||||
## --- Buffered ---
|
|
||||||
width = self.apertures[last_path_aperture]["size"]
|
|
||||||
geo = LineString(path).buffer(width/2)
|
|
||||||
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
|
@staticmethod
|
||||||
def create_flash_geometry(location, aperture):
|
def create_flash_geometry(location, aperture):
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
[{"kind": "excellon", "filename": "C:/Users/jpcaram/Dropbox/CNC/pcbcam/test_files/FlatCam_Drilling_Test/FlatCam_Drilling_Test.drl"}, {"kind": "excellon", "filename": "C:/Users/jpcaram/Dropbox/CNC/pcbcam/test_files/Excellon_Planck/X-Y CONTROLLER - Drill Data - Through Hole.drl"}, {"kind": "gerber", "filename": "C:/Users/jpcaram/Dropbox/CNC/pcbcam/test_files/7V-PSU/7V PSU.GTL"}, {"kind": "cncjob", "filename": "C:/Users/jpcaram/Dropbox/CNC/pcbcam/test_files/output.gcode"}, {"kind": "project", "filename": "C:/Users/jpcaram/Dropbox/CNC/pcbcam/test_files/project_copy.fcproj"}, {"kind": "project", "filename": "C:/Users/jpcaram/Dropbox/CNC/pcbcam/test_files/a_project.fcproj"}, {"kind": "project", "filename": "C:/Users/jpcaram/Dropbox/CNC/pcbcam/cirkuix/tests/FlatCAM_TestProject.fcproj"}, {"kind": "cncjob", "filename": "C:/Users/jpcaram/Dropbox/CNC/pcbcam/cirkuix/tests/FlatCAM_TestGCode.gcode"}, {"kind": "cncjob", "filename": "C:/Users/jpcaram/Dropbox/CNC/pcbcam/cirkuix/tests/CBS-B_Cu_ISOLATION_GCODE.ngc"}, {"kind": "excellon", "filename": "C:/Users/jpcaram/Dropbox/CNC/pcbcam/cirkuix/tests/FlatCAM_TestDrill.drl"}]
|
[{"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.gbr"}, {"kind": "gerber", "filename": "C:/Users/jpcaram/Dropbox/CNC/pcbcam/test_files/7V-PSU.zip"}, {"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/XGerber/do-kotle.Bot"}, {"kind": "excellon", "filename": "C:/Users/jpcaram/Dropbox/CNC/pcbcam/test_files/FlatCam_Drilling_Test/FlatCam_Drilling_Test.drl"}, {"kind": "excellon", "filename": "C:/Users/jpcaram/Dropbox/CNC/pcbcam/test_files/Excellon_Planck/X-Y CONTROLLER - Drill Data - Through Hole.drl"}, {"kind": "gerber", "filename": "C:/Users/jpcaram/Dropbox/CNC/pcbcam/test_files/7V-PSU/7V PSU.GTL"}, {"kind": "cncjob", "filename": "C:/Users/jpcaram/Dropbox/CNC/pcbcam/test_files/output.gcode"}, {"kind": "project", "filename": "C:/Users/jpcaram/Dropbox/CNC/pcbcam/test_files/project_copy.fcproj"}]
|
||||||
Reference in New Issue
Block a user