- 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:
parent
2c536258ed
commit
a6b89dbf3a
|
@ -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.
|
||||
"""
|
||||
|
|
|
@ -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
375
camlib.py
|
@ -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.
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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_()
|
|
@ -4,7 +4,7 @@
|
|||
import sys
|
||||
sys.path.append('../../')
|
||||
|
||||
from camlib import *
|
||||
from flatcamParsers.ParseGerber import *
|
||||
|
||||
log = logging.getLogger('base2')
|
||||
log.setLevel(logging.WARNING)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
|
@ -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_())
|
|
@ -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')
|
||||
|
|
|
@ -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-')
|
||||
|
|
|
@ -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 *
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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" %
|
||||
|
|
Loading…
Reference in New Issue