- 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 FlatCAMWorkerStack import WorkerStack
from flatcamGUI.VisPyVisuals import Color
# from flatcamGUI.VisPyVisuals import Color
from vispy.gloo.util import _screenshot
from vispy.io import write_png
@ -71,12 +71,12 @@ fcTranslate.apply_language('strings')
if '_' not in builtins.__dict__:
_ = gettext.gettext
# ########################################
# # App ###
# ########################################
class App(QtCore.QObject):
# ########################################
# # App ###
# ########################################
"""
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 Excellon and Gerber classes from camlib into their own files in the flatcamParser folder
- 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

375
camlib.py
View File

@ -49,7 +49,7 @@ import ezdxf
# TODO: Commented for FlatCAM packaging with cx_freeze
# from scipy.spatial import KDTree, Delaunay
# from scipy.spatial import Delaunay
from flatcamParsers.ParseGerber import ApertureMacro
from flatcamParsers.ParseSVG import *
from flatcamParsers.ParseDXF import *
@ -82,6 +82,379 @@ class ParseError(Exception):
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):
"""
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__:
_ = 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):
"""
@ -265,7 +289,7 @@ class Excellon(Geometry):
line_units = ''
#### Parsing starts here ## ##
# ## Parsing starts here ## ##
line_num = 0 # Line number
eline = ""
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__:
_ = 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):
"""
@ -1134,25 +1160,25 @@ class Gerber(Geometry):
try:
circular_x = parse_gerber_number(circular_x,
self.int_digits, self.frac_digits, self.gerber_zeros)
except:
except Exception as e:
circular_x = current_x
try:
circular_y = parse_gerber_number(circular_y,
self.int_digits, self.frac_digits, self.gerber_zeros)
except:
except Exception as e:
circular_y = current_y
# 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
try:
i = parse_gerber_number(i, self.int_digits, self.frac_digits, self.gerber_zeros)
except:
except Exception as e:
i = 0
try:
j = parse_gerber_number(j, self.int_digits, self.frac_digits, self.gerber_zeros)
except:
except Exception as e:
j = 0
if quadrant_mode is None:
@ -1598,6 +1624,7 @@ class Gerber(Geometry):
:type xfactor: float
:param yfactor: Number by which to scale on Y axis.
:type yfactor: float
:param point: reference point for scaling operation
:rtype : None
"""
log.debug("camlib.Gerber.scale()")
@ -1628,7 +1655,7 @@ class Gerber(Geometry):
# variables to display the percentage of work done
self.geo_len = 0
try:
for g in self.solid_geometry:
for __ in self.solid_geometry:
self.geo_len += 1
except TypeError:
self.geo_len = 1
@ -1717,7 +1744,7 @@ class Gerber(Geometry):
# variables to display the percentage of work done
self.geo_len = 0
try:
for g in self.solid_geometry:
for __ in self.solid_geometry:
self.geo_len += 1
except TypeError:
self.geo_len = 1
@ -1796,7 +1823,7 @@ class Gerber(Geometry):
# variables to display the percentage of work done
self.geo_len = 0
try:
for g in self.solid_geometry:
for __ in self.solid_geometry:
self.geo_len += 1
except TypeError:
self.geo_len = 1
@ -1857,6 +1884,10 @@ class Gerber(Geometry):
See shapely manual for more information:
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()")
@ -1865,7 +1896,7 @@ class Gerber(Geometry):
# variables to display the percentage of work done
self.geo_len = 0
try:
for g in self.solid_geometry:
for __ in self.solid_geometry:
self.geo_len += 1
except TypeError:
self.geo_len = 1
@ -1926,7 +1957,7 @@ class Gerber(Geometry):
# variables to display the percentage of work done
self.geo_len = 0
try:
for g in self.solid_geometry:
for __ in self.solid_geometry:
self.geo_len += 1
except TypeError:
self.geo_len = 1
@ -1972,376 +2003,3 @@ class Gerber(Geometry):
self.app.inform.emit('[success] %s' %
_("Gerber Rotate done."))
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.QtCore import Qt
class TitleBar(QtWidgets.QDialog):
def __init__(self, parent=None):
QtWidgets.QWidget.__init__(self, parent)
self.setWindowFlags(Qt.FramelessWindowHint);
self.setWindowFlags(Qt.FramelessWindowHint)
css = """
QWidget{
Background: #AA00AA;
@ -60,19 +61,19 @@ class TitleBar(QtWidgets.QDialog):
self.maximize.clicked.connect(self.showMaxRestore)
def showSmall(self):
box.showMinimized();
box.showMinimized()
def showMaxRestore(self):
if(self.maxNormal):
box.showNormal();
self.maxNormal= False;
self.maximize.setIcon(QtGui.QIcon('img/max.png'));
box.showNormal()
self.maxNormal= False
self.maximize.setIcon(QtGui.QIcon('img/max.png'))
print(1)
else:
box.showMaximized();
self.maxNormal= True;
box.showMaximized()
self.maxNormal= True
print(2)
self.maximize.setIcon(QtGui.QIcon('img/max2.png'));
self.maximize.setIcon(QtGui.QIcon('img/max2.png'))
def close(self):
box.close()
@ -88,7 +89,7 @@ class TitleBar(QtWidgets.QDialog):
class Frame(QtWidgets.QFrame):
def __init__(self, parent=None):
QtWidgets.QFrame.__init__(self, parent)
self.m_mouse_down= False;
self.m_mouse_down= False
self.setFrameShape(QtWidgets.QFrame.StyledPanel)
css = """
QFrame{
@ -122,25 +123,26 @@ class Frame(QtWidgets.QFrame):
return self.m_titleBar
def mousePressEvent(self,event):
self.m_old_pos = event.pos();
self.m_mouse_down = event.button()== Qt.LeftButton;
self.m_old_pos = event.pos()
self.m_mouse_down = event.button()== Qt.LeftButton
def mouseMoveEvent(self,event):
x=event.x()
y=event.y()
def mouseReleaseEvent(self,event):
m_mouse_down=False;
m_mouse_down=False
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv);
app = QtWidgets.QApplication(sys.argv)
box = Frame()
box.move(60,60)
l=QtWidgets.QVBoxLayout(box.contentWidget());
l = QtWidgets.QVBoxLayout(box.contentWidget())
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
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)
box.show()
app.exec_()

View File

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

View File

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

View File

@ -2,6 +2,7 @@ import sys
from PyQt5.Qt import *
from PyQt5 import QtGui, QtWidgets
class MyPopup(QWidget):
def __init__(self):
QWidget.__init__(self)
@ -30,6 +31,7 @@ class MyPopup(QWidget):
# dc.drawLine(0, 0, 100, 100)
# dc.drawLine(100, 0, 0, 100)
class MainWindow(QMainWindow):
def __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.show()
class App(QApplication):
def __init__(self, *args):
QtWidgets.QApplication.__init__(self, *args)
@ -56,10 +59,12 @@ class App(QApplication):
def byebye(self):
self.exit(0)
def main(args):
global app
app = App(args)
app.exec_()
if __name__ == "__main__":
main(sys.argv)

View File

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

View File

@ -1,7 +1,7 @@
import os
os.chdir('../')
from camlib import *
from flatcamParsers.ParseGerber import *
g = Gerber()
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
os.chdir('../')
from camlib import *
#from matplotlib.figure import Figure
from flatcamParsers.ParseGerber import *
from flatcamParsers.ParseExcellon import *
from matplotlib import pyplot
# 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"
g = Gerber()
g.parse_file(gerber_filename)
@ -32,7 +33,7 @@ ax.set_aspect(1)
# Plot gerber
for geo in g.solid_geometry:
x, y = geo.exterior.coords.xy
plot(x, y, 'k-')
pyplot.plot(x, y, 'k-')
for ints in geo.interiors:
x, y = ints.coords.xy
ax.plot(x, y, 'k-')
@ -40,7 +41,7 @@ for geo in g.solid_geometry:
# Plot excellon
for geo in ex.solid_geometry:
x, y = geo.exterior.coords.xy
plot(x, y, 'r-')
pyplot.plot(x, y, 'r-')
for ints in geo.interiors:
x, y = ints.coords.xy
ax.plot(x, y, 'g-')

View File

@ -1,6 +1,7 @@
from shapely.geometry import LineString, Polygon
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 *

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,13 +1,13 @@
import sys
import unittest
from PyQt4 import QtGui
from PyQt4.QtCore import QThread
from PyQt5 import QtWidgets, QtGui
from PyQt5.QtCore import QThread
from FlatCAMApp import App
from os import listdir
from os.path import isfile
from FlatCAMObj import FlatCAMGerber, FlatCAMGeometry, FlatCAMCNCjob, FlatCAMExcellon
from ObjectUI import GerberObjectUI, GeometryObjectUI
from flatcamGUI.ObjectUI import GerberObjectUI, GeometryObjectUI
from time import sleep
import os
import tempfile
@ -36,13 +36,13 @@ class TclShellTest(unittest.TestCase):
# reason for this is reuse one test window only,
# CANNOT DO THIS HERE!!!
#from tests.test_tclCommands import *
# from tests.test_tclCommands import *
@classmethod
def setUpClass(cls):
cls.setup = True
cls.app = QtGui.QApplication(sys.argv)
cls.app = QtWidgets.QApplication(sys.argv)
# Create App, keep app defaults (do not load
# 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('new')
#----------------------------------------
# ----------------------------------------
# Units must be IN
#----------------------------------------
# ----------------------------------------
units = self.fc.exec_command_test('get_sys units')
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('new')
#----------------------------------------
# ----------------------------------------
# Units must be MM
#----------------------------------------
# ----------------------------------------
units = self.fc.exec_command_test('get_sys units')
self.assertEqual(units, "MM")
@ -103,9 +103,9 @@ class TclShellTest(unittest.TestCase):
gbr_cmd = 'open_gerber {path}/{filename} -outname {outname}'
#-----------------------------------------
# -----------------------------------------
# Open top layer and check for object type
#-----------------------------------------
# -----------------------------------------
cmd = gbr_cmd.format(
path=self.gerber_files,
filename=self.copper_top_filename,
@ -116,9 +116,9 @@ class TclShellTest(unittest.TestCase):
"Expected FlatCAMGerber, instead, %s is %s" %
(self.gerber_top_name, type(gerber_top_obj)))
#--------------------------------------------
# --------------------------------------------
# Open bottom layer and check for object type
#--------------------------------------------
# --------------------------------------------
cmd = gbr_cmd.format(
path=self.gerber_files,
filename=self.copper_bottom_filename,
@ -129,9 +129,9 @@ class TclShellTest(unittest.TestCase):
"Expected FlatCAMGerber, instead, %s is %s" %
(self.gerber_bottom_name, type(gerber_bottom_obj)))
#--------------------------------------------
# --------------------------------------------
# Open cutout layer and check for object type
#--------------------------------------------
# --------------------------------------------
cmd = gbr_cmd.format(
path=self.gerber_files,
filename=self.cutout_filename,
@ -160,9 +160,9 @@ class TclShellTest(unittest.TestCase):
# TODO: Check deleteb object is gone.
#--------------------------------------------
# --------------------------------------------
# Exteriors of cutout layer, check type
#--------------------------------------------
# --------------------------------------------
obj = self.fc.collection.get_by_name(self.gerber_cutout_name + '_iso_exterior')
self.assertTrue(isinstance(obj, FlatCAMGeometry),
"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))
# 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('exteriors %s -outname %s' % (self.gerber_cutout_name + '_bottom_iso', self.gerber_cutout_name + '_bottom_iso_exterior'))
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(
'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'))
obj = self.fc.collection.get_by_name(self.gerber_cutout_name + '_bottom_iso_exterior')
self.assertTrue(isinstance(obj, FlatCAMGeometry),
@ -187,12 +193,20 @@ class TclShellTest(unittest.TestCase):
"Expected 5 objects, found %d" % len(names))
# 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_bottom_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))
# 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('join_geometries %s %s %s' % (self.gerber_bottom_name + '_join_iso', self.gerber_bottom_name + '_iso', self.gerber_cutout_name + '_bottom_iso_exterior'))
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(
'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
names = self.fc.collection.get_names()
@ -211,14 +225,21 @@ class TclShellTest(unittest.TestCase):
"Expected 5 objects, found %d" % len(names))
# 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('exteriors %s -outname %s' % (self.gerber_cutout_name + '_bottom_iso', self.gerber_cutout_name + '_bottom_iso_exterior'))
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(
'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'))
obj = self.fc.collection.get_by_name(self.gerber_cutout_name + '_bottom_iso_exterior')
self.assertTrue(isinstance(obj, FlatCAMGeometry),
"Expected FlatCAMGeometry, instead, %s is %s" %
(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
names = self.fc.collection.get_names()
@ -229,7 +250,8 @@ class TclShellTest(unittest.TestCase):
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)
self.assertTrue(isinstance(gerber_top_obj, FlatCAMGerber),
"Expected FlatCAMGerber, instead, %s is %s" %
@ -237,7 +259,8 @@ class TclShellTest(unittest.TestCase):
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)
self.assertTrue(isinstance(excellon_obj, FlatCAMExcellon),
"Expected FlatCAMExcellon, instead, %s is %s" %