- moved back the ApertureMacro class to camlib for now and made some import changes in the new ParseGerber and ParseExcellon classes

- some changes to the tests - perhaps I will try adding a few tests in the future
This commit is contained in:
Marius Stanciu 2019-10-06 15:56:41 +03:00 committed by Marius
parent 2c536258ed
commit a6b89dbf3a
22 changed files with 646 additions and 562 deletions

View File

@ -52,7 +52,7 @@ from flatcamEditors.FlatCAMTextEditor import TextEditor
from FlatCAMProcess import * from FlatCAMProcess import *
from FlatCAMWorkerStack import WorkerStack from FlatCAMWorkerStack import WorkerStack
from flatcamGUI.VisPyVisuals import Color # from flatcamGUI.VisPyVisuals import Color
from vispy.gloo.util import _screenshot from vispy.gloo.util import _screenshot
from vispy.io import write_png from vispy.io import write_png
@ -71,12 +71,12 @@ fcTranslate.apply_language('strings')
if '_' not in builtins.__dict__: if '_' not in builtins.__dict__:
_ = gettext.gettext _ = gettext.gettext
# ########################################
# # App ###
# ########################################
class App(QtCore.QObject): class App(QtCore.QObject):
# ########################################
# # App ###
# ########################################
""" """
The main application class. The constructor starts the GUI. The main application class. The constructor starts the GUI.
""" """

View File

@ -17,6 +17,8 @@ CAD program, and create G-Code for Isolation routing.
- made the Rules Check Tool document window Read Only - made the Rules Check Tool document window Read Only
- made Excellon and Gerber classes from camlib into their own files in the flatcamParser folder - made Excellon and Gerber classes from camlib into their own files in the flatcamParser folder
- moved the ApertureMacro class from camlib to ParseGerber file - moved the ApertureMacro class from camlib to ParseGerber file
- moved back the ApertureMacro class to camlib for now and made some import changes in the new ParseGerber and ParseExcellon classes
- some changes to the tests - perhaps I will try adding a few tests in the future
5.10.2019 5.10.2019

375
camlib.py
View File

@ -49,7 +49,7 @@ import ezdxf
# TODO: Commented for FlatCAM packaging with cx_freeze # TODO: Commented for FlatCAM packaging with cx_freeze
# from scipy.spatial import KDTree, Delaunay # from scipy.spatial import KDTree, Delaunay
# from scipy.spatial import Delaunay # from scipy.spatial import Delaunay
from flatcamParsers.ParseGerber import ApertureMacro
from flatcamParsers.ParseSVG import * from flatcamParsers.ParseSVG import *
from flatcamParsers.ParseDXF import * from flatcamParsers.ParseDXF import *
@ -82,6 +82,379 @@ class ParseError(Exception):
pass pass
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
am1_re = re.compile(r'^%AM([^\*]+)\*(.+)?(%)?$')
am2_re = re.compile(r'(.*)%$')
amcomm_re = re.compile(r'^0(.*)')
amprim_re = re.compile(r'^[1-9].*')
amvar_re = re.compile(r'^\$([0-9a-zA-z]+)=(.*)')
def __init__(self, name=None):
self.name = name
self.raw = ""
# ## These below are recomputed for every aperture
# ## definition, in other words, are temporary variables.
self.primitives = []
self.locvars = {}
self.geometry = None
def to_dict(self):
"""
Returns the object in a serializable form. Only the name and
raw are required.
:return: Dictionary representing the object. JSON ready.
:rtype: dict
"""
return {
'name': self.name,
'raw': self.raw
}
def from_dict(self, d):
"""
Populates the object from a serial representation created
with ``self.to_dict()``.
:param d: Serial representation of an ApertureMacro object.
:return: None
"""
for attr in ['name', 'raw']:
setattr(self, attr, d[attr])
def parse_content(self):
"""
Creates numerical lists for all primitives in the aperture
macro (in ``self.raw``) by replacing all variables by their
values iteratively and evaluating expressions. Results
are stored in ``self.primitives``.
:return: None
"""
# Cleanup
self.raw = self.raw.replace('\n', '').replace('\r', '').strip(" *")
self.primitives = []
# Separate parts
parts = self.raw.split('*')
# ### Every part in the macro ####
for part in parts:
# ## Comments. Ignored.
match = ApertureMacro.amcomm_re.search(part)
if match:
continue
# ## Variables
# These are variables defined locally inside the macro. They can be
# numerical constant or defind in terms of previously define
# variables, which can be defined locally or in an aperture
# definition. All replacements ocurr here.
match = ApertureMacro.amvar_re.search(part)
if match:
var = match.group(1)
val = match.group(2)
# Replace variables in value
for v in self.locvars:
# replaced the following line with the next to fix Mentor custom apertures not parsed OK
# val = re.sub((r'\$'+str(v)+r'(?![0-9a-zA-Z])'), str(self.locvars[v]), val)
val = val.replace('$' + str(v), str(self.locvars[v]))
# Make all others 0
val = re.sub(r'\$[0-9a-zA-Z](?![0-9a-zA-Z])', "0", val)
# Change x with *
val = re.sub(r'[xX]', "*", val)
# Eval() and store.
self.locvars[var] = eval(val)
continue
# ## Primitives
# Each is an array. The first identifies the primitive, while the
# rest depend on the primitive. All are strings representing a
# number and may contain variable definition. The values of these
# variables are defined in an aperture definition.
match = ApertureMacro.amprim_re.search(part)
if match:
# ## Replace all variables
for v in self.locvars:
# replaced the following line with the next to fix Mentor custom apertures not parsed OK
# part = re.sub(r'\$' + str(v) + r'(?![0-9a-zA-Z])', str(self.locvars[v]), part)
part = part.replace('$' + str(v), str(self.locvars[v]))
# Make all others 0
part = re.sub(r'\$[0-9a-zA-Z](?![0-9a-zA-Z])', "0", part)
# Change x with *
part = re.sub(r'[xX]', "*", part)
# ## Store
elements = part.split(",")
self.primitives.append([eval(x) for x in elements])
continue
log.warning("Unknown syntax of aperture macro part: %s" % str(part))
def append(self, data):
"""
Appends a string to the raw macro.
:param data: Part of the macro.
:type data: str
:return: None
"""
self.raw += data
@staticmethod
def default2zero(n, mods):
"""
Pads the ``mods`` list with zeros resulting in an
list of length n.
:param n: Length of the resulting list.
:type n: int
:param mods: List to be padded.
:type mods: list
:return: Zero-padded list.
:rtype: list
"""
x = [0.0] * n
na = len(mods)
x[0:na] = mods
return x
@staticmethod
def make_circle(mods):
"""
:param mods: (Exposure 0/1, Diameter >=0, X-coord, Y-coord)
:return:
"""
pol, dia, x, y = ApertureMacro.default2zero(4, mods)
return {"pol": int(pol), "geometry": Point(x, y).buffer(dia/2)}
@staticmethod
def make_vectorline(mods):
"""
:param mods: (Exposure 0/1, Line width >= 0, X-start, Y-start, X-end, Y-end,
rotation angle around origin in degrees)
:return:
"""
pol, width, xs, ys, xe, ye, angle = ApertureMacro.default2zero(7, mods)
line = LineString([(xs, ys), (xe, ye)])
box = line.buffer(width/2, cap_style=2)
box_rotated = affinity.rotate(box, angle, origin=(0, 0))
return {"pol": int(pol), "geometry": box_rotated}
@staticmethod
def make_centerline(mods):
"""
:param mods: (Exposure 0/1, width >=0, height >=0, x-center, y-center,
rotation angle around origin in degrees)
:return:
"""
pol, width, height, x, y, angle = ApertureMacro.default2zero(6, mods)
box = shply_box(x-width/2, y-height/2, x+width/2, y+height/2)
box_rotated = affinity.rotate(box, angle, origin=(0, 0))
return {"pol": int(pol), "geometry": box_rotated}
@staticmethod
def make_lowerleftline(mods):
"""
:param mods: (exposure 0/1, width >=0, height >=0, x-lowerleft, y-lowerleft,
rotation angle around origin in degrees)
:return:
"""
pol, width, height, x, y, angle = ApertureMacro.default2zero(6, mods)
box = shply_box(x, y, x+width, y+height)
box_rotated = affinity.rotate(box, angle, origin=(0, 0))
return {"pol": int(pol), "geometry": box_rotated}
@staticmethod
def make_outline(mods):
"""
:param mods:
:return:
"""
pol = mods[0]
n = mods[1]
points = [(0, 0)]*(n+1)
for i in range(n+1):
points[i] = mods[2*i + 2:2*i + 4]
angle = mods[2*n + 4]
poly = Polygon(points)
poly_rotated = affinity.rotate(poly, angle, origin=(0, 0))
return {"pol": int(pol), "geometry": poly_rotated}
@staticmethod
def make_polygon(mods):
"""
Note: Specs indicate that rotation is only allowed if the center
(x, y) == (0, 0). I will tolerate breaking this rule.
:param mods: (exposure 0/1, n_verts 3<=n<=12, x-center, y-center,
diameter of circumscribed circle >=0, rotation angle around origin)
:return:
"""
pol, nverts, x, y, dia, angle = ApertureMacro.default2zero(6, mods)
points = [(0, 0)]*nverts
for i in range(nverts):
points[i] = (x + 0.5 * dia * cos(2*pi * i/nverts),
y + 0.5 * dia * sin(2*pi * i/nverts))
poly = Polygon(points)
poly_rotated = affinity.rotate(poly, angle, origin=(0, 0))
return {"pol": int(pol), "geometry": poly_rotated}
@staticmethod
def make_moire(mods):
"""
Note: Specs indicate that rotation is only allowed if the center
(x, y) == (0, 0). I will tolerate breaking this rule.
:param mods: (x-center, y-center, outer_dia_outer_ring, ring thickness,
gap, max_rings, crosshair_thickness, crosshair_len, rotation
angle around origin in degrees)
:return:
"""
x, y, dia, thickness, gap, nrings, cross_th, cross_len, angle = ApertureMacro.default2zero(9, mods)
r = dia/2 - thickness/2
result = Point((x, y)).buffer(r).exterior.buffer(thickness/2.0)
ring = Point((x, y)).buffer(r).exterior.buffer(thickness/2.0) # Need a copy!
i = 1 # Number of rings created so far
# ## If the ring does not have an interior it means that it is
# ## a disk. Then stop.
while len(ring.interiors) > 0 and i < nrings:
r -= thickness + gap
if r <= 0:
break
ring = Point((x, y)).buffer(r).exterior.buffer(thickness/2.0)
result = cascaded_union([result, ring])
i += 1
# ## Crosshair
hor = LineString([(x - cross_len, y), (x + cross_len, y)]).buffer(cross_th/2.0, cap_style=2)
ver = LineString([(x, y-cross_len), (x, y + cross_len)]).buffer(cross_th/2.0, cap_style=2)
result = cascaded_union([result, hor, ver])
return {"pol": 1, "geometry": result}
@staticmethod
def make_thermal(mods):
"""
Note: Specs indicate that rotation is only allowed if the center
(x, y) == (0, 0). I will tolerate breaking this rule.
:param mods: [x-center, y-center, diameter-outside, diameter-inside,
gap-thickness, rotation angle around origin]
:return:
"""
x, y, dout, din, t, angle = ApertureMacro.default2zero(6, mods)
ring = Point((x, y)).buffer(dout/2.0).difference(Point((x, y)).buffer(din/2.0))
hline = LineString([(x - dout/2.0, y), (x + dout/2.0, y)]).buffer(t/2.0, cap_style=3)
vline = LineString([(x, y - dout/2.0), (x, y + dout/2.0)]).buffer(t/2.0, cap_style=3)
thermal = ring.difference(hline.union(vline))
return {"pol": 1, "geometry": thermal}
def make_geometry(self, modifiers):
"""
Runs the macro for the given modifiers and generates
the corresponding geometry.
:param modifiers: Modifiers (parameters) for this macro
:type modifiers: list
:return: Shapely geometry
:rtype: shapely.geometry.polygon
"""
# ## Primitive makers
makers = {
"1": ApertureMacro.make_circle,
"2": ApertureMacro.make_vectorline,
"20": ApertureMacro.make_vectorline,
"21": ApertureMacro.make_centerline,
"22": ApertureMacro.make_lowerleftline,
"4": ApertureMacro.make_outline,
"5": ApertureMacro.make_polygon,
"6": ApertureMacro.make_moire,
"7": ApertureMacro.make_thermal
}
# ## Store modifiers as local variables
modifiers = modifiers or []
modifiers = [float(m) for m in modifiers]
self.locvars = {}
for i in range(0, len(modifiers)):
self.locvars[str(i + 1)] = modifiers[i]
# ## Parse
self.primitives = [] # Cleanup
self.geometry = Polygon()
self.parse_content()
# ## Make the geometry
for primitive in self.primitives:
# Make the primitive
prim_geo = makers[str(int(primitive[0]))](primitive[1:])
# Add it (according to polarity)
# if self.geometry is None and prim_geo['pol'] == 1:
# self.geometry = prim_geo['geometry']
# continue
if prim_geo['pol'] == 1:
self.geometry = self.geometry.union(prim_geo['geometry'])
continue
if prim_geo['pol'] == 0:
self.geometry = self.geometry.difference(prim_geo['geometry'])
continue
return self.geometry
class Geometry(object): class Geometry(object):
""" """
Base geometry class. Base geometry class.

View File

@ -1,8 +1,32 @@
from camlib import * from camlib import Geometry
import FlatCAMApp
import FlatCAMTranslation as fcTranslate
from shapely.geometry import Polygon, Point, LineString, MultiPolygon
from shapely.ops import cascaded_union
import shapely.affinity as affinity
import re
import traceback
import gettext
import builtins
import numpy as np
from numpy import Inf
import logging
if '_' not in builtins.__dict__: if '_' not in builtins.__dict__:
_ = gettext.gettext _ = gettext.gettext
log = logging.getLogger('base2')
log.setLevel(logging.DEBUG)
formatter = logging.Formatter('[%(levelname)s] %(message)s')
handler = logging.StreamHandler()
handler.setFormatter(formatter)
log.addHandler(handler)
class Excellon(Geometry): class Excellon(Geometry):
""" """
@ -265,7 +289,7 @@ class Excellon(Geometry):
line_units = '' line_units = ''
#### Parsing starts here ## ## # ## Parsing starts here ## ##
line_num = 0 # Line number line_num = 0 # Line number
eline = "" eline = ""
try: try:

View File

@ -1,9 +1,35 @@
from camlib import Geometry, ApertureMacro, parse_gerber_number, arc, arctan2, arc_angle
import FlatCAMApp
import FlatCAMTranslation as fcTranslate
from camlib import * from shapely.geometry import Polygon, Point, LineString, MultiPolygon
from shapely.ops import cascaded_union
import shapely.affinity as affinity
from shapely.geometry import box as shply_box
import re
import traceback
from copy import deepcopy
import gettext
import builtins
import numpy as np
from numpy import Inf
from math import sqrt, pi, sin, cos
import sys
import logging
if '_' not in builtins.__dict__: if '_' not in builtins.__dict__:
_ = gettext.gettext _ = gettext.gettext
log = logging.getLogger('base2')
log.setLevel(logging.DEBUG)
formatter = logging.Formatter('[%(levelname)s] %(message)s')
handler = logging.StreamHandler()
handler.setFormatter(formatter)
log.addHandler(handler)
class Gerber(Geometry): class Gerber(Geometry):
""" """
@ -1134,25 +1160,25 @@ class Gerber(Geometry):
try: try:
circular_x = parse_gerber_number(circular_x, circular_x = parse_gerber_number(circular_x,
self.int_digits, self.frac_digits, self.gerber_zeros) self.int_digits, self.frac_digits, self.gerber_zeros)
except: except Exception as e:
circular_x = current_x circular_x = current_x
try: try:
circular_y = parse_gerber_number(circular_y, circular_y = parse_gerber_number(circular_y,
self.int_digits, self.frac_digits, self.gerber_zeros) self.int_digits, self.frac_digits, self.gerber_zeros)
except: except Exception as e:
circular_y = current_y circular_y = current_y
# According to Gerber specification i and j are not modal, which means that when i or j are missing, # According to Gerber specification i and j are not modal, which means that when i or j are missing,
# they are to be interpreted as being zero # they are to be interpreted as being zero
try: try:
i = parse_gerber_number(i, self.int_digits, self.frac_digits, self.gerber_zeros) i = parse_gerber_number(i, self.int_digits, self.frac_digits, self.gerber_zeros)
except: except Exception as e:
i = 0 i = 0
try: try:
j = parse_gerber_number(j, self.int_digits, self.frac_digits, self.gerber_zeros) j = parse_gerber_number(j, self.int_digits, self.frac_digits, self.gerber_zeros)
except: except Exception as e:
j = 0 j = 0
if quadrant_mode is None: if quadrant_mode is None:
@ -1598,6 +1624,7 @@ class Gerber(Geometry):
:type xfactor: float :type xfactor: float
:param yfactor: Number by which to scale on Y axis. :param yfactor: Number by which to scale on Y axis.
:type yfactor: float :type yfactor: float
:param point: reference point for scaling operation
:rtype : None :rtype : None
""" """
log.debug("camlib.Gerber.scale()") log.debug("camlib.Gerber.scale()")
@ -1628,7 +1655,7 @@ class Gerber(Geometry):
# variables to display the percentage of work done # variables to display the percentage of work done
self.geo_len = 0 self.geo_len = 0
try: try:
for g in self.solid_geometry: for __ in self.solid_geometry:
self.geo_len += 1 self.geo_len += 1
except TypeError: except TypeError:
self.geo_len = 1 self.geo_len = 1
@ -1717,7 +1744,7 @@ class Gerber(Geometry):
# variables to display the percentage of work done # variables to display the percentage of work done
self.geo_len = 0 self.geo_len = 0
try: try:
for g in self.solid_geometry: for __ in self.solid_geometry:
self.geo_len += 1 self.geo_len += 1
except TypeError: except TypeError:
self.geo_len = 1 self.geo_len = 1
@ -1796,7 +1823,7 @@ class Gerber(Geometry):
# variables to display the percentage of work done # variables to display the percentage of work done
self.geo_len = 0 self.geo_len = 0
try: try:
for g in self.solid_geometry: for __ in self.solid_geometry:
self.geo_len += 1 self.geo_len += 1
except TypeError: except TypeError:
self.geo_len = 1 self.geo_len = 1
@ -1857,6 +1884,10 @@ class Gerber(Geometry):
See shapely manual for more information: See shapely manual for more information:
http://toblerity.org/shapely/manual.html#affine-transformations http://toblerity.org/shapely/manual.html#affine-transformations
:param angle_x: the angle on X axis for skewing
:param angle_y: the angle on Y axis for skewing
:param point: reference point for skewing operation
:return None
""" """
log.debug("camlib.Gerber.skew()") log.debug("camlib.Gerber.skew()")
@ -1865,7 +1896,7 @@ class Gerber(Geometry):
# variables to display the percentage of work done # variables to display the percentage of work done
self.geo_len = 0 self.geo_len = 0
try: try:
for g in self.solid_geometry: for __ in self.solid_geometry:
self.geo_len += 1 self.geo_len += 1
except TypeError: except TypeError:
self.geo_len = 1 self.geo_len = 1
@ -1926,7 +1957,7 @@ class Gerber(Geometry):
# variables to display the percentage of work done # variables to display the percentage of work done
self.geo_len = 0 self.geo_len = 0
try: try:
for g in self.solid_geometry: for __ in self.solid_geometry:
self.geo_len += 1 self.geo_len += 1
except TypeError: except TypeError:
self.geo_len = 1 self.geo_len = 1
@ -1972,376 +2003,3 @@ class Gerber(Geometry):
self.app.inform.emit('[success] %s' % self.app.inform.emit('[success] %s' %
_("Gerber Rotate done.")) _("Gerber Rotate done."))
self.app.proc_container.new_text = '' self.app.proc_container.new_text = ''
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
am1_re = re.compile(r'^%AM([^\*]+)\*(.+)?(%)?$')
am2_re = re.compile(r'(.*)%$')
amcomm_re = re.compile(r'^0(.*)')
amprim_re = re.compile(r'^[1-9].*')
amvar_re = re.compile(r'^\$([0-9a-zA-z]+)=(.*)')
def __init__(self, name=None):
self.name = name
self.raw = ""
# ## These below are recomputed for every aperture
# ## definition, in other words, are temporary variables.
self.primitives = []
self.locvars = {}
self.geometry = None
def to_dict(self):
"""
Returns the object in a serializable form. Only the name and
raw are required.
:return: Dictionary representing the object. JSON ready.
:rtype: dict
"""
return {
'name': self.name,
'raw': self.raw
}
def from_dict(self, d):
"""
Populates the object from a serial representation created
with ``self.to_dict()``.
:param d: Serial representation of an ApertureMacro object.
:return: None
"""
for attr in ['name', 'raw']:
setattr(self, attr, d[attr])
def parse_content(self):
"""
Creates numerical lists for all primitives in the aperture
macro (in ``self.raw``) by replacing all variables by their
values iteratively and evaluating expressions. Results
are stored in ``self.primitives``.
:return: None
"""
# Cleanup
self.raw = self.raw.replace('\n', '').replace('\r', '').strip(" *")
self.primitives = []
# Separate parts
parts = self.raw.split('*')
# ### Every part in the macro ####
for part in parts:
# ## Comments. Ignored.
match = ApertureMacro.amcomm_re.search(part)
if match:
continue
# ## Variables
# These are variables defined locally inside the macro. They can be
# numerical constant or defind in terms of previously define
# variables, which can be defined locally or in an aperture
# definition. All replacements ocurr here.
match = ApertureMacro.amvar_re.search(part)
if match:
var = match.group(1)
val = match.group(2)
# Replace variables in value
for v in self.locvars:
# replaced the following line with the next to fix Mentor custom apertures not parsed OK
# val = re.sub((r'\$'+str(v)+r'(?![0-9a-zA-Z])'), str(self.locvars[v]), val)
val = val.replace('$' + str(v), str(self.locvars[v]))
# Make all others 0
val = re.sub(r'\$[0-9a-zA-Z](?![0-9a-zA-Z])', "0", val)
# Change x with *
val = re.sub(r'[xX]', "*", val)
# Eval() and store.
self.locvars[var] = eval(val)
continue
# ## Primitives
# Each is an array. The first identifies the primitive, while the
# rest depend on the primitive. All are strings representing a
# number and may contain variable definition. The values of these
# variables are defined in an aperture definition.
match = ApertureMacro.amprim_re.search(part)
if match:
# ## Replace all variables
for v in self.locvars:
# replaced the following line with the next to fix Mentor custom apertures not parsed OK
# part = re.sub(r'\$' + str(v) + r'(?![0-9a-zA-Z])', str(self.locvars[v]), part)
part = part.replace('$' + str(v), str(self.locvars[v]))
# Make all others 0
part = re.sub(r'\$[0-9a-zA-Z](?![0-9a-zA-Z])', "0", part)
# Change x with *
part = re.sub(r'[xX]', "*", part)
# ## Store
elements = part.split(",")
self.primitives.append([eval(x) for x in elements])
continue
log.warning("Unknown syntax of aperture macro part: %s" % str(part))
def append(self, data):
"""
Appends a string to the raw macro.
:param data: Part of the macro.
:type data: str
:return: None
"""
self.raw += data
@staticmethod
def default2zero(n, mods):
"""
Pads the ``mods`` list with zeros resulting in an
list of length n.
:param n: Length of the resulting list.
:type n: int
:param mods: List to be padded.
:type mods: list
:return: Zero-padded list.
:rtype: list
"""
x = [0.0] * n
na = len(mods)
x[0:na] = mods
return x
@staticmethod
def make_circle(mods):
"""
:param mods: (Exposure 0/1, Diameter >=0, X-coord, Y-coord)
:return:
"""
pol, dia, x, y = ApertureMacro.default2zero(4, mods)
return {"pol": int(pol), "geometry": Point(x, y).buffer(dia/2)}
@staticmethod
def make_vectorline(mods):
"""
:param mods: (Exposure 0/1, Line width >= 0, X-start, Y-start, X-end, Y-end,
rotation angle around origin in degrees)
:return:
"""
pol, width, xs, ys, xe, ye, angle = ApertureMacro.default2zero(7, mods)
line = LineString([(xs, ys), (xe, ye)])
box = line.buffer(width/2, cap_style=2)
box_rotated = affinity.rotate(box, angle, origin=(0, 0))
return {"pol": int(pol), "geometry": box_rotated}
@staticmethod
def make_centerline(mods):
"""
:param mods: (Exposure 0/1, width >=0, height >=0, x-center, y-center,
rotation angle around origin in degrees)
:return:
"""
pol, width, height, x, y, angle = ApertureMacro.default2zero(6, mods)
box = shply_box(x-width/2, y-height/2, x+width/2, y+height/2)
box_rotated = affinity.rotate(box, angle, origin=(0, 0))
return {"pol": int(pol), "geometry": box_rotated}
@staticmethod
def make_lowerleftline(mods):
"""
:param mods: (exposure 0/1, width >=0, height >=0, x-lowerleft, y-lowerleft,
rotation angle around origin in degrees)
:return:
"""
pol, width, height, x, y, angle = ApertureMacro.default2zero(6, mods)
box = shply_box(x, y, x+width, y+height)
box_rotated = affinity.rotate(box, angle, origin=(0, 0))
return {"pol": int(pol), "geometry": box_rotated}
@staticmethod
def make_outline(mods):
"""
:param mods:
:return:
"""
pol = mods[0]
n = mods[1]
points = [(0, 0)]*(n+1)
for i in range(n+1):
points[i] = mods[2*i + 2:2*i + 4]
angle = mods[2*n + 4]
poly = Polygon(points)
poly_rotated = affinity.rotate(poly, angle, origin=(0, 0))
return {"pol": int(pol), "geometry": poly_rotated}
@staticmethod
def make_polygon(mods):
"""
Note: Specs indicate that rotation is only allowed if the center
(x, y) == (0, 0). I will tolerate breaking this rule.
:param mods: (exposure 0/1, n_verts 3<=n<=12, x-center, y-center,
diameter of circumscribed circle >=0, rotation angle around origin)
:return:
"""
pol, nverts, x, y, dia, angle = ApertureMacro.default2zero(6, mods)
points = [(0, 0)]*nverts
for i in range(nverts):
points[i] = (x + 0.5 * dia * cos(2*pi * i/nverts),
y + 0.5 * dia * sin(2*pi * i/nverts))
poly = Polygon(points)
poly_rotated = affinity.rotate(poly, angle, origin=(0, 0))
return {"pol": int(pol), "geometry": poly_rotated}
@staticmethod
def make_moire(mods):
"""
Note: Specs indicate that rotation is only allowed if the center
(x, y) == (0, 0). I will tolerate breaking this rule.
:param mods: (x-center, y-center, outer_dia_outer_ring, ring thickness,
gap, max_rings, crosshair_thickness, crosshair_len, rotation
angle around origin in degrees)
:return:
"""
x, y, dia, thickness, gap, nrings, cross_th, cross_len, angle = ApertureMacro.default2zero(9, mods)
r = dia/2 - thickness/2
result = Point((x, y)).buffer(r).exterior.buffer(thickness/2.0)
ring = Point((x, y)).buffer(r).exterior.buffer(thickness/2.0) # Need a copy!
i = 1 # Number of rings created so far
# ## If the ring does not have an interior it means that it is
# ## a disk. Then stop.
while len(ring.interiors) > 0 and i < nrings:
r -= thickness + gap
if r <= 0:
break
ring = Point((x, y)).buffer(r).exterior.buffer(thickness/2.0)
result = cascaded_union([result, ring])
i += 1
# ## Crosshair
hor = LineString([(x - cross_len, y), (x + cross_len, y)]).buffer(cross_th/2.0, cap_style=2)
ver = LineString([(x, y-cross_len), (x, y + cross_len)]).buffer(cross_th/2.0, cap_style=2)
result = cascaded_union([result, hor, ver])
return {"pol": 1, "geometry": result}
@staticmethod
def make_thermal(mods):
"""
Note: Specs indicate that rotation is only allowed if the center
(x, y) == (0, 0). I will tolerate breaking this rule.
:param mods: [x-center, y-center, diameter-outside, diameter-inside,
gap-thickness, rotation angle around origin]
:return:
"""
x, y, dout, din, t, angle = ApertureMacro.default2zero(6, mods)
ring = Point((x, y)).buffer(dout/2.0).difference(Point((x, y)).buffer(din/2.0))
hline = LineString([(x - dout/2.0, y), (x + dout/2.0, y)]).buffer(t/2.0, cap_style=3)
vline = LineString([(x, y - dout/2.0), (x, y + dout/2.0)]).buffer(t/2.0, cap_style=3)
thermal = ring.difference(hline.union(vline))
return {"pol": 1, "geometry": thermal}
def make_geometry(self, modifiers):
"""
Runs the macro for the given modifiers and generates
the corresponding geometry.
:param modifiers: Modifiers (parameters) for this macro
:type modifiers: list
:return: Shapely geometry
:rtype: shapely.geometry.polygon
"""
# ## Primitive makers
makers = {
"1": ApertureMacro.make_circle,
"2": ApertureMacro.make_vectorline,
"20": ApertureMacro.make_vectorline,
"21": ApertureMacro.make_centerline,
"22": ApertureMacro.make_lowerleftline,
"4": ApertureMacro.make_outline,
"5": ApertureMacro.make_polygon,
"6": ApertureMacro.make_moire,
"7": ApertureMacro.make_thermal
}
# ## Store modifiers as local variables
modifiers = modifiers or []
modifiers = [float(m) for m in modifiers]
self.locvars = {}
for i in range(0, len(modifiers)):
self.locvars[str(i + 1)] = modifiers[i]
# ## Parse
self.primitives = [] # Cleanup
self.geometry = Polygon()
self.parse_content()
# ## Make the geometry
for primitive in self.primitives:
# Make the primitive
prim_geo = makers[str(int(primitive[0]))](primitive[1:])
# Add it (according to polarity)
# if self.geometry is None and prim_geo['pol'] == 1:
# self.geometry = prim_geo['geometry']
# continue
if prim_geo['pol'] == 1:
self.geometry = self.geometry.union(prim_geo['geometry'])
continue
if prim_geo['pol'] == 0:
self.geometry = self.geometry.difference(prim_geo['geometry'])
continue
return self.geometry

View File

@ -3,10 +3,11 @@ from PyQt5 import QtGui, QtWidgets
from PyQt5 import QtCore from PyQt5 import QtCore
from PyQt5.QtCore import Qt from PyQt5.QtCore import Qt
class TitleBar(QtWidgets.QDialog): class TitleBar(QtWidgets.QDialog):
def __init__(self, parent=None): def __init__(self, parent=None):
QtWidgets.QWidget.__init__(self, parent) QtWidgets.QWidget.__init__(self, parent)
self.setWindowFlags(Qt.FramelessWindowHint); self.setWindowFlags(Qt.FramelessWindowHint)
css = """ css = """
QWidget{ QWidget{
Background: #AA00AA; Background: #AA00AA;
@ -60,19 +61,19 @@ class TitleBar(QtWidgets.QDialog):
self.maximize.clicked.connect(self.showMaxRestore) self.maximize.clicked.connect(self.showMaxRestore)
def showSmall(self): def showSmall(self):
box.showMinimized(); box.showMinimized()
def showMaxRestore(self): def showMaxRestore(self):
if(self.maxNormal): if(self.maxNormal):
box.showNormal(); box.showNormal()
self.maxNormal= False; self.maxNormal= False
self.maximize.setIcon(QtGui.QIcon('img/max.png')); self.maximize.setIcon(QtGui.QIcon('img/max.png'))
print(1) print(1)
else: else:
box.showMaximized(); box.showMaximized()
self.maxNormal= True; self.maxNormal= True
print(2) print(2)
self.maximize.setIcon(QtGui.QIcon('img/max2.png')); self.maximize.setIcon(QtGui.QIcon('img/max2.png'))
def close(self): def close(self):
box.close() box.close()
@ -88,7 +89,7 @@ class TitleBar(QtWidgets.QDialog):
class Frame(QtWidgets.QFrame): class Frame(QtWidgets.QFrame):
def __init__(self, parent=None): def __init__(self, parent=None):
QtWidgets.QFrame.__init__(self, parent) QtWidgets.QFrame.__init__(self, parent)
self.m_mouse_down= False; self.m_mouse_down= False
self.setFrameShape(QtWidgets.QFrame.StyledPanel) self.setFrameShape(QtWidgets.QFrame.StyledPanel)
css = """ css = """
QFrame{ QFrame{
@ -122,25 +123,26 @@ class Frame(QtWidgets.QFrame):
return self.m_titleBar return self.m_titleBar
def mousePressEvent(self,event): def mousePressEvent(self,event):
self.m_old_pos = event.pos(); self.m_old_pos = event.pos()
self.m_mouse_down = event.button()== Qt.LeftButton; self.m_mouse_down = event.button()== Qt.LeftButton
def mouseMoveEvent(self,event): def mouseMoveEvent(self,event):
x=event.x() x=event.x()
y=event.y() y=event.y()
def mouseReleaseEvent(self,event): def mouseReleaseEvent(self,event):
m_mouse_down=False; m_mouse_down=False
if __name__ == '__main__': if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv); app = QtWidgets.QApplication(sys.argv)
box = Frame() box = Frame()
box.move(60,60) box.move(60,60)
l=QtWidgets.QVBoxLayout(box.contentWidget()); l = QtWidgets.QVBoxLayout(box.contentWidget())
l.setContentsMargins(0, 0,0 ,0) l.setContentsMargins(0, 0,0 ,0)
edit=QtWidgets.QLabel("""I would've did anything for you to show you how much I adored you edit = QtWidgets.QLabel("""I would've did anything for you to show you how much I adored you
But it's over now, it's too late to save our loveJust promise me you'll think of me But it's over now, it's too late to save our loveJust promise me you'll think of me
Every time you look up in the sky and see a star 'cuz I'm your star."""); Every time you look up in the sky and see a star 'cuz I'm your star.""")
l.addWidget(edit) l.addWidget(edit)
box.show() box.show()
app.exec_() app.exec_()

View File

@ -4,7 +4,7 @@
import sys import sys
sys.path.append('../../') sys.path.append('../../')
from camlib import * from flatcamParsers.ParseGerber import *
log = logging.getLogger('base2') log = logging.getLogger('base2')
log.setLevel(logging.WARNING) log.setLevel(logging.WARNING)

View File

@ -3,7 +3,7 @@ import pstats
import sys import sys
sys.path.append('../../') sys.path.append('../../')
from camlib import * from flatcamParsers.ParseGerber import *
log = logging.getLogger('base2') log = logging.getLogger('base2')
log.setLevel(logging.WARNING) log.setLevel(logging.WARNING)

View File

@ -2,6 +2,7 @@ import sys
from PyQt5.Qt import * from PyQt5.Qt import *
from PyQt5 import QtGui, QtWidgets from PyQt5 import QtGui, QtWidgets
class MyPopup(QWidget): class MyPopup(QWidget):
def __init__(self): def __init__(self):
QWidget.__init__(self) QWidget.__init__(self)
@ -30,6 +31,7 @@ class MyPopup(QWidget):
# dc.drawLine(0, 0, 100, 100) # dc.drawLine(0, 0, 100, 100)
# dc.drawLine(100, 0, 0, 100) # dc.drawLine(100, 0, 0, 100)
class MainWindow(QMainWindow): class MainWindow(QMainWindow):
def __init__(self, *args): def __init__(self, *args):
QtWidgets.QMainWindow.__init__(self, *args) QtWidgets.QMainWindow.__init__(self, *args)
@ -46,6 +48,7 @@ class MainWindow(QMainWindow):
self.w.setGeometry(QRect(100, 100, 400, 200)) self.w.setGeometry(QRect(100, 100, 400, 200))
self.w.show() self.w.show()
class App(QApplication): class App(QApplication):
def __init__(self, *args): def __init__(self, *args):
QtWidgets.QApplication.__init__(self, *args) QtWidgets.QApplication.__init__(self, *args)
@ -56,10 +59,12 @@ class App(QApplication):
def byebye(self): def byebye(self):
self.exit(0) self.exit(0)
def main(args): def main(args):
global app global app
app = App(args) app = App(args)
app.exec_() app.exec_()
if __name__ == "__main__": if __name__ == "__main__":
main(sys.argv) main(sys.argv)

View File

@ -1,5 +1,5 @@
import sys import sys
from PyQt4 import QtCore, QtGui from PyQt5 import QtCore, QtGui, QtWidgets
class MyObj(): class MyObj():
@ -16,12 +16,12 @@ def parse():
raise Exception("Intentional Exception") raise Exception("Intentional Exception")
class Example(QtGui.QWidget): class Example(QtWidgets.QWidget):
def __init__(self): def __init__(self):
super(Example, self).__init__() super(Example, self).__init__()
qbtn = QtGui.QPushButton('Raise', self) qbtn = QtWidgets.QPushButton('Raise', self)
qbtn.clicked.connect(parse) qbtn.clicked.connect(parse)
self.setWindowTitle('Quit button') self.setWindowTitle('Quit button')
@ -29,6 +29,6 @@ class Example(QtGui.QWidget):
if __name__ == '__main__': if __name__ == '__main__':
app = QtGui.QApplication(sys.argv) app = QtWidgets.QApplication(sys.argv)
ex = Example() ex = Example()
sys.exit(app.exec_()) sys.exit(app.exec_())

View File

@ -1,7 +1,7 @@
import os import os
os.chdir('../') os.chdir('../')
from camlib import * from flatcamParsers.ParseGerber import *
g = Gerber() g = Gerber()
g.parse_file(r'C:\Users\jpcaram\Dropbox\CNC\pcbcam\test_files\PlacaReles-F_Cu.gtl') g.parse_file(r'C:\Users\jpcaram\Dropbox\CNC\pcbcam\test_files\PlacaReles-F_Cu.gtl')

View File

@ -8,12 +8,13 @@ Created on Sun Jan 05 13:30:47 2014
import os import os
os.chdir('../') os.chdir('../')
from camlib import * from flatcamParsers.ParseGerber import *
#from matplotlib.figure import Figure from flatcamParsers.ParseExcellon import *
from matplotlib import pyplot from matplotlib import pyplot
# Gerber. To see if the Excellon is correct # Gerber. To see if the Excellon is correct
project_dir = "tests/" project_dir = "tests/gerber_files"
gerber_filename = project_dir + "KiCad_Squarer-F_Cu.gtl" gerber_filename = project_dir + "KiCad_Squarer-F_Cu.gtl"
g = Gerber() g = Gerber()
g.parse_file(gerber_filename) g.parse_file(gerber_filename)
@ -32,7 +33,7 @@ ax.set_aspect(1)
# Plot gerber # Plot gerber
for geo in g.solid_geometry: for geo in g.solid_geometry:
x, y = geo.exterior.coords.xy x, y = geo.exterior.coords.xy
plot(x, y, 'k-') pyplot.plot(x, y, 'k-')
for ints in geo.interiors: for ints in geo.interiors:
x, y = ints.coords.xy x, y = ints.coords.xy
ax.plot(x, y, 'k-') ax.plot(x, y, 'k-')
@ -40,7 +41,7 @@ for geo in g.solid_geometry:
# Plot excellon # Plot excellon
for geo in ex.solid_geometry: for geo in ex.solid_geometry:
x, y = geo.exterior.coords.xy x, y = geo.exterior.coords.xy
plot(x, y, 'r-') pyplot.plot(x, y, 'r-')
for ints in geo.interiors: for ints in geo.interiors:
x, y = ints.coords.xy x, y = ints.coords.xy
ax.plot(x, y, 'g-') ax.plot(x, y, 'g-')

View File

@ -1,6 +1,7 @@
from shapely.geometry import LineString, Polygon from shapely.geometry import LineString, Polygon
from shapely.ops import cascaded_union, unary_union from shapely.ops import cascaded_union, unary_union
from matplotlib.pyplot import plot, subplot, show from matplotlib.pyplot import plot, subplot, show, axes
from matplotlib.axes import *
from camlib import * from camlib import *

View File

@ -1,5 +1,6 @@
import unittest import unittest
import camlib from flatcamParsers.ParseExcellon import Excellon
from flatcamParsers.ParseGerber import Gerber
class ExcellonNumberParseTestInch(unittest.TestCase): class ExcellonNumberParseTestInch(unittest.TestCase):
@ -16,39 +17,39 @@ class ExcellonNumberParseTestInch(unittest.TestCase):
# of digits you typed and automatically fill in the missing zeros. # of digits you typed and automatically fill in the missing zeros.
def test_inch_leading_6digit(self): def test_inch_leading_6digit(self):
excellon = camlib.Excellon() excellon = Excellon()
self.assertEqual(excellon.zeros, "L") self.assertEqual(excellon.zeros, "L")
self.assertEqual(excellon.parse_number("123456"), 12.3456) self.assertEqual(excellon.parse_number("123456"), 12.3456)
def test_inch_leading_5digit(self): def test_inch_leading_5digit(self):
excellon = camlib.Excellon() excellon = Excellon()
self.assertEqual(excellon.parse_number("12345"), 12.345) self.assertEqual(excellon.parse_number("12345"), 12.345)
def test_inch_leading_15digit(self): def test_inch_leading_15digit(self):
excellon = camlib.Excellon() excellon = Excellon()
self.assertEqual(excellon.parse_number("012345"), 1.2345) self.assertEqual(excellon.parse_number("012345"), 1.2345)
def test_inch_leading_51digit(self): def test_inch_leading_51digit(self):
excellon = camlib.Excellon() excellon = Excellon()
self.assertEqual(excellon.parse_number("123450"), 12.345) self.assertEqual(excellon.parse_number("123450"), 12.345)
def test_inch_trailing_6digit(self): def test_inch_trailing_6digit(self):
excellon = camlib.Excellon() excellon = Excellon()
excellon.zeros = "T" excellon.zeros = "T"
self.assertEqual(excellon.parse_number("123456"), 12.3456) self.assertEqual(excellon.parse_number("123456"), 12.3456)
def test_inch_trailing_5digit(self): def test_inch_trailing_5digit(self):
excellon = camlib.Excellon() excellon = Excellon()
excellon.zeros = "T" excellon.zeros = "T"
self.assertEqual(excellon.parse_number("12345"), 1.2345) self.assertEqual(excellon.parse_number("12345"), 1.2345)
def test_inch_trailing_15digit(self): def test_inch_trailing_15digit(self):
excellon = camlib.Excellon() excellon = Excellon()
excellon.zeros = "T" excellon.zeros = "T"
self.assertEqual(excellon.parse_number("012345"), 1.2345) self.assertEqual(excellon.parse_number("012345"), 1.2345)
def test_inch_trailing_51digit(self): def test_inch_trailing_51digit(self):
excellon = camlib.Excellon() excellon = Excellon()
excellon.zeros = "T" excellon.zeros = "T"
self.assertEqual(excellon.parse_number("123450"), 12.345) self.assertEqual(excellon.parse_number("123450"), 12.345)
@ -67,45 +68,45 @@ class ExcellonNumberParseTestMetric(unittest.TestCase):
# of digits you typed and automatically fill in the missing zeros. # of digits you typed and automatically fill in the missing zeros.
def test_inch_leading_6digit(self): def test_inch_leading_6digit(self):
excellon = camlib.Excellon() excellon = Excellon()
excellon.units = "mm" excellon.units = "mm"
self.assertEqual(excellon.parse_number("123456"), 123.456) self.assertEqual(excellon.parse_number("123456"), 123.456)
def test_inch_leading_5digit(self): def test_inch_leading_5digit(self):
excellon = camlib.Excellon() excellon = Excellon()
excellon.units = "mm" excellon.units = "mm"
self.assertEqual(excellon.parse_number("12345"), 123.45) self.assertEqual(excellon.parse_number("12345"), 123.45)
def test_inch_leading_15digit(self): def test_inch_leading_15digit(self):
excellon = camlib.Excellon() excellon = Excellon()
excellon.units = "mm" excellon.units = "mm"
self.assertEqual(excellon.parse_number("012345"), 12.345) self.assertEqual(excellon.parse_number("012345"), 12.345)
def test_inch_leading_51digit(self): def test_inch_leading_51digit(self):
excellon = camlib.Excellon() excellon = Excellon()
excellon.units = "mm" excellon.units = "mm"
self.assertEqual(excellon.parse_number("123450"), 123.45) self.assertEqual(excellon.parse_number("123450"), 123.45)
def test_inch_trailing_6digit(self): def test_inch_trailing_6digit(self):
excellon = camlib.Excellon() excellon = Excellon()
excellon.units = "mm" excellon.units = "mm"
excellon.zeros = "T" excellon.zeros = "T"
self.assertEqual(excellon.parse_number("123456"), 123.456) self.assertEqual(excellon.parse_number("123456"), 123.456)
def test_inch_trailing_5digit(self): def test_inch_trailing_5digit(self):
excellon = camlib.Excellon() excellon = Excellon()
excellon.units = "mm" excellon.units = "mm"
excellon.zeros = "T" excellon.zeros = "T"
self.assertEqual(excellon.parse_number("12345"), 12.345) self.assertEqual(excellon.parse_number("12345"), 12.345)
def test_inch_trailing_15digit(self): def test_inch_trailing_15digit(self):
excellon = camlib.Excellon() excellon = Excellon()
excellon.units = "mm" excellon.units = "mm"
excellon.zeros = "T" excellon.zeros = "T"
self.assertEqual(excellon.parse_number("012345"), 12.345) self.assertEqual(excellon.parse_number("012345"), 12.345)
def test_inch_trailing_51digit(self): def test_inch_trailing_51digit(self):
excellon = camlib.Excellon() excellon = Excellon()
excellon.units = "mm" excellon.units = "mm"
excellon.zeros = "T" excellon.zeros = "T"
self.assertEqual(excellon.parse_number("123450"), 123.45) self.assertEqual(excellon.parse_number("123450"), 123.45)
@ -114,7 +115,7 @@ class ExcellonNumberParseTestMetric(unittest.TestCase):
class ExcellonFormatM72Test(unittest.TestCase): class ExcellonFormatM72Test(unittest.TestCase):
def setUp(self): def setUp(self):
self.excellon = camlib.Excellon() self.excellon = Excellon()
code = """ code = """
M48 M48
M72 M72
@ -141,7 +142,7 @@ class ExcellonFormatM72Test(unittest.TestCase):
class ExcellonFormatM71Test(unittest.TestCase): class ExcellonFormatM71Test(unittest.TestCase):
def setUp(self): def setUp(self):
self.excellon = camlib.Excellon() self.excellon = Excellon()
code = """ code = """
M48 M48
M71 M71
@ -168,7 +169,7 @@ class ExcellonFormatM71Test(unittest.TestCase):
class ExcellonFormatINCHLZTest(unittest.TestCase): class ExcellonFormatINCHLZTest(unittest.TestCase):
def setUp(self): def setUp(self):
self.excellon = camlib.Excellon() self.excellon = Excellon()
code = """ code = """
M48 M48
INCH,LZ INCH,LZ
@ -195,7 +196,7 @@ class ExcellonFormatINCHLZTest(unittest.TestCase):
class ExcellonFormatINCHTest(unittest.TestCase): class ExcellonFormatINCHTest(unittest.TestCase):
def setUp(self): def setUp(self):
self.excellon = camlib.Excellon() self.excellon = Excellon()
code = """ code = """
M48 M48
INCH,LZ INCH,LZ
@ -222,7 +223,7 @@ class ExcellonFormatINCHTest(unittest.TestCase):
class ExcellonFormatINCHTZTest(unittest.TestCase): class ExcellonFormatINCHTZTest(unittest.TestCase):
def setUp(self): def setUp(self):
self.excellon = camlib.Excellon() self.excellon = Excellon()
code = """ code = """
M48 M48
INCH,TZ INCH,TZ
@ -249,7 +250,7 @@ class ExcellonFormatINCHTZTest(unittest.TestCase):
class ExcellonFormatMETRICLZTest(unittest.TestCase): class ExcellonFormatMETRICLZTest(unittest.TestCase):
def setUp(self): def setUp(self):
self.excellon = camlib.Excellon() self.excellon = Excellon()
code = """ code = """
M48 M48
METRIC,LZ METRIC,LZ
@ -276,7 +277,7 @@ class ExcellonFormatMETRICLZTest(unittest.TestCase):
class ExcellonFormatMETRICTest(unittest.TestCase): class ExcellonFormatMETRICTest(unittest.TestCase):
def setUp(self): def setUp(self):
self.excellon = camlib.Excellon() self.excellon = Excellon()
code = """ code = """
M48 M48
METRIC,LZ METRIC,LZ
@ -303,7 +304,7 @@ class ExcellonFormatMETRICTest(unittest.TestCase):
class ExcellonFormatMETRICTZTest(unittest.TestCase): class ExcellonFormatMETRICTZTest(unittest.TestCase):
def setUp(self): def setUp(self):
self.excellon = camlib.Excellon() self.excellon = Excellon()
code = """ code = """
M48 M48
METRIC,TZ METRIC,TZ

View File

@ -1,9 +1,9 @@
import unittest import unittest
from PyQt4 import QtGui from PyQt5 import QtGui, QtWidgets
import sys import sys
from FlatCAMApp import App from FlatCAMApp import App
from FlatCAMObj import FlatCAMExcellon, FlatCAMCNCjob from FlatCAMObj import FlatCAMExcellon, FlatCAMCNCjob
from ObjectUI import ExcellonObjectUI from flatcamGUI.ObjectUI import ExcellonObjectUI
import tempfile import tempfile
import os import os
from time import sleep from time import sleep
@ -21,7 +21,7 @@ class ExcellonFlowTestCase(unittest.TestCase):
filename = 'case1.drl' filename = 'case1.drl'
def setUp(self): def setUp(self):
self.app = QtGui.QApplication(sys.argv) self.app = QtWidgets.QApplication(sys.argv)
# Create App, keep app defaults (do not load # Create App, keep app defaults (do not load
# user-defined defaults). # user-defined defaults).
@ -79,19 +79,19 @@ class ExcellonFlowTestCase(unittest.TestCase):
option, value, form_field.get_value() option, value, form_field.get_value()
)) ))
#-------------------------------------------------- # --------------------------------------------------
# Changes in the GUI should be read in when # Changes in the GUI should be read in when
# running any process. Changing something here. # running any process. Changing something here.
#-------------------------------------------------- # --------------------------------------------------
form_field = excellon_obj.form_fields['feedrate'] form_field = excellon_obj.form_fields['feedrate']
value = form_field.get_value() value = form_field.get_value()
form_field.set_value(value * 1.1) # Increase by 10% form_field.set_value(value * 1.1) # Increase by 10%
print(("'feedrate' == {}".format(value))) print(("'feedrate' == {}".format(value)))
#-------------------------------------------------- # --------------------------------------------------
# Create GCode using all tools. # Create GCode using all tools.
#-------------------------------------------------- # --------------------------------------------------
assert isinstance(excellon_obj, FlatCAMExcellon) # Just for the IDE assert isinstance(excellon_obj, FlatCAMExcellon) # Just for the IDE
ui = excellon_obj.ui ui = excellon_obj.ui
@ -110,9 +110,9 @@ class ExcellonFlowTestCase(unittest.TestCase):
sleep(0.1) sleep(0.1)
self.app.processEvents() self.app.processEvents()
#--------------------------------------------- # ---------------------------------------------
# Check that GUI has been read in. # Check that GUI has been read in.
#--------------------------------------------- # ---------------------------------------------
value = excellon_obj.options['feedrate'] value = excellon_obj.options['feedrate']
form_value = form_field.get_value() form_value = form_field.get_value()
@ -121,33 +121,33 @@ class ExcellonFlowTestCase(unittest.TestCase):
"which has {}".format('feedrate', form_value, value)) "which has {}".format('feedrate', form_value, value))
print(("'feedrate' == {}".format(value))) print(("'feedrate' == {}".format(value)))
#--------------------------------------------- # ---------------------------------------------
# Check that only 1 object has been created. # Check that only 1 object has been created.
#--------------------------------------------- # ---------------------------------------------
names = self.fc.collection.get_names() names = self.fc.collection.get_names()
self.assertEqual(len(names), 2, self.assertEqual(len(names), 2,
"Expected 2 objects, found %d" % len(names)) "Expected 2 objects, found %d" % len(names))
#------------------------------------------------------- # -------------------------------------------------------
# Make sure the CNCJob Object has the correct name # Make sure the CNCJob Object has the correct name
#------------------------------------------------------- # -------------------------------------------------------
cncjob_name = excellon_name + "_cnc" cncjob_name = excellon_name + "_cnc"
self.assertTrue(cncjob_name in names, self.assertTrue(cncjob_name in names,
"Object named %s not found." % cncjob_name) "Object named %s not found." % cncjob_name)
#------------------------------------------------------- # -------------------------------------------------------
# Get the object make sure it's a cncjob object # Get the object make sure it's a cncjob object
#------------------------------------------------------- # -------------------------------------------------------
cncjob_obj = self.fc.collection.get_by_name(cncjob_name) cncjob_obj = self.fc.collection.get_by_name(cncjob_name)
self.assertTrue(isinstance(cncjob_obj, FlatCAMCNCjob), self.assertTrue(isinstance(cncjob_obj, FlatCAMCNCjob),
"Expected a FlatCAMCNCjob, got %s" % type(cncjob_obj)) "Expected a FlatCAMCNCjob, got %s" % type(cncjob_obj))
#----------------------------------------- # -----------------------------------------
# Export G-Code, check output # Export G-Code, check output
#----------------------------------------- # -----------------------------------------
assert isinstance(cncjob_obj, FlatCAMCNCjob) # For IDE assert isinstance(cncjob_obj, FlatCAMCNCjob) # For IDE
# get system temporary file(try create it and delete) # get system temporary file(try create it and delete)

View File

@ -1,15 +1,16 @@
import unittest import unittest
import camlib import camlib
from flatcamParsers.ParseGerber import Gerber
class GerberBuffer(unittest.TestCase): class GerberBuffer(unittest.TestCase):
def setUp(self): def setUp(self):
self.gerber1 = camlib.Gerber() self.gerber1 = Gerber()
self.gerber1.use_buffer_for_union = True self.gerber1.use_buffer_for_union = True
self.gerber1.parse_file("tests/gerber_files/STM32F4-spindle.cmp") self.gerber1.parse_file("tests/gerber_files/STM32F4-spindle.cmp")
geometry1 = self.gerber1.solid_geometry geometry1 = self.gerber1.solid_geometry
self.geometry1_area = self.compute_area(geometry1) self.geometry1_area = self.compute_area(geometry1)
self.gerber2 = camlib.Gerber() self.gerber2 = Gerber()
self.gerber2.use_buffer_for_union = False self.gerber2.use_buffer_for_union = False
self.gerber2.parse_file("tests/gerber_files/STM32F4-spindle.cmp") self.gerber2.parse_file("tests/gerber_files/STM32F4-spindle.cmp")
geometry2 = self.gerber2.solid_geometry geometry2 = self.gerber2.solid_geometry
@ -21,7 +22,7 @@ class GerberBuffer(unittest.TestCase):
for geo in geometry: for geo in geometry:
area += geo.area area += geo.area
## Not iterable, do the actual indexing and add. # Not iterable, do the actual indexing and add.
except TypeError: except TypeError:
area = geometry.area area = geometry.area
return area return area

View File

@ -1,9 +1,9 @@
import sys import sys
import unittest import unittest
from PyQt4 import QtGui from PyQt5 import QtGui, QtWidgets
from FlatCAMApp import App, tclCommands from FlatCAMApp import App, tclCommands
from FlatCAMObj import FlatCAMGerber, FlatCAMGeometry, FlatCAMCNCjob from FlatCAMObj import FlatCAMGerber, FlatCAMGeometry, FlatCAMCNCjob
from ObjectUI import GerberObjectUI, GeometryObjectUI from flatcamGUI.ObjectUI import GerberObjectUI, GeometryObjectUI
from time import sleep from time import sleep
import os import os
import tempfile import tempfile
@ -21,7 +21,7 @@ class GerberFlowTestCase(unittest.TestCase):
filename = 'simple1.gbr' filename = 'simple1.gbr'
def setUp(self): def setUp(self):
self.app = QtGui.QApplication(sys.argv) self.app = QtWidgets.QApplication(sys.argv)
# Create App, keep app defaults (do not load # Create App, keep app defaults (do not load
# user-defined defaults). # user-defined defaults).
@ -38,30 +38,30 @@ class GerberFlowTestCase(unittest.TestCase):
names = self.fc.collection.get_names() names = self.fc.collection.get_names()
print(names) print(names)
#-------------------------------------- # --------------------------------------
# Total of 1 objects. # Total of 1 objects.
#-------------------------------------- # --------------------------------------
self.assertEqual(len(names), 1, self.assertEqual(len(names), 1,
"Expected 1 object, found %d" % len(names)) "Expected 1 object, found %d" % len(names))
#-------------------------------------- # --------------------------------------
# Object's name matches the file name. # Object's name matches the file name.
#-------------------------------------- # --------------------------------------
self.assertEqual(names[0], self.filename, self.assertEqual(names[0], self.filename,
"Expected name == %s, got %s" % (self.filename, names[0])) "Expected name == %s, got %s" % (self.filename, names[0]))
#--------------------------------------- # ---------------------------------------
# Get object by that name, make sure it's a FlatCAMGerber. # Get object by that name, make sure it's a FlatCAMGerber.
#--------------------------------------- # ---------------------------------------
gerber_name = names[0] gerber_name = names[0]
gerber_obj = self.fc.collection.get_by_name(gerber_name) gerber_obj = self.fc.collection.get_by_name(gerber_name)
self.assertTrue(isinstance(gerber_obj, FlatCAMGerber), self.assertTrue(isinstance(gerber_obj, FlatCAMGerber),
"Expected FlatCAMGerber, instead, %s is %s" % "Expected FlatCAMGerber, instead, %s is %s" %
(gerber_name, type(gerber_obj))) (gerber_name, type(gerber_obj)))
#---------------------------------------- # ----------------------------------------
# Object's GUI matches Object's options # Object's GUI matches Object's options
#---------------------------------------- # ----------------------------------------
# TODO: Open GUI with double-click on object. # TODO: Open GUI with double-click on object.
# Opens the Object's GUI, populates it. # Opens the Object's GUI, populates it.
gerber_obj.build_ui() gerber_obj.build_ui()
@ -79,20 +79,20 @@ class GerberFlowTestCase(unittest.TestCase):
option, value, form_field.get_value() option, value, form_field.get_value()
)) ))
#-------------------------------------------------- # --------------------------------------------------
# Changes in the GUI should be read in when # Changes in the GUI should be read in when
# running any process. Changing something here. # running any process. Changing something here.
#-------------------------------------------------- # --------------------------------------------------
form_field = gerber_obj.form_fields['isotooldia'] form_field = gerber_obj.form_fields['isotooldia']
value = form_field.get_value() value = form_field.get_value()
form_field.set_value(value * 1.1) # Increase by 10% form_field.set_value(value * 1.1) # Increase by 10%
print(("'isotooldia' == {}".format(value))) print(("'isotooldia' == {}".format(value)))
#-------------------------------------------------- # --------------------------------------------------
# Create isolation routing using default values # Create isolation routing using default values
# and by clicking on the button. # and by clicking on the button.
#-------------------------------------------------- # --------------------------------------------------
# Get the object's GUI and click on "Generate Geometry" under # Get the object's GUI and click on "Generate Geometry" under
# "Isolation Routing" # "Isolation Routing"
assert isinstance(gerber_obj, FlatCAMGerber) # Just for the IDE assert isinstance(gerber_obj, FlatCAMGerber) # Just for the IDE
@ -102,9 +102,9 @@ class GerberFlowTestCase(unittest.TestCase):
assert isinstance(ui, GerberObjectUI) assert isinstance(ui, GerberObjectUI)
ui.generate_iso_button.click() # Click ui.generate_iso_button.click() # Click
#--------------------------------------------- # ---------------------------------------------
# Check that GUI has been read in. # Check that GUI has been read in.
#--------------------------------------------- # ---------------------------------------------
value = gerber_obj.options['isotooldia'] value = gerber_obj.options['isotooldia']
form_value = form_field.get_value() form_value = form_field.get_value()
self.assertEqual(value, form_value, self.assertEqual(value, form_value,
@ -112,30 +112,30 @@ class GerberFlowTestCase(unittest.TestCase):
"which has {}".format('isotooldia', form_value, value)) "which has {}".format('isotooldia', form_value, value))
print(("'isotooldia' == {}".format(value))) print(("'isotooldia' == {}".format(value)))
#--------------------------------------------- # ---------------------------------------------
# Check that only 1 object has been created. # Check that only 1 object has been created.
#--------------------------------------------- # ---------------------------------------------
names = self.fc.collection.get_names() names = self.fc.collection.get_names()
self.assertEqual(len(names), 2, self.assertEqual(len(names), 2,
"Expected 2 objects, found %d" % len(names)) "Expected 2 objects, found %d" % len(names))
#------------------------------------------------------- # -------------------------------------------------------
# Make sure the Geometry Object has the correct name # Make sure the Geometry Object has the correct name
#------------------------------------------------------- # -------------------------------------------------------
geo_name = gerber_name + "_iso" geo_name = gerber_name + "_iso"
self.assertTrue(geo_name in names, self.assertTrue(geo_name in names,
"Object named %s not found." % geo_name) "Object named %s not found." % geo_name)
#------------------------------------------------------- # -------------------------------------------------------
# Get the object make sure it's a geometry object # Get the object make sure it's a geometry object
#------------------------------------------------------- # -------------------------------------------------------
geo_obj = self.fc.collection.get_by_name(geo_name) geo_obj = self.fc.collection.get_by_name(geo_name)
self.assertTrue(isinstance(geo_obj, FlatCAMGeometry), self.assertTrue(isinstance(geo_obj, FlatCAMGeometry),
"Expected a FlatCAMGeometry, got %s" % type(geo_obj)) "Expected a FlatCAMGeometry, got %s" % type(geo_obj))
#------------------------------------ # ------------------------------------
# Open the UI, make CNCObject # Open the UI, make CNCObject
#------------------------------------ # ------------------------------------
geo_obj.build_ui() geo_obj.build_ui()
ui = geo_obj.ui ui = geo_obj.ui
assert isinstance(ui, GeometryObjectUI) # Just for the IDE assert isinstance(ui, GeometryObjectUI) # Just for the IDE
@ -152,30 +152,30 @@ class GerberFlowTestCase(unittest.TestCase):
sleep(0.1) sleep(0.1)
self.app.processEvents() self.app.processEvents()
#--------------------------------------------- # ---------------------------------------------
# Check that only 1 object has been created. # Check that only 1 object has been created.
#--------------------------------------------- # ---------------------------------------------
names = self.fc.collection.get_names() names = self.fc.collection.get_names()
self.assertEqual(len(names), 3, self.assertEqual(len(names), 3,
"Expected 3 objects, found %d" % len(names)) "Expected 3 objects, found %d" % len(names))
#------------------------------------------------------- # -------------------------------------------------------
# Make sure the CNC Job Object has the correct name # Make sure the CNC Job Object has the correct name
#------------------------------------------------------- # -------------------------------------------------------
cnc_name = geo_name + "_cnc" cnc_name = geo_name + "_cnc"
self.assertTrue(cnc_name in names, self.assertTrue(cnc_name in names,
"Object named %s not found." % geo_name) "Object named %s not found." % geo_name)
#------------------------------------------------------- # -------------------------------------------------------
# Get the object make sure it's a CNC Job object # Get the object make sure it's a CNC Job object
#------------------------------------------------------- # -------------------------------------------------------
cnc_obj = self.fc.collection.get_by_name(cnc_name) cnc_obj = self.fc.collection.get_by_name(cnc_name)
self.assertTrue(isinstance(cnc_obj, FlatCAMCNCjob), self.assertTrue(isinstance(cnc_obj, FlatCAMCNCjob),
"Expected a FlatCAMCNCJob, got %s" % type(geo_obj)) "Expected a FlatCAMCNCJob, got %s" % type(geo_obj))
#----------------------------------------- # -----------------------------------------
# Export G-Code, check output # Export G-Code, check output
#----------------------------------------- # -----------------------------------------
assert isinstance(cnc_obj, FlatCAMCNCjob) assert isinstance(cnc_obj, FlatCAMCNCjob)
output_filename = "" output_filename = ""
# get system temporary file(try create it and delete also) # get system temporary file(try create it and delete also)

View File

@ -3,6 +3,7 @@ import unittest
from shapely.geometry import LineString, Polygon from shapely.geometry import LineString, Polygon
from shapely.ops import cascaded_union, unary_union from shapely.ops import cascaded_union, unary_union
from matplotlib.pyplot import plot, subplot, show, cla, clf, xlim, ylim, title from matplotlib.pyplot import plot, subplot, show, cla, clf, xlim, ylim, title
from matplotlib.axes import *
from camlib import * from camlib import *
from copy import deepcopy from copy import deepcopy

View File

@ -84,5 +84,6 @@ class PathConnectTest1(unittest.TestCase):
matches = [p for p in result if p.equals(LineString([[0, 0], [1, 1], [2, 1]]))] matches = [p for p in result if p.equals(LineString([[0, 0], [1, 1], [2, 1]]))]
self.assertEqual(len(matches), 1) self.assertEqual(len(matches), 1)
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()

View File

@ -1,9 +1,9 @@
import sys import sys
import unittest import unittest
from PyQt4 import QtGui from PyQt5 import QtGui, QtWidgets
from FlatCAMApp import App from FlatCAMApp import App
from FlatCAMObj import FlatCAMGeometry, FlatCAMCNCjob from FlatCAMObj import FlatCAMGeometry, FlatCAMCNCjob
from ObjectUI import GerberObjectUI, GeometryObjectUI from flatcamGUI.ObjectUI import GerberObjectUI, GeometryObjectUI
from time import sleep from time import sleep
import os import os
import tempfile import tempfile
@ -13,7 +13,7 @@ from shapely.geometry import LineString, LinearRing, Polygon, MultiPolygon
class PolyPaintTestCase(unittest.TestCase): class PolyPaintTestCase(unittest.TestCase):
def setUp(self): def setUp(self):
self.app = QtGui.QApplication(sys.argv) self.app = QtWidgets.QApplication(sys.argv)
# Create App, keep app defaults (do not load # Create App, keep app defaults (do not load
# user-defined defaults). # user-defined defaults).
@ -218,10 +218,3 @@ class PolyPaintTestCase(unittest.TestCase):
self.assertTrue(isinstance(geo, LineString)) self.assertTrue(isinstance(geo, LineString))
# Lots of points (Should be 1000s) # Lots of points (Should be 1000s)
self.assertGreater(len(geo.coords), 2) self.assertGreater(len(geo.coords), 2)

View File

@ -1,9 +1,9 @@
import sys import sys
import unittest import unittest
from PyQt4 import QtGui from PyQt5 import QtWidgets
from FlatCAMApp import App from FlatCAMApp import App
from FlatCAMObj import FlatCAMGeometry, FlatCAMCNCjob from FlatCAMObj import FlatCAMGeometry, FlatCAMCNCjob
from ObjectUI import GerberObjectUI, GeometryObjectUI from flatcamGUI.ObjectUI import GerberObjectUI, GeometryObjectUI
from time import sleep from time import sleep
import os import os
import tempfile import tempfile
@ -12,7 +12,7 @@ import tempfile
class SVGFlowTestCase(unittest.TestCase): class SVGFlowTestCase(unittest.TestCase):
def setUp(self): def setUp(self):
self.app = QtGui.QApplication(sys.argv) self.app = QtWidgets.QApplication(sys.argv)
# Create App, keep app defaults (do not load # Create App, keep app defaults (do not load
# user-defined defaults). # user-defined defaults).
@ -31,30 +31,28 @@ class SVGFlowTestCase(unittest.TestCase):
names = self.fc.collection.get_names() names = self.fc.collection.get_names()
print(names) print(names)
#-------------------------------------- # --------------------------------------
# Total of 1 objects. # Total of 1 objects.
#-------------------------------------- # --------------------------------------
self.assertEqual(len(names), 1, self.assertEqual(len(names), 1, "Expected 1 object, found %d" % len(names))
"Expected 1 object, found %d" % len(names))
#-------------------------------------- # --------------------------------------
# Object's name matches the file name. # Object's name matches the file name.
#-------------------------------------- # --------------------------------------
self.assertEqual(names[0], self.filename, self.assertEqual(names[0], self.filename, "Expected name == %s, got %s" % (self.filename, names[0]))
"Expected name == %s, got %s" % (self.filename, names[0]))
#--------------------------------------- # ---------------------------------------
# Get object by that name, make sure it's a FlatCAMGerber. # Get object by that name, make sure it's a FlatCAMGerber.
#--------------------------------------- # ---------------------------------------
geo_name = names[0] geo_name = names[0]
geo_obj = self.fc.collection.get_by_name(geo_name) geo_obj = self.fc.collection.get_by_name(geo_name)
self.assertTrue(isinstance(geo_obj, FlatCAMGeometry), self.assertTrue(isinstance(geo_obj, FlatCAMGeometry),
"Expected FlatCAMGeometry, instead, %s is %s" % "Expected FlatCAMGeometry, instead, %s is %s" %
(geo_name, type(geo_obj))) (geo_name, type(geo_obj)))
#---------------------------------------- # ----------------------------------------
# Object's GUI matches Object's options # Object's GUI matches Object's options
#---------------------------------------- # ----------------------------------------
# TODO: Open GUI with double-click on object. # TODO: Open GUI with double-click on object.
# Opens the Object's GUI, populates it. # Opens the Object's GUI, populates it.
geo_obj.build_ui() geo_obj.build_ui()
@ -72,9 +70,9 @@ class SVGFlowTestCase(unittest.TestCase):
option, value, form_field.get_value() option, value, form_field.get_value()
)) ))
#------------------------------------ # ------------------------------------
# Open the UI, make CNCObject # Open the UI, make CNCObject
#------------------------------------ # ------------------------------------
geo_obj.build_ui() geo_obj.build_ui()
ui = geo_obj.ui ui = geo_obj.ui
assert isinstance(ui, GeometryObjectUI) # Just for the IDE assert isinstance(ui, GeometryObjectUI) # Just for the IDE
@ -91,30 +89,30 @@ class SVGFlowTestCase(unittest.TestCase):
sleep(0.1) sleep(0.1)
self.app.processEvents() self.app.processEvents()
#--------------------------------------------- # ---------------------------------------------
# Check that only 1 object has been created. # Check that only 1 object has been created.
#--------------------------------------------- # ---------------------------------------------
names = self.fc.collection.get_names() names = self.fc.collection.get_names()
self.assertEqual(len(names), 2, self.assertEqual(len(names), 2,
"Expected 2 objects, found %d" % len(names)) "Expected 2 objects, found %d" % len(names))
#------------------------------------------------------- # -------------------------------------------------------
# Make sure the CNC Job Object has the correct name # Make sure the CNC Job Object has the correct name
#------------------------------------------------------- # -------------------------------------------------------
cnc_name = geo_name + "_cnc" cnc_name = geo_name + "_cnc"
self.assertTrue(cnc_name in names, self.assertTrue(cnc_name in names,
"Object named %s not found." % geo_name) "Object named %s not found." % geo_name)
#------------------------------------------------------- # -------------------------------------------------------
# Get the object make sure it's a CNC Job object # Get the object make sure it's a CNC Job object
#------------------------------------------------------- # -------------------------------------------------------
cnc_obj = self.fc.collection.get_by_name(cnc_name) cnc_obj = self.fc.collection.get_by_name(cnc_name)
self.assertTrue(isinstance(cnc_obj, FlatCAMCNCjob), self.assertTrue(isinstance(cnc_obj, FlatCAMCNCjob),
"Expected a FlatCAMCNCJob, got %s" % type(geo_obj)) "Expected a FlatCAMCNCJob, got %s" % type(geo_obj))
#----------------------------------------- # -----------------------------------------
# Export G-Code, check output # Export G-Code, check output
#----------------------------------------- # -----------------------------------------
assert isinstance(cnc_obj, FlatCAMCNCjob) assert isinstance(cnc_obj, FlatCAMCNCjob)
output_filename = "" output_filename = ""
# get system temporary file(try create it and delete also) # get system temporary file(try create it and delete also)

View File

@ -1,13 +1,13 @@
import sys import sys
import unittest import unittest
from PyQt4 import QtGui from PyQt5 import QtWidgets, QtGui
from PyQt4.QtCore import QThread from PyQt5.QtCore import QThread
from FlatCAMApp import App from FlatCAMApp import App
from os import listdir from os import listdir
from os.path import isfile from os.path import isfile
from FlatCAMObj import FlatCAMGerber, FlatCAMGeometry, FlatCAMCNCjob, FlatCAMExcellon from FlatCAMObj import FlatCAMGerber, FlatCAMGeometry, FlatCAMCNCjob, FlatCAMExcellon
from ObjectUI import GerberObjectUI, GeometryObjectUI from flatcamGUI.ObjectUI import GerberObjectUI, GeometryObjectUI
from time import sleep from time import sleep
import os import os
import tempfile import tempfile
@ -36,13 +36,13 @@ class TclShellTest(unittest.TestCase):
# reason for this is reuse one test window only, # reason for this is reuse one test window only,
# CANNOT DO THIS HERE!!! # CANNOT DO THIS HERE!!!
#from tests.test_tclCommands import * # from tests.test_tclCommands import *
@classmethod @classmethod
def setUpClass(cls): def setUpClass(cls):
cls.setup = True cls.setup = True
cls.app = QtGui.QApplication(sys.argv) cls.app = QtWidgets.QApplication(sys.argv)
# Create App, keep app defaults (do not load # Create App, keep app defaults (do not load
# user-defined defaults). # user-defined defaults).
@ -78,9 +78,9 @@ class TclShellTest(unittest.TestCase):
self.fc.exec_command_test('set_sys units IN') self.fc.exec_command_test('set_sys units IN')
self.fc.exec_command_test('new') self.fc.exec_command_test('new')
#---------------------------------------- # ----------------------------------------
# Units must be IN # Units must be IN
#---------------------------------------- # ----------------------------------------
units = self.fc.exec_command_test('get_sys units') units = self.fc.exec_command_test('get_sys units')
self.assertEqual(units, "IN") self.assertEqual(units, "IN")
@ -88,9 +88,9 @@ class TclShellTest(unittest.TestCase):
self.fc.exec_command_test('set_sys units MM') self.fc.exec_command_test('set_sys units MM')
self.fc.exec_command_test('new') self.fc.exec_command_test('new')
#---------------------------------------- # ----------------------------------------
# Units must be MM # Units must be MM
#---------------------------------------- # ----------------------------------------
units = self.fc.exec_command_test('get_sys units') units = self.fc.exec_command_test('get_sys units')
self.assertEqual(units, "MM") self.assertEqual(units, "MM")
@ -103,9 +103,9 @@ class TclShellTest(unittest.TestCase):
gbr_cmd = 'open_gerber {path}/{filename} -outname {outname}' gbr_cmd = 'open_gerber {path}/{filename} -outname {outname}'
#----------------------------------------- # -----------------------------------------
# Open top layer and check for object type # Open top layer and check for object type
#----------------------------------------- # -----------------------------------------
cmd = gbr_cmd.format( cmd = gbr_cmd.format(
path=self.gerber_files, path=self.gerber_files,
filename=self.copper_top_filename, filename=self.copper_top_filename,
@ -116,9 +116,9 @@ class TclShellTest(unittest.TestCase):
"Expected FlatCAMGerber, instead, %s is %s" % "Expected FlatCAMGerber, instead, %s is %s" %
(self.gerber_top_name, type(gerber_top_obj))) (self.gerber_top_name, type(gerber_top_obj)))
#-------------------------------------------- # --------------------------------------------
# Open bottom layer and check for object type # Open bottom layer and check for object type
#-------------------------------------------- # --------------------------------------------
cmd = gbr_cmd.format( cmd = gbr_cmd.format(
path=self.gerber_files, path=self.gerber_files,
filename=self.copper_bottom_filename, filename=self.copper_bottom_filename,
@ -129,9 +129,9 @@ class TclShellTest(unittest.TestCase):
"Expected FlatCAMGerber, instead, %s is %s" % "Expected FlatCAMGerber, instead, %s is %s" %
(self.gerber_bottom_name, type(gerber_bottom_obj))) (self.gerber_bottom_name, type(gerber_bottom_obj)))
#-------------------------------------------- # --------------------------------------------
# Open cutout layer and check for object type # Open cutout layer and check for object type
#-------------------------------------------- # --------------------------------------------
cmd = gbr_cmd.format( cmd = gbr_cmd.format(
path=self.gerber_files, path=self.gerber_files,
filename=self.cutout_filename, filename=self.cutout_filename,
@ -160,9 +160,9 @@ class TclShellTest(unittest.TestCase):
# TODO: Check deleteb object is gone. # TODO: Check deleteb object is gone.
#-------------------------------------------- # --------------------------------------------
# Exteriors of cutout layer, check type # Exteriors of cutout layer, check type
#-------------------------------------------- # --------------------------------------------
obj = self.fc.collection.get_by_name(self.gerber_cutout_name + '_iso_exterior') obj = self.fc.collection.get_by_name(self.gerber_cutout_name + '_iso_exterior')
self.assertTrue(isinstance(obj, FlatCAMGeometry), self.assertTrue(isinstance(obj, FlatCAMGeometry),
"Expected FlatCAMGeometry, instead, %s is %s" % "Expected FlatCAMGeometry, instead, %s is %s" %
@ -173,8 +173,14 @@ class TclShellTest(unittest.TestCase):
self.fc.exec_command_test('mirror %s -box %s -axis X' % (self.gerber_cutout_name, self.gerber_cutout_name)) self.fc.exec_command_test('mirror %s -box %s -axis X' % (self.gerber_cutout_name, self.gerber_cutout_name))
# exteriors delete and join geometries for bottom layer # exteriors delete and join geometries for bottom layer
self.fc.exec_command_test('isolate %s -dia %f -outname %s' % (self.gerber_cutout_name, self.engraver_diameter, self.gerber_cutout_name + '_bottom_iso')) self.fc.exec_command_test(
self.fc.exec_command_test('exteriors %s -outname %s' % (self.gerber_cutout_name + '_bottom_iso', self.gerber_cutout_name + '_bottom_iso_exterior')) 'isolate %s -dia %f -outname %s' %
(self.gerber_cutout_name, self.engraver_diameter, self.gerber_cutout_name + '_bottom_iso')
)
self.fc.exec_command_test(
'exteriors %s -outname %s' %
(self.gerber_cutout_name + '_bottom_iso', self.gerber_cutout_name + '_bottom_iso_exterior')
)
self.fc.exec_command_test('delete %s' % (self.gerber_cutout_name + '_bottom_iso')) self.fc.exec_command_test('delete %s' % (self.gerber_cutout_name + '_bottom_iso'))
obj = self.fc.collection.get_by_name(self.gerber_cutout_name + '_bottom_iso_exterior') obj = self.fc.collection.get_by_name(self.gerber_cutout_name + '_bottom_iso_exterior')
self.assertTrue(isinstance(obj, FlatCAMGeometry), self.assertTrue(isinstance(obj, FlatCAMGeometry),
@ -187,12 +193,20 @@ class TclShellTest(unittest.TestCase):
"Expected 5 objects, found %d" % len(names)) "Expected 5 objects, found %d" % len(names))
# isolate traces # isolate traces
self.fc.exec_command_test('isolate %s -dia %f' % (self.gerber_top_name, self.engraver_diameter)) self.fc.exec_command_test('isolate %s -dia %f' % (self.gerber_top_name, self.engraver_diameter))
self.fc.exec_command_test('isolate %s -dia %f' % (self.gerber_bottom_name, self.engraver_diameter)) self.fc.exec_command_test('isolate %s -dia %f' % (self.gerber_bottom_name, self.engraver_diameter))
# join isolated geometries for top and bottom # join isolated geometries for top and bottom
self.fc.exec_command_test('join_geometries %s %s %s' % (self.gerber_top_name + '_join_iso', self.gerber_top_name + '_iso', self.gerber_cutout_name + '_iso_exterior')) self.fc.exec_command_test(
self.fc.exec_command_test('join_geometries %s %s %s' % (self.gerber_bottom_name + '_join_iso', self.gerber_bottom_name + '_iso', self.gerber_cutout_name + '_bottom_iso_exterior')) 'join_geometries %s %s %s' %
(self.gerber_top_name + '_join_iso', self.gerber_top_name + '_iso',
self.gerber_cutout_name + '_iso_exterior')
)
self.fc.exec_command_test(
'join_geometries %s %s %s' %
(self.gerber_bottom_name + '_join_iso', self.gerber_bottom_name + '_iso',
self.gerber_cutout_name + '_bottom_iso_exterior')
)
# at this stage we should have 9 objects # at this stage we should have 9 objects
names = self.fc.collection.get_names() names = self.fc.collection.get_names()
@ -211,14 +225,21 @@ class TclShellTest(unittest.TestCase):
"Expected 5 objects, found %d" % len(names)) "Expected 5 objects, found %d" % len(names))
# geocutout bottom test (it cuts to same object) # geocutout bottom test (it cuts to same object)
self.fc.exec_command_test('isolate %s -dia %f -outname %s' % (self.gerber_cutout_name, self.cutout_diameter, self.gerber_cutout_name + '_bottom_iso')) self.fc.exec_command_test(
self.fc.exec_command_test('exteriors %s -outname %s' % (self.gerber_cutout_name + '_bottom_iso', self.gerber_cutout_name + '_bottom_iso_exterior')) 'isolate %s -dia %f -outname %s' %
(self.gerber_cutout_name, self.cutout_diameter, self.gerber_cutout_name + '_bottom_iso')
)
self.fc.exec_command_test(
'exteriors %s -outname %s' %
(self.gerber_cutout_name + '_bottom_iso', self.gerber_cutout_name + '_bottom_iso_exterior')
)
self.fc.exec_command_test('delete %s' % (self.gerber_cutout_name + '_bottom_iso')) self.fc.exec_command_test('delete %s' % (self.gerber_cutout_name + '_bottom_iso'))
obj = self.fc.collection.get_by_name(self.gerber_cutout_name + '_bottom_iso_exterior') obj = self.fc.collection.get_by_name(self.gerber_cutout_name + '_bottom_iso_exterior')
self.assertTrue(isinstance(obj, FlatCAMGeometry), self.assertTrue(isinstance(obj, FlatCAMGeometry),
"Expected FlatCAMGeometry, instead, %s is %s" % "Expected FlatCAMGeometry, instead, %s is %s" %
(self.gerber_cutout_name + '_bottom_iso_exterior', type(obj))) (self.gerber_cutout_name + '_bottom_iso_exterior', type(obj)))
self.fc.exec_command_test('geocutout %s -dia %f -gapsize 0.3 -gaps 4' % (self.gerber_cutout_name + '_bottom_iso_exterior', self.cutout_diameter)) self.fc.exec_command_test('geocutout %s -dia %f -gapsize 0.3 -gaps 4' %
(self.gerber_cutout_name + '_bottom_iso_exterior', self.cutout_diameter))
# at this stage we should have 6 objects # at this stage we should have 6 objects
names = self.fc.collection.get_names() names = self.fc.collection.get_names()
@ -229,7 +250,8 @@ class TclShellTest(unittest.TestCase):
def test_open_gerber(self): def test_open_gerber(self):
self.fc.exec_command_test('open_gerber %s/%s -outname %s' % (self.gerber_files, self.copper_top_filename, self.gerber_top_name)) self.fc.exec_command_test('open_gerber %s/%s -outname %s' %
(self.gerber_files, self.copper_top_filename, self.gerber_top_name))
gerber_top_obj = self.fc.collection.get_by_name(self.gerber_top_name) gerber_top_obj = self.fc.collection.get_by_name(self.gerber_top_name)
self.assertTrue(isinstance(gerber_top_obj, FlatCAMGerber), self.assertTrue(isinstance(gerber_top_obj, FlatCAMGerber),
"Expected FlatCAMGerber, instead, %s is %s" % "Expected FlatCAMGerber, instead, %s is %s" %
@ -237,7 +259,8 @@ class TclShellTest(unittest.TestCase):
def test_excellon_flow(self): def test_excellon_flow(self):
self.fc.exec_command_test('open_excellon %s/%s -outname %s' % (self.gerber_files, self.excellon_filename, self.excellon_name)) self.fc.exec_command_test('open_excellon %s/%s -outname %s' %
(self.gerber_files, self.excellon_filename, self.excellon_name))
excellon_obj = self.fc.collection.get_by_name(self.excellon_name) excellon_obj = self.fc.collection.get_by_name(self.excellon_name)
self.assertTrue(isinstance(excellon_obj, FlatCAMExcellon), self.assertTrue(isinstance(excellon_obj, FlatCAMExcellon),
"Expected FlatCAMExcellon, instead, %s is %s" % "Expected FlatCAMExcellon, instead, %s is %s" %