- 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 FlatCAMProcess import *
|
||||||
from FlatCAMWorkerStack import WorkerStack
|
from FlatCAMWorkerStack import WorkerStack
|
||||||
from flatcamGUI.VisPyVisuals import Color
|
# from flatcamGUI.VisPyVisuals import Color
|
||||||
from vispy.gloo.util import _screenshot
|
from vispy.gloo.util import _screenshot
|
||||||
from vispy.io import write_png
|
from vispy.io import write_png
|
||||||
|
|
||||||
|
@ -71,12 +71,12 @@ fcTranslate.apply_language('strings')
|
||||||
if '_' not in builtins.__dict__:
|
if '_' not in builtins.__dict__:
|
||||||
_ = gettext.gettext
|
_ = gettext.gettext
|
||||||
|
|
||||||
# ########################################
|
|
||||||
# # App ###
|
|
||||||
# ########################################
|
|
||||||
|
|
||||||
|
|
||||||
class App(QtCore.QObject):
|
class App(QtCore.QObject):
|
||||||
|
# ########################################
|
||||||
|
# # App ###
|
||||||
|
# ########################################
|
||||||
|
|
||||||
"""
|
"""
|
||||||
The main application class. The constructor starts the GUI.
|
The main application class. The constructor starts the GUI.
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -17,6 +17,8 @@ CAD program, and create G-Code for Isolation routing.
|
||||||
- made the Rules Check Tool document window Read Only
|
- made the Rules Check Tool document window Read Only
|
||||||
- made Excellon and Gerber classes from camlib into their own files in the flatcamParser folder
|
- made Excellon and Gerber classes from camlib into their own files in the flatcamParser folder
|
||||||
- moved the ApertureMacro class from camlib to ParseGerber file
|
- moved the ApertureMacro class from camlib to ParseGerber file
|
||||||
|
- moved back the ApertureMacro class to camlib for now and made some import changes in the new ParseGerber and ParseExcellon classes
|
||||||
|
- some changes to the tests - perhaps I will try adding a few tests in the future
|
||||||
|
|
||||||
5.10.2019
|
5.10.2019
|
||||||
|
|
||||||
|
|
375
camlib.py
375
camlib.py
|
@ -49,7 +49,7 @@ import ezdxf
|
||||||
# TODO: Commented for FlatCAM packaging with cx_freeze
|
# TODO: Commented for FlatCAM packaging with cx_freeze
|
||||||
# from scipy.spatial import KDTree, Delaunay
|
# from scipy.spatial import KDTree, Delaunay
|
||||||
# from scipy.spatial import Delaunay
|
# from scipy.spatial import Delaunay
|
||||||
from flatcamParsers.ParseGerber import ApertureMacro
|
|
||||||
from flatcamParsers.ParseSVG import *
|
from flatcamParsers.ParseSVG import *
|
||||||
from flatcamParsers.ParseDXF import *
|
from flatcamParsers.ParseDXF import *
|
||||||
|
|
||||||
|
@ -82,6 +82,379 @@ class ParseError(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class ApertureMacro:
|
||||||
|
"""
|
||||||
|
Syntax of aperture macros.
|
||||||
|
|
||||||
|
<AM command>: AM<Aperture macro name>*<Macro content>
|
||||||
|
<Macro content>: {{<Variable definition>*}{<Primitive>*}}
|
||||||
|
<Variable definition>: $K=<Arithmetic expression>
|
||||||
|
<Primitive>: <Primitive code>,<Modifier>{,<Modifier>}|<Comment>
|
||||||
|
<Modifier>: $M|< Arithmetic expression>
|
||||||
|
<Comment>: 0 <Text>
|
||||||
|
"""
|
||||||
|
|
||||||
|
# ## Regular expressions
|
||||||
|
am1_re = re.compile(r'^%AM([^\*]+)\*(.+)?(%)?$')
|
||||||
|
am2_re = re.compile(r'(.*)%$')
|
||||||
|
amcomm_re = re.compile(r'^0(.*)')
|
||||||
|
amprim_re = re.compile(r'^[1-9].*')
|
||||||
|
amvar_re = re.compile(r'^\$([0-9a-zA-z]+)=(.*)')
|
||||||
|
|
||||||
|
def __init__(self, name=None):
|
||||||
|
self.name = name
|
||||||
|
self.raw = ""
|
||||||
|
|
||||||
|
# ## These below are recomputed for every aperture
|
||||||
|
# ## definition, in other words, are temporary variables.
|
||||||
|
self.primitives = []
|
||||||
|
self.locvars = {}
|
||||||
|
self.geometry = None
|
||||||
|
|
||||||
|
def to_dict(self):
|
||||||
|
"""
|
||||||
|
Returns the object in a serializable form. Only the name and
|
||||||
|
raw are required.
|
||||||
|
|
||||||
|
:return: Dictionary representing the object. JSON ready.
|
||||||
|
:rtype: dict
|
||||||
|
"""
|
||||||
|
|
||||||
|
return {
|
||||||
|
'name': self.name,
|
||||||
|
'raw': self.raw
|
||||||
|
}
|
||||||
|
|
||||||
|
def from_dict(self, d):
|
||||||
|
"""
|
||||||
|
Populates the object from a serial representation created
|
||||||
|
with ``self.to_dict()``.
|
||||||
|
|
||||||
|
:param d: Serial representation of an ApertureMacro object.
|
||||||
|
:return: None
|
||||||
|
"""
|
||||||
|
for attr in ['name', 'raw']:
|
||||||
|
setattr(self, attr, d[attr])
|
||||||
|
|
||||||
|
def parse_content(self):
|
||||||
|
"""
|
||||||
|
Creates numerical lists for all primitives in the aperture
|
||||||
|
macro (in ``self.raw``) by replacing all variables by their
|
||||||
|
values iteratively and evaluating expressions. Results
|
||||||
|
are stored in ``self.primitives``.
|
||||||
|
|
||||||
|
:return: None
|
||||||
|
"""
|
||||||
|
# Cleanup
|
||||||
|
self.raw = self.raw.replace('\n', '').replace('\r', '').strip(" *")
|
||||||
|
self.primitives = []
|
||||||
|
|
||||||
|
# Separate parts
|
||||||
|
parts = self.raw.split('*')
|
||||||
|
|
||||||
|
# ### Every part in the macro ####
|
||||||
|
for part in parts:
|
||||||
|
# ## Comments. Ignored.
|
||||||
|
match = ApertureMacro.amcomm_re.search(part)
|
||||||
|
if match:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# ## Variables
|
||||||
|
# These are variables defined locally inside the macro. They can be
|
||||||
|
# numerical constant or defind in terms of previously define
|
||||||
|
# variables, which can be defined locally or in an aperture
|
||||||
|
# definition. All replacements ocurr here.
|
||||||
|
match = ApertureMacro.amvar_re.search(part)
|
||||||
|
if match:
|
||||||
|
var = match.group(1)
|
||||||
|
val = match.group(2)
|
||||||
|
|
||||||
|
# Replace variables in value
|
||||||
|
for v in self.locvars:
|
||||||
|
# replaced the following line with the next to fix Mentor custom apertures not parsed OK
|
||||||
|
# val = re.sub((r'\$'+str(v)+r'(?![0-9a-zA-Z])'), str(self.locvars[v]), val)
|
||||||
|
val = val.replace('$' + str(v), str(self.locvars[v]))
|
||||||
|
|
||||||
|
# Make all others 0
|
||||||
|
val = re.sub(r'\$[0-9a-zA-Z](?![0-9a-zA-Z])', "0", val)
|
||||||
|
# Change x with *
|
||||||
|
val = re.sub(r'[xX]', "*", val)
|
||||||
|
|
||||||
|
# Eval() and store.
|
||||||
|
self.locvars[var] = eval(val)
|
||||||
|
continue
|
||||||
|
|
||||||
|
# ## Primitives
|
||||||
|
# Each is an array. The first identifies the primitive, while the
|
||||||
|
# rest depend on the primitive. All are strings representing a
|
||||||
|
# number and may contain variable definition. The values of these
|
||||||
|
# variables are defined in an aperture definition.
|
||||||
|
match = ApertureMacro.amprim_re.search(part)
|
||||||
|
if match:
|
||||||
|
# ## Replace all variables
|
||||||
|
for v in self.locvars:
|
||||||
|
# replaced the following line with the next to fix Mentor custom apertures not parsed OK
|
||||||
|
# part = re.sub(r'\$' + str(v) + r'(?![0-9a-zA-Z])', str(self.locvars[v]), part)
|
||||||
|
part = part.replace('$' + str(v), str(self.locvars[v]))
|
||||||
|
|
||||||
|
# Make all others 0
|
||||||
|
part = re.sub(r'\$[0-9a-zA-Z](?![0-9a-zA-Z])', "0", part)
|
||||||
|
|
||||||
|
# Change x with *
|
||||||
|
part = re.sub(r'[xX]', "*", part)
|
||||||
|
|
||||||
|
# ## Store
|
||||||
|
elements = part.split(",")
|
||||||
|
self.primitives.append([eval(x) for x in elements])
|
||||||
|
continue
|
||||||
|
|
||||||
|
log.warning("Unknown syntax of aperture macro part: %s" % str(part))
|
||||||
|
|
||||||
|
def append(self, data):
|
||||||
|
"""
|
||||||
|
Appends a string to the raw macro.
|
||||||
|
|
||||||
|
:param data: Part of the macro.
|
||||||
|
:type data: str
|
||||||
|
:return: None
|
||||||
|
"""
|
||||||
|
self.raw += data
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def default2zero(n, mods):
|
||||||
|
"""
|
||||||
|
Pads the ``mods`` list with zeros resulting in an
|
||||||
|
list of length n.
|
||||||
|
|
||||||
|
:param n: Length of the resulting list.
|
||||||
|
:type n: int
|
||||||
|
:param mods: List to be padded.
|
||||||
|
:type mods: list
|
||||||
|
:return: Zero-padded list.
|
||||||
|
:rtype: list
|
||||||
|
"""
|
||||||
|
x = [0.0] * n
|
||||||
|
na = len(mods)
|
||||||
|
x[0:na] = mods
|
||||||
|
return x
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def make_circle(mods):
|
||||||
|
"""
|
||||||
|
|
||||||
|
:param mods: (Exposure 0/1, Diameter >=0, X-coord, Y-coord)
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
|
||||||
|
pol, dia, x, y = ApertureMacro.default2zero(4, mods)
|
||||||
|
|
||||||
|
return {"pol": int(pol), "geometry": Point(x, y).buffer(dia/2)}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def make_vectorline(mods):
|
||||||
|
"""
|
||||||
|
|
||||||
|
:param mods: (Exposure 0/1, Line width >= 0, X-start, Y-start, X-end, Y-end,
|
||||||
|
rotation angle around origin in degrees)
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
pol, width, xs, ys, xe, ye, angle = ApertureMacro.default2zero(7, mods)
|
||||||
|
|
||||||
|
line = LineString([(xs, ys), (xe, ye)])
|
||||||
|
box = line.buffer(width/2, cap_style=2)
|
||||||
|
box_rotated = affinity.rotate(box, angle, origin=(0, 0))
|
||||||
|
|
||||||
|
return {"pol": int(pol), "geometry": box_rotated}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def make_centerline(mods):
|
||||||
|
"""
|
||||||
|
|
||||||
|
:param mods: (Exposure 0/1, width >=0, height >=0, x-center, y-center,
|
||||||
|
rotation angle around origin in degrees)
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
|
||||||
|
pol, width, height, x, y, angle = ApertureMacro.default2zero(6, mods)
|
||||||
|
|
||||||
|
box = shply_box(x-width/2, y-height/2, x+width/2, y+height/2)
|
||||||
|
box_rotated = affinity.rotate(box, angle, origin=(0, 0))
|
||||||
|
|
||||||
|
return {"pol": int(pol), "geometry": box_rotated}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def make_lowerleftline(mods):
|
||||||
|
"""
|
||||||
|
|
||||||
|
:param mods: (exposure 0/1, width >=0, height >=0, x-lowerleft, y-lowerleft,
|
||||||
|
rotation angle around origin in degrees)
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
|
||||||
|
pol, width, height, x, y, angle = ApertureMacro.default2zero(6, mods)
|
||||||
|
|
||||||
|
box = shply_box(x, y, x+width, y+height)
|
||||||
|
box_rotated = affinity.rotate(box, angle, origin=(0, 0))
|
||||||
|
|
||||||
|
return {"pol": int(pol), "geometry": box_rotated}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def make_outline(mods):
|
||||||
|
"""
|
||||||
|
|
||||||
|
:param mods:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
|
||||||
|
pol = mods[0]
|
||||||
|
n = mods[1]
|
||||||
|
points = [(0, 0)]*(n+1)
|
||||||
|
|
||||||
|
for i in range(n+1):
|
||||||
|
points[i] = mods[2*i + 2:2*i + 4]
|
||||||
|
|
||||||
|
angle = mods[2*n + 4]
|
||||||
|
|
||||||
|
poly = Polygon(points)
|
||||||
|
poly_rotated = affinity.rotate(poly, angle, origin=(0, 0))
|
||||||
|
|
||||||
|
return {"pol": int(pol), "geometry": poly_rotated}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def make_polygon(mods):
|
||||||
|
"""
|
||||||
|
Note: Specs indicate that rotation is only allowed if the center
|
||||||
|
(x, y) == (0, 0). I will tolerate breaking this rule.
|
||||||
|
|
||||||
|
:param mods: (exposure 0/1, n_verts 3<=n<=12, x-center, y-center,
|
||||||
|
diameter of circumscribed circle >=0, rotation angle around origin)
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
|
||||||
|
pol, nverts, x, y, dia, angle = ApertureMacro.default2zero(6, mods)
|
||||||
|
points = [(0, 0)]*nverts
|
||||||
|
|
||||||
|
for i in range(nverts):
|
||||||
|
points[i] = (x + 0.5 * dia * cos(2*pi * i/nverts),
|
||||||
|
y + 0.5 * dia * sin(2*pi * i/nverts))
|
||||||
|
|
||||||
|
poly = Polygon(points)
|
||||||
|
poly_rotated = affinity.rotate(poly, angle, origin=(0, 0))
|
||||||
|
|
||||||
|
return {"pol": int(pol), "geometry": poly_rotated}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def make_moire(mods):
|
||||||
|
"""
|
||||||
|
Note: Specs indicate that rotation is only allowed if the center
|
||||||
|
(x, y) == (0, 0). I will tolerate breaking this rule.
|
||||||
|
|
||||||
|
:param mods: (x-center, y-center, outer_dia_outer_ring, ring thickness,
|
||||||
|
gap, max_rings, crosshair_thickness, crosshair_len, rotation
|
||||||
|
angle around origin in degrees)
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
|
||||||
|
x, y, dia, thickness, gap, nrings, cross_th, cross_len, angle = ApertureMacro.default2zero(9, mods)
|
||||||
|
|
||||||
|
r = dia/2 - thickness/2
|
||||||
|
result = Point((x, y)).buffer(r).exterior.buffer(thickness/2.0)
|
||||||
|
ring = Point((x, y)).buffer(r).exterior.buffer(thickness/2.0) # Need a copy!
|
||||||
|
|
||||||
|
i = 1 # Number of rings created so far
|
||||||
|
|
||||||
|
# ## If the ring does not have an interior it means that it is
|
||||||
|
# ## a disk. Then stop.
|
||||||
|
while len(ring.interiors) > 0 and i < nrings:
|
||||||
|
r -= thickness + gap
|
||||||
|
if r <= 0:
|
||||||
|
break
|
||||||
|
ring = Point((x, y)).buffer(r).exterior.buffer(thickness/2.0)
|
||||||
|
result = cascaded_union([result, ring])
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
# ## Crosshair
|
||||||
|
hor = LineString([(x - cross_len, y), (x + cross_len, y)]).buffer(cross_th/2.0, cap_style=2)
|
||||||
|
ver = LineString([(x, y-cross_len), (x, y + cross_len)]).buffer(cross_th/2.0, cap_style=2)
|
||||||
|
result = cascaded_union([result, hor, ver])
|
||||||
|
|
||||||
|
return {"pol": 1, "geometry": result}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def make_thermal(mods):
|
||||||
|
"""
|
||||||
|
Note: Specs indicate that rotation is only allowed if the center
|
||||||
|
(x, y) == (0, 0). I will tolerate breaking this rule.
|
||||||
|
|
||||||
|
:param mods: [x-center, y-center, diameter-outside, diameter-inside,
|
||||||
|
gap-thickness, rotation angle around origin]
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
|
||||||
|
x, y, dout, din, t, angle = ApertureMacro.default2zero(6, mods)
|
||||||
|
|
||||||
|
ring = Point((x, y)).buffer(dout/2.0).difference(Point((x, y)).buffer(din/2.0))
|
||||||
|
hline = LineString([(x - dout/2.0, y), (x + dout/2.0, y)]).buffer(t/2.0, cap_style=3)
|
||||||
|
vline = LineString([(x, y - dout/2.0), (x, y + dout/2.0)]).buffer(t/2.0, cap_style=3)
|
||||||
|
thermal = ring.difference(hline.union(vline))
|
||||||
|
|
||||||
|
return {"pol": 1, "geometry": thermal}
|
||||||
|
|
||||||
|
def make_geometry(self, modifiers):
|
||||||
|
"""
|
||||||
|
Runs the macro for the given modifiers and generates
|
||||||
|
the corresponding geometry.
|
||||||
|
|
||||||
|
:param modifiers: Modifiers (parameters) for this macro
|
||||||
|
:type modifiers: list
|
||||||
|
:return: Shapely geometry
|
||||||
|
:rtype: shapely.geometry.polygon
|
||||||
|
"""
|
||||||
|
|
||||||
|
# ## Primitive makers
|
||||||
|
makers = {
|
||||||
|
"1": ApertureMacro.make_circle,
|
||||||
|
"2": ApertureMacro.make_vectorline,
|
||||||
|
"20": ApertureMacro.make_vectorline,
|
||||||
|
"21": ApertureMacro.make_centerline,
|
||||||
|
"22": ApertureMacro.make_lowerleftline,
|
||||||
|
"4": ApertureMacro.make_outline,
|
||||||
|
"5": ApertureMacro.make_polygon,
|
||||||
|
"6": ApertureMacro.make_moire,
|
||||||
|
"7": ApertureMacro.make_thermal
|
||||||
|
}
|
||||||
|
|
||||||
|
# ## Store modifiers as local variables
|
||||||
|
modifiers = modifiers or []
|
||||||
|
modifiers = [float(m) for m in modifiers]
|
||||||
|
self.locvars = {}
|
||||||
|
for i in range(0, len(modifiers)):
|
||||||
|
self.locvars[str(i + 1)] = modifiers[i]
|
||||||
|
|
||||||
|
# ## Parse
|
||||||
|
self.primitives = [] # Cleanup
|
||||||
|
self.geometry = Polygon()
|
||||||
|
self.parse_content()
|
||||||
|
|
||||||
|
# ## Make the geometry
|
||||||
|
for primitive in self.primitives:
|
||||||
|
# Make the primitive
|
||||||
|
prim_geo = makers[str(int(primitive[0]))](primitive[1:])
|
||||||
|
|
||||||
|
# Add it (according to polarity)
|
||||||
|
# if self.geometry is None and prim_geo['pol'] == 1:
|
||||||
|
# self.geometry = prim_geo['geometry']
|
||||||
|
# continue
|
||||||
|
if prim_geo['pol'] == 1:
|
||||||
|
self.geometry = self.geometry.union(prim_geo['geometry'])
|
||||||
|
continue
|
||||||
|
if prim_geo['pol'] == 0:
|
||||||
|
self.geometry = self.geometry.difference(prim_geo['geometry'])
|
||||||
|
continue
|
||||||
|
|
||||||
|
return self.geometry
|
||||||
|
|
||||||
|
|
||||||
class Geometry(object):
|
class Geometry(object):
|
||||||
"""
|
"""
|
||||||
Base geometry class.
|
Base geometry class.
|
||||||
|
|
|
@ -1,8 +1,32 @@
|
||||||
from camlib import *
|
from camlib import Geometry
|
||||||
|
import FlatCAMApp
|
||||||
|
|
||||||
|
import FlatCAMTranslation as fcTranslate
|
||||||
|
|
||||||
|
from shapely.geometry import Polygon, Point, LineString, MultiPolygon
|
||||||
|
from shapely.ops import cascaded_union
|
||||||
|
import shapely.affinity as affinity
|
||||||
|
|
||||||
|
import re
|
||||||
|
import traceback
|
||||||
|
import gettext
|
||||||
|
import builtins
|
||||||
|
import numpy as np
|
||||||
|
from numpy import Inf
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
if '_' not in builtins.__dict__:
|
if '_' not in builtins.__dict__:
|
||||||
_ = gettext.gettext
|
_ = gettext.gettext
|
||||||
|
|
||||||
|
log = logging.getLogger('base2')
|
||||||
|
log.setLevel(logging.DEBUG)
|
||||||
|
|
||||||
|
formatter = logging.Formatter('[%(levelname)s] %(message)s')
|
||||||
|
handler = logging.StreamHandler()
|
||||||
|
handler.setFormatter(formatter)
|
||||||
|
log.addHandler(handler)
|
||||||
|
|
||||||
|
|
||||||
class Excellon(Geometry):
|
class Excellon(Geometry):
|
||||||
"""
|
"""
|
||||||
|
@ -265,7 +289,7 @@ class Excellon(Geometry):
|
||||||
|
|
||||||
line_units = ''
|
line_units = ''
|
||||||
|
|
||||||
#### Parsing starts here ## ##
|
# ## Parsing starts here ## ##
|
||||||
line_num = 0 # Line number
|
line_num = 0 # Line number
|
||||||
eline = ""
|
eline = ""
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -1,9 +1,35 @@
|
||||||
|
from camlib import Geometry, ApertureMacro, parse_gerber_number, arc, arctan2, arc_angle
|
||||||
|
import FlatCAMApp
|
||||||
|
import FlatCAMTranslation as fcTranslate
|
||||||
|
|
||||||
from camlib import *
|
from shapely.geometry import Polygon, Point, LineString, MultiPolygon
|
||||||
|
from shapely.ops import cascaded_union
|
||||||
|
import shapely.affinity as affinity
|
||||||
|
from shapely.geometry import box as shply_box
|
||||||
|
import re
|
||||||
|
import traceback
|
||||||
|
from copy import deepcopy
|
||||||
|
|
||||||
|
import gettext
|
||||||
|
import builtins
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
from numpy import Inf
|
||||||
|
from math import sqrt, pi, sin, cos
|
||||||
|
import sys
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
if '_' not in builtins.__dict__:
|
if '_' not in builtins.__dict__:
|
||||||
_ = gettext.gettext
|
_ = gettext.gettext
|
||||||
|
|
||||||
|
log = logging.getLogger('base2')
|
||||||
|
log.setLevel(logging.DEBUG)
|
||||||
|
|
||||||
|
formatter = logging.Formatter('[%(levelname)s] %(message)s')
|
||||||
|
handler = logging.StreamHandler()
|
||||||
|
handler.setFormatter(formatter)
|
||||||
|
log.addHandler(handler)
|
||||||
|
|
||||||
class Gerber(Geometry):
|
class Gerber(Geometry):
|
||||||
"""
|
"""
|
||||||
|
@ -1134,25 +1160,25 @@ class Gerber(Geometry):
|
||||||
try:
|
try:
|
||||||
circular_x = parse_gerber_number(circular_x,
|
circular_x = parse_gerber_number(circular_x,
|
||||||
self.int_digits, self.frac_digits, self.gerber_zeros)
|
self.int_digits, self.frac_digits, self.gerber_zeros)
|
||||||
except:
|
except Exception as e:
|
||||||
circular_x = current_x
|
circular_x = current_x
|
||||||
|
|
||||||
try:
|
try:
|
||||||
circular_y = parse_gerber_number(circular_y,
|
circular_y = parse_gerber_number(circular_y,
|
||||||
self.int_digits, self.frac_digits, self.gerber_zeros)
|
self.int_digits, self.frac_digits, self.gerber_zeros)
|
||||||
except:
|
except Exception as e:
|
||||||
circular_y = current_y
|
circular_y = current_y
|
||||||
|
|
||||||
# According to Gerber specification i and j are not modal, which means that when i or j are missing,
|
# According to Gerber specification i and j are not modal, which means that when i or j are missing,
|
||||||
# they are to be interpreted as being zero
|
# they are to be interpreted as being zero
|
||||||
try:
|
try:
|
||||||
i = parse_gerber_number(i, self.int_digits, self.frac_digits, self.gerber_zeros)
|
i = parse_gerber_number(i, self.int_digits, self.frac_digits, self.gerber_zeros)
|
||||||
except:
|
except Exception as e:
|
||||||
i = 0
|
i = 0
|
||||||
|
|
||||||
try:
|
try:
|
||||||
j = parse_gerber_number(j, self.int_digits, self.frac_digits, self.gerber_zeros)
|
j = parse_gerber_number(j, self.int_digits, self.frac_digits, self.gerber_zeros)
|
||||||
except:
|
except Exception as e:
|
||||||
j = 0
|
j = 0
|
||||||
|
|
||||||
if quadrant_mode is None:
|
if quadrant_mode is None:
|
||||||
|
@ -1598,6 +1624,7 @@ class Gerber(Geometry):
|
||||||
:type xfactor: float
|
:type xfactor: float
|
||||||
:param yfactor: Number by which to scale on Y axis.
|
:param yfactor: Number by which to scale on Y axis.
|
||||||
:type yfactor: float
|
:type yfactor: float
|
||||||
|
:param point: reference point for scaling operation
|
||||||
:rtype : None
|
:rtype : None
|
||||||
"""
|
"""
|
||||||
log.debug("camlib.Gerber.scale()")
|
log.debug("camlib.Gerber.scale()")
|
||||||
|
@ -1628,7 +1655,7 @@ class Gerber(Geometry):
|
||||||
# variables to display the percentage of work done
|
# variables to display the percentage of work done
|
||||||
self.geo_len = 0
|
self.geo_len = 0
|
||||||
try:
|
try:
|
||||||
for g in self.solid_geometry:
|
for __ in self.solid_geometry:
|
||||||
self.geo_len += 1
|
self.geo_len += 1
|
||||||
except TypeError:
|
except TypeError:
|
||||||
self.geo_len = 1
|
self.geo_len = 1
|
||||||
|
@ -1717,7 +1744,7 @@ class Gerber(Geometry):
|
||||||
# variables to display the percentage of work done
|
# variables to display the percentage of work done
|
||||||
self.geo_len = 0
|
self.geo_len = 0
|
||||||
try:
|
try:
|
||||||
for g in self.solid_geometry:
|
for __ in self.solid_geometry:
|
||||||
self.geo_len += 1
|
self.geo_len += 1
|
||||||
except TypeError:
|
except TypeError:
|
||||||
self.geo_len = 1
|
self.geo_len = 1
|
||||||
|
@ -1796,7 +1823,7 @@ class Gerber(Geometry):
|
||||||
# variables to display the percentage of work done
|
# variables to display the percentage of work done
|
||||||
self.geo_len = 0
|
self.geo_len = 0
|
||||||
try:
|
try:
|
||||||
for g in self.solid_geometry:
|
for __ in self.solid_geometry:
|
||||||
self.geo_len += 1
|
self.geo_len += 1
|
||||||
except TypeError:
|
except TypeError:
|
||||||
self.geo_len = 1
|
self.geo_len = 1
|
||||||
|
@ -1857,6 +1884,10 @@ class Gerber(Geometry):
|
||||||
|
|
||||||
See shapely manual for more information:
|
See shapely manual for more information:
|
||||||
http://toblerity.org/shapely/manual.html#affine-transformations
|
http://toblerity.org/shapely/manual.html#affine-transformations
|
||||||
|
:param angle_x: the angle on X axis for skewing
|
||||||
|
:param angle_y: the angle on Y axis for skewing
|
||||||
|
:param point: reference point for skewing operation
|
||||||
|
:return None
|
||||||
"""
|
"""
|
||||||
log.debug("camlib.Gerber.skew()")
|
log.debug("camlib.Gerber.skew()")
|
||||||
|
|
||||||
|
@ -1865,7 +1896,7 @@ class Gerber(Geometry):
|
||||||
# variables to display the percentage of work done
|
# variables to display the percentage of work done
|
||||||
self.geo_len = 0
|
self.geo_len = 0
|
||||||
try:
|
try:
|
||||||
for g in self.solid_geometry:
|
for __ in self.solid_geometry:
|
||||||
self.geo_len += 1
|
self.geo_len += 1
|
||||||
except TypeError:
|
except TypeError:
|
||||||
self.geo_len = 1
|
self.geo_len = 1
|
||||||
|
@ -1926,7 +1957,7 @@ class Gerber(Geometry):
|
||||||
# variables to display the percentage of work done
|
# variables to display the percentage of work done
|
||||||
self.geo_len = 0
|
self.geo_len = 0
|
||||||
try:
|
try:
|
||||||
for g in self.solid_geometry:
|
for __ in self.solid_geometry:
|
||||||
self.geo_len += 1
|
self.geo_len += 1
|
||||||
except TypeError:
|
except TypeError:
|
||||||
self.geo_len = 1
|
self.geo_len = 1
|
||||||
|
@ -1972,376 +2003,3 @@ class Gerber(Geometry):
|
||||||
self.app.inform.emit('[success] %s' %
|
self.app.inform.emit('[success] %s' %
|
||||||
_("Gerber Rotate done."))
|
_("Gerber Rotate done."))
|
||||||
self.app.proc_container.new_text = ''
|
self.app.proc_container.new_text = ''
|
||||||
|
|
||||||
|
|
||||||
class ApertureMacro:
|
|
||||||
"""
|
|
||||||
Syntax of aperture macros.
|
|
||||||
|
|
||||||
<AM command>: AM<Aperture macro name>*<Macro content>
|
|
||||||
<Macro content>: {{<Variable definition>*}{<Primitive>*}}
|
|
||||||
<Variable definition>: $K=<Arithmetic expression>
|
|
||||||
<Primitive>: <Primitive code>,<Modifier>{,<Modifier>}|<Comment>
|
|
||||||
<Modifier>: $M|< Arithmetic expression>
|
|
||||||
<Comment>: 0 <Text>
|
|
||||||
"""
|
|
||||||
|
|
||||||
# ## Regular expressions
|
|
||||||
am1_re = re.compile(r'^%AM([^\*]+)\*(.+)?(%)?$')
|
|
||||||
am2_re = re.compile(r'(.*)%$')
|
|
||||||
amcomm_re = re.compile(r'^0(.*)')
|
|
||||||
amprim_re = re.compile(r'^[1-9].*')
|
|
||||||
amvar_re = re.compile(r'^\$([0-9a-zA-z]+)=(.*)')
|
|
||||||
|
|
||||||
def __init__(self, name=None):
|
|
||||||
self.name = name
|
|
||||||
self.raw = ""
|
|
||||||
|
|
||||||
# ## These below are recomputed for every aperture
|
|
||||||
# ## definition, in other words, are temporary variables.
|
|
||||||
self.primitives = []
|
|
||||||
self.locvars = {}
|
|
||||||
self.geometry = None
|
|
||||||
|
|
||||||
def to_dict(self):
|
|
||||||
"""
|
|
||||||
Returns the object in a serializable form. Only the name and
|
|
||||||
raw are required.
|
|
||||||
|
|
||||||
:return: Dictionary representing the object. JSON ready.
|
|
||||||
:rtype: dict
|
|
||||||
"""
|
|
||||||
|
|
||||||
return {
|
|
||||||
'name': self.name,
|
|
||||||
'raw': self.raw
|
|
||||||
}
|
|
||||||
|
|
||||||
def from_dict(self, d):
|
|
||||||
"""
|
|
||||||
Populates the object from a serial representation created
|
|
||||||
with ``self.to_dict()``.
|
|
||||||
|
|
||||||
:param d: Serial representation of an ApertureMacro object.
|
|
||||||
:return: None
|
|
||||||
"""
|
|
||||||
for attr in ['name', 'raw']:
|
|
||||||
setattr(self, attr, d[attr])
|
|
||||||
|
|
||||||
def parse_content(self):
|
|
||||||
"""
|
|
||||||
Creates numerical lists for all primitives in the aperture
|
|
||||||
macro (in ``self.raw``) by replacing all variables by their
|
|
||||||
values iteratively and evaluating expressions. Results
|
|
||||||
are stored in ``self.primitives``.
|
|
||||||
|
|
||||||
:return: None
|
|
||||||
"""
|
|
||||||
# Cleanup
|
|
||||||
self.raw = self.raw.replace('\n', '').replace('\r', '').strip(" *")
|
|
||||||
self.primitives = []
|
|
||||||
|
|
||||||
# Separate parts
|
|
||||||
parts = self.raw.split('*')
|
|
||||||
|
|
||||||
# ### Every part in the macro ####
|
|
||||||
for part in parts:
|
|
||||||
# ## Comments. Ignored.
|
|
||||||
match = ApertureMacro.amcomm_re.search(part)
|
|
||||||
if match:
|
|
||||||
continue
|
|
||||||
|
|
||||||
# ## Variables
|
|
||||||
# These are variables defined locally inside the macro. They can be
|
|
||||||
# numerical constant or defind in terms of previously define
|
|
||||||
# variables, which can be defined locally or in an aperture
|
|
||||||
# definition. All replacements ocurr here.
|
|
||||||
match = ApertureMacro.amvar_re.search(part)
|
|
||||||
if match:
|
|
||||||
var = match.group(1)
|
|
||||||
val = match.group(2)
|
|
||||||
|
|
||||||
# Replace variables in value
|
|
||||||
for v in self.locvars:
|
|
||||||
# replaced the following line with the next to fix Mentor custom apertures not parsed OK
|
|
||||||
# val = re.sub((r'\$'+str(v)+r'(?![0-9a-zA-Z])'), str(self.locvars[v]), val)
|
|
||||||
val = val.replace('$' + str(v), str(self.locvars[v]))
|
|
||||||
|
|
||||||
# Make all others 0
|
|
||||||
val = re.sub(r'\$[0-9a-zA-Z](?![0-9a-zA-Z])', "0", val)
|
|
||||||
# Change x with *
|
|
||||||
val = re.sub(r'[xX]', "*", val)
|
|
||||||
|
|
||||||
# Eval() and store.
|
|
||||||
self.locvars[var] = eval(val)
|
|
||||||
continue
|
|
||||||
|
|
||||||
# ## Primitives
|
|
||||||
# Each is an array. The first identifies the primitive, while the
|
|
||||||
# rest depend on the primitive. All are strings representing a
|
|
||||||
# number and may contain variable definition. The values of these
|
|
||||||
# variables are defined in an aperture definition.
|
|
||||||
match = ApertureMacro.amprim_re.search(part)
|
|
||||||
if match:
|
|
||||||
# ## Replace all variables
|
|
||||||
for v in self.locvars:
|
|
||||||
# replaced the following line with the next to fix Mentor custom apertures not parsed OK
|
|
||||||
# part = re.sub(r'\$' + str(v) + r'(?![0-9a-zA-Z])', str(self.locvars[v]), part)
|
|
||||||
part = part.replace('$' + str(v), str(self.locvars[v]))
|
|
||||||
|
|
||||||
# Make all others 0
|
|
||||||
part = re.sub(r'\$[0-9a-zA-Z](?![0-9a-zA-Z])', "0", part)
|
|
||||||
|
|
||||||
# Change x with *
|
|
||||||
part = re.sub(r'[xX]', "*", part)
|
|
||||||
|
|
||||||
# ## Store
|
|
||||||
elements = part.split(",")
|
|
||||||
self.primitives.append([eval(x) for x in elements])
|
|
||||||
continue
|
|
||||||
|
|
||||||
log.warning("Unknown syntax of aperture macro part: %s" % str(part))
|
|
||||||
|
|
||||||
def append(self, data):
|
|
||||||
"""
|
|
||||||
Appends a string to the raw macro.
|
|
||||||
|
|
||||||
:param data: Part of the macro.
|
|
||||||
:type data: str
|
|
||||||
:return: None
|
|
||||||
"""
|
|
||||||
self.raw += data
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def default2zero(n, mods):
|
|
||||||
"""
|
|
||||||
Pads the ``mods`` list with zeros resulting in an
|
|
||||||
list of length n.
|
|
||||||
|
|
||||||
:param n: Length of the resulting list.
|
|
||||||
:type n: int
|
|
||||||
:param mods: List to be padded.
|
|
||||||
:type mods: list
|
|
||||||
:return: Zero-padded list.
|
|
||||||
:rtype: list
|
|
||||||
"""
|
|
||||||
x = [0.0] * n
|
|
||||||
na = len(mods)
|
|
||||||
x[0:na] = mods
|
|
||||||
return x
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def make_circle(mods):
|
|
||||||
"""
|
|
||||||
|
|
||||||
:param mods: (Exposure 0/1, Diameter >=0, X-coord, Y-coord)
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
|
|
||||||
pol, dia, x, y = ApertureMacro.default2zero(4, mods)
|
|
||||||
|
|
||||||
return {"pol": int(pol), "geometry": Point(x, y).buffer(dia/2)}
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def make_vectorline(mods):
|
|
||||||
"""
|
|
||||||
|
|
||||||
:param mods: (Exposure 0/1, Line width >= 0, X-start, Y-start, X-end, Y-end,
|
|
||||||
rotation angle around origin in degrees)
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
pol, width, xs, ys, xe, ye, angle = ApertureMacro.default2zero(7, mods)
|
|
||||||
|
|
||||||
line = LineString([(xs, ys), (xe, ye)])
|
|
||||||
box = line.buffer(width/2, cap_style=2)
|
|
||||||
box_rotated = affinity.rotate(box, angle, origin=(0, 0))
|
|
||||||
|
|
||||||
return {"pol": int(pol), "geometry": box_rotated}
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def make_centerline(mods):
|
|
||||||
"""
|
|
||||||
|
|
||||||
:param mods: (Exposure 0/1, width >=0, height >=0, x-center, y-center,
|
|
||||||
rotation angle around origin in degrees)
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
|
|
||||||
pol, width, height, x, y, angle = ApertureMacro.default2zero(6, mods)
|
|
||||||
|
|
||||||
box = shply_box(x-width/2, y-height/2, x+width/2, y+height/2)
|
|
||||||
box_rotated = affinity.rotate(box, angle, origin=(0, 0))
|
|
||||||
|
|
||||||
return {"pol": int(pol), "geometry": box_rotated}
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def make_lowerleftline(mods):
|
|
||||||
"""
|
|
||||||
|
|
||||||
:param mods: (exposure 0/1, width >=0, height >=0, x-lowerleft, y-lowerleft,
|
|
||||||
rotation angle around origin in degrees)
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
|
|
||||||
pol, width, height, x, y, angle = ApertureMacro.default2zero(6, mods)
|
|
||||||
|
|
||||||
box = shply_box(x, y, x+width, y+height)
|
|
||||||
box_rotated = affinity.rotate(box, angle, origin=(0, 0))
|
|
||||||
|
|
||||||
return {"pol": int(pol), "geometry": box_rotated}
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def make_outline(mods):
|
|
||||||
"""
|
|
||||||
|
|
||||||
:param mods:
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
|
|
||||||
pol = mods[0]
|
|
||||||
n = mods[1]
|
|
||||||
points = [(0, 0)]*(n+1)
|
|
||||||
|
|
||||||
for i in range(n+1):
|
|
||||||
points[i] = mods[2*i + 2:2*i + 4]
|
|
||||||
|
|
||||||
angle = mods[2*n + 4]
|
|
||||||
|
|
||||||
poly = Polygon(points)
|
|
||||||
poly_rotated = affinity.rotate(poly, angle, origin=(0, 0))
|
|
||||||
|
|
||||||
return {"pol": int(pol), "geometry": poly_rotated}
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def make_polygon(mods):
|
|
||||||
"""
|
|
||||||
Note: Specs indicate that rotation is only allowed if the center
|
|
||||||
(x, y) == (0, 0). I will tolerate breaking this rule.
|
|
||||||
|
|
||||||
:param mods: (exposure 0/1, n_verts 3<=n<=12, x-center, y-center,
|
|
||||||
diameter of circumscribed circle >=0, rotation angle around origin)
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
|
|
||||||
pol, nverts, x, y, dia, angle = ApertureMacro.default2zero(6, mods)
|
|
||||||
points = [(0, 0)]*nverts
|
|
||||||
|
|
||||||
for i in range(nverts):
|
|
||||||
points[i] = (x + 0.5 * dia * cos(2*pi * i/nverts),
|
|
||||||
y + 0.5 * dia * sin(2*pi * i/nverts))
|
|
||||||
|
|
||||||
poly = Polygon(points)
|
|
||||||
poly_rotated = affinity.rotate(poly, angle, origin=(0, 0))
|
|
||||||
|
|
||||||
return {"pol": int(pol), "geometry": poly_rotated}
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def make_moire(mods):
|
|
||||||
"""
|
|
||||||
Note: Specs indicate that rotation is only allowed if the center
|
|
||||||
(x, y) == (0, 0). I will tolerate breaking this rule.
|
|
||||||
|
|
||||||
:param mods: (x-center, y-center, outer_dia_outer_ring, ring thickness,
|
|
||||||
gap, max_rings, crosshair_thickness, crosshair_len, rotation
|
|
||||||
angle around origin in degrees)
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
|
|
||||||
x, y, dia, thickness, gap, nrings, cross_th, cross_len, angle = ApertureMacro.default2zero(9, mods)
|
|
||||||
|
|
||||||
r = dia/2 - thickness/2
|
|
||||||
result = Point((x, y)).buffer(r).exterior.buffer(thickness/2.0)
|
|
||||||
ring = Point((x, y)).buffer(r).exterior.buffer(thickness/2.0) # Need a copy!
|
|
||||||
|
|
||||||
i = 1 # Number of rings created so far
|
|
||||||
|
|
||||||
# ## If the ring does not have an interior it means that it is
|
|
||||||
# ## a disk. Then stop.
|
|
||||||
while len(ring.interiors) > 0 and i < nrings:
|
|
||||||
r -= thickness + gap
|
|
||||||
if r <= 0:
|
|
||||||
break
|
|
||||||
ring = Point((x, y)).buffer(r).exterior.buffer(thickness/2.0)
|
|
||||||
result = cascaded_union([result, ring])
|
|
||||||
i += 1
|
|
||||||
|
|
||||||
# ## Crosshair
|
|
||||||
hor = LineString([(x - cross_len, y), (x + cross_len, y)]).buffer(cross_th/2.0, cap_style=2)
|
|
||||||
ver = LineString([(x, y-cross_len), (x, y + cross_len)]).buffer(cross_th/2.0, cap_style=2)
|
|
||||||
result = cascaded_union([result, hor, ver])
|
|
||||||
|
|
||||||
return {"pol": 1, "geometry": result}
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def make_thermal(mods):
|
|
||||||
"""
|
|
||||||
Note: Specs indicate that rotation is only allowed if the center
|
|
||||||
(x, y) == (0, 0). I will tolerate breaking this rule.
|
|
||||||
|
|
||||||
:param mods: [x-center, y-center, diameter-outside, diameter-inside,
|
|
||||||
gap-thickness, rotation angle around origin]
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
|
|
||||||
x, y, dout, din, t, angle = ApertureMacro.default2zero(6, mods)
|
|
||||||
|
|
||||||
ring = Point((x, y)).buffer(dout/2.0).difference(Point((x, y)).buffer(din/2.0))
|
|
||||||
hline = LineString([(x - dout/2.0, y), (x + dout/2.0, y)]).buffer(t/2.0, cap_style=3)
|
|
||||||
vline = LineString([(x, y - dout/2.0), (x, y + dout/2.0)]).buffer(t/2.0, cap_style=3)
|
|
||||||
thermal = ring.difference(hline.union(vline))
|
|
||||||
|
|
||||||
return {"pol": 1, "geometry": thermal}
|
|
||||||
|
|
||||||
def make_geometry(self, modifiers):
|
|
||||||
"""
|
|
||||||
Runs the macro for the given modifiers and generates
|
|
||||||
the corresponding geometry.
|
|
||||||
|
|
||||||
:param modifiers: Modifiers (parameters) for this macro
|
|
||||||
:type modifiers: list
|
|
||||||
:return: Shapely geometry
|
|
||||||
:rtype: shapely.geometry.polygon
|
|
||||||
"""
|
|
||||||
|
|
||||||
# ## Primitive makers
|
|
||||||
makers = {
|
|
||||||
"1": ApertureMacro.make_circle,
|
|
||||||
"2": ApertureMacro.make_vectorline,
|
|
||||||
"20": ApertureMacro.make_vectorline,
|
|
||||||
"21": ApertureMacro.make_centerline,
|
|
||||||
"22": ApertureMacro.make_lowerleftline,
|
|
||||||
"4": ApertureMacro.make_outline,
|
|
||||||
"5": ApertureMacro.make_polygon,
|
|
||||||
"6": ApertureMacro.make_moire,
|
|
||||||
"7": ApertureMacro.make_thermal
|
|
||||||
}
|
|
||||||
|
|
||||||
# ## Store modifiers as local variables
|
|
||||||
modifiers = modifiers or []
|
|
||||||
modifiers = [float(m) for m in modifiers]
|
|
||||||
self.locvars = {}
|
|
||||||
for i in range(0, len(modifiers)):
|
|
||||||
self.locvars[str(i + 1)] = modifiers[i]
|
|
||||||
|
|
||||||
# ## Parse
|
|
||||||
self.primitives = [] # Cleanup
|
|
||||||
self.geometry = Polygon()
|
|
||||||
self.parse_content()
|
|
||||||
|
|
||||||
# ## Make the geometry
|
|
||||||
for primitive in self.primitives:
|
|
||||||
# Make the primitive
|
|
||||||
prim_geo = makers[str(int(primitive[0]))](primitive[1:])
|
|
||||||
|
|
||||||
# Add it (according to polarity)
|
|
||||||
# if self.geometry is None and prim_geo['pol'] == 1:
|
|
||||||
# self.geometry = prim_geo['geometry']
|
|
||||||
# continue
|
|
||||||
if prim_geo['pol'] == 1:
|
|
||||||
self.geometry = self.geometry.union(prim_geo['geometry'])
|
|
||||||
continue
|
|
||||||
if prim_geo['pol'] == 0:
|
|
||||||
self.geometry = self.geometry.difference(prim_geo['geometry'])
|
|
||||||
continue
|
|
||||||
|
|
||||||
return self.geometry
|
|
||||||
|
|
|
@ -3,10 +3,11 @@ from PyQt5 import QtGui, QtWidgets
|
||||||
from PyQt5 import QtCore
|
from PyQt5 import QtCore
|
||||||
from PyQt5.QtCore import Qt
|
from PyQt5.QtCore import Qt
|
||||||
|
|
||||||
|
|
||||||
class TitleBar(QtWidgets.QDialog):
|
class TitleBar(QtWidgets.QDialog):
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
QtWidgets.QWidget.__init__(self, parent)
|
QtWidgets.QWidget.__init__(self, parent)
|
||||||
self.setWindowFlags(Qt.FramelessWindowHint);
|
self.setWindowFlags(Qt.FramelessWindowHint)
|
||||||
css = """
|
css = """
|
||||||
QWidget{
|
QWidget{
|
||||||
Background: #AA00AA;
|
Background: #AA00AA;
|
||||||
|
@ -60,19 +61,19 @@ class TitleBar(QtWidgets.QDialog):
|
||||||
self.maximize.clicked.connect(self.showMaxRestore)
|
self.maximize.clicked.connect(self.showMaxRestore)
|
||||||
|
|
||||||
def showSmall(self):
|
def showSmall(self):
|
||||||
box.showMinimized();
|
box.showMinimized()
|
||||||
|
|
||||||
def showMaxRestore(self):
|
def showMaxRestore(self):
|
||||||
if(self.maxNormal):
|
if(self.maxNormal):
|
||||||
box.showNormal();
|
box.showNormal()
|
||||||
self.maxNormal= False;
|
self.maxNormal= False
|
||||||
self.maximize.setIcon(QtGui.QIcon('img/max.png'));
|
self.maximize.setIcon(QtGui.QIcon('img/max.png'))
|
||||||
print(1)
|
print(1)
|
||||||
else:
|
else:
|
||||||
box.showMaximized();
|
box.showMaximized()
|
||||||
self.maxNormal= True;
|
self.maxNormal= True
|
||||||
print(2)
|
print(2)
|
||||||
self.maximize.setIcon(QtGui.QIcon('img/max2.png'));
|
self.maximize.setIcon(QtGui.QIcon('img/max2.png'))
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
box.close()
|
box.close()
|
||||||
|
@ -88,7 +89,7 @@ class TitleBar(QtWidgets.QDialog):
|
||||||
class Frame(QtWidgets.QFrame):
|
class Frame(QtWidgets.QFrame):
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
QtWidgets.QFrame.__init__(self, parent)
|
QtWidgets.QFrame.__init__(self, parent)
|
||||||
self.m_mouse_down= False;
|
self.m_mouse_down= False
|
||||||
self.setFrameShape(QtWidgets.QFrame.StyledPanel)
|
self.setFrameShape(QtWidgets.QFrame.StyledPanel)
|
||||||
css = """
|
css = """
|
||||||
QFrame{
|
QFrame{
|
||||||
|
@ -122,25 +123,26 @@ class Frame(QtWidgets.QFrame):
|
||||||
return self.m_titleBar
|
return self.m_titleBar
|
||||||
|
|
||||||
def mousePressEvent(self,event):
|
def mousePressEvent(self,event):
|
||||||
self.m_old_pos = event.pos();
|
self.m_old_pos = event.pos()
|
||||||
self.m_mouse_down = event.button()== Qt.LeftButton;
|
self.m_mouse_down = event.button()== Qt.LeftButton
|
||||||
|
|
||||||
def mouseMoveEvent(self,event):
|
def mouseMoveEvent(self,event):
|
||||||
x=event.x()
|
x=event.x()
|
||||||
y=event.y()
|
y=event.y()
|
||||||
|
|
||||||
def mouseReleaseEvent(self,event):
|
def mouseReleaseEvent(self,event):
|
||||||
m_mouse_down=False;
|
m_mouse_down=False
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
app = QtWidgets.QApplication(sys.argv);
|
app = QtWidgets.QApplication(sys.argv)
|
||||||
box = Frame()
|
box = Frame()
|
||||||
box.move(60,60)
|
box.move(60,60)
|
||||||
l=QtWidgets.QVBoxLayout(box.contentWidget());
|
l = QtWidgets.QVBoxLayout(box.contentWidget())
|
||||||
l.setContentsMargins(0, 0,0 ,0)
|
l.setContentsMargins(0, 0,0 ,0)
|
||||||
edit=QtWidgets.QLabel("""I would've did anything for you to show you how much I adored you
|
edit = QtWidgets.QLabel("""I would've did anything for you to show you how much I adored you
|
||||||
But it's over now, it's too late to save our loveJust promise me you'll think of me
|
But it's over now, it's too late to save our loveJust promise me you'll think of me
|
||||||
Every time you look up in the sky and see a star 'cuz I'm your star.""");
|
Every time you look up in the sky and see a star 'cuz I'm your star.""")
|
||||||
l.addWidget(edit)
|
l.addWidget(edit)
|
||||||
box.show()
|
box.show()
|
||||||
app.exec_()
|
app.exec_()
|
|
@ -4,7 +4,7 @@
|
||||||
import sys
|
import sys
|
||||||
sys.path.append('../../')
|
sys.path.append('../../')
|
||||||
|
|
||||||
from camlib import *
|
from flatcamParsers.ParseGerber import *
|
||||||
|
|
||||||
log = logging.getLogger('base2')
|
log = logging.getLogger('base2')
|
||||||
log.setLevel(logging.WARNING)
|
log.setLevel(logging.WARNING)
|
||||||
|
|
|
@ -3,7 +3,7 @@ import pstats
|
||||||
import sys
|
import sys
|
||||||
sys.path.append('../../')
|
sys.path.append('../../')
|
||||||
|
|
||||||
from camlib import *
|
from flatcamParsers.ParseGerber import *
|
||||||
|
|
||||||
log = logging.getLogger('base2')
|
log = logging.getLogger('base2')
|
||||||
log.setLevel(logging.WARNING)
|
log.setLevel(logging.WARNING)
|
||||||
|
|
|
@ -2,6 +2,7 @@ import sys
|
||||||
from PyQt5.Qt import *
|
from PyQt5.Qt import *
|
||||||
from PyQt5 import QtGui, QtWidgets
|
from PyQt5 import QtGui, QtWidgets
|
||||||
|
|
||||||
|
|
||||||
class MyPopup(QWidget):
|
class MyPopup(QWidget):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
QWidget.__init__(self)
|
QWidget.__init__(self)
|
||||||
|
@ -30,6 +31,7 @@ class MyPopup(QWidget):
|
||||||
# dc.drawLine(0, 0, 100, 100)
|
# dc.drawLine(0, 0, 100, 100)
|
||||||
# dc.drawLine(100, 0, 0, 100)
|
# dc.drawLine(100, 0, 0, 100)
|
||||||
|
|
||||||
|
|
||||||
class MainWindow(QMainWindow):
|
class MainWindow(QMainWindow):
|
||||||
def __init__(self, *args):
|
def __init__(self, *args):
|
||||||
QtWidgets.QMainWindow.__init__(self, *args)
|
QtWidgets.QMainWindow.__init__(self, *args)
|
||||||
|
@ -46,6 +48,7 @@ class MainWindow(QMainWindow):
|
||||||
self.w.setGeometry(QRect(100, 100, 400, 200))
|
self.w.setGeometry(QRect(100, 100, 400, 200))
|
||||||
self.w.show()
|
self.w.show()
|
||||||
|
|
||||||
|
|
||||||
class App(QApplication):
|
class App(QApplication):
|
||||||
def __init__(self, *args):
|
def __init__(self, *args):
|
||||||
QtWidgets.QApplication.__init__(self, *args)
|
QtWidgets.QApplication.__init__(self, *args)
|
||||||
|
@ -56,10 +59,12 @@ class App(QApplication):
|
||||||
def byebye(self):
|
def byebye(self):
|
||||||
self.exit(0)
|
self.exit(0)
|
||||||
|
|
||||||
|
|
||||||
def main(args):
|
def main(args):
|
||||||
global app
|
global app
|
||||||
app = App(args)
|
app = App(args)
|
||||||
app.exec_()
|
app.exec_()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main(sys.argv)
|
main(sys.argv)
|
|
@ -1,5 +1,5 @@
|
||||||
import sys
|
import sys
|
||||||
from PyQt4 import QtCore, QtGui
|
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||||
|
|
||||||
|
|
||||||
class MyObj():
|
class MyObj():
|
||||||
|
@ -16,12 +16,12 @@ def parse():
|
||||||
raise Exception("Intentional Exception")
|
raise Exception("Intentional Exception")
|
||||||
|
|
||||||
|
|
||||||
class Example(QtGui.QWidget):
|
class Example(QtWidgets.QWidget):
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super(Example, self).__init__()
|
super(Example, self).__init__()
|
||||||
|
|
||||||
qbtn = QtGui.QPushButton('Raise', self)
|
qbtn = QtWidgets.QPushButton('Raise', self)
|
||||||
qbtn.clicked.connect(parse)
|
qbtn.clicked.connect(parse)
|
||||||
|
|
||||||
self.setWindowTitle('Quit button')
|
self.setWindowTitle('Quit button')
|
||||||
|
@ -29,6 +29,6 @@ class Example(QtGui.QWidget):
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
app = QtGui.QApplication(sys.argv)
|
app = QtWidgets.QApplication(sys.argv)
|
||||||
ex = Example()
|
ex = Example()
|
||||||
sys.exit(app.exec_())
|
sys.exit(app.exec_())
|
|
@ -1,7 +1,7 @@
|
||||||
import os
|
import os
|
||||||
os.chdir('../')
|
os.chdir('../')
|
||||||
|
|
||||||
from camlib import *
|
from flatcamParsers.ParseGerber import *
|
||||||
|
|
||||||
g = Gerber()
|
g = Gerber()
|
||||||
g.parse_file(r'C:\Users\jpcaram\Dropbox\CNC\pcbcam\test_files\PlacaReles-F_Cu.gtl')
|
g.parse_file(r'C:\Users\jpcaram\Dropbox\CNC\pcbcam\test_files\PlacaReles-F_Cu.gtl')
|
||||||
|
|
|
@ -8,12 +8,13 @@ Created on Sun Jan 05 13:30:47 2014
|
||||||
import os
|
import os
|
||||||
os.chdir('../')
|
os.chdir('../')
|
||||||
|
|
||||||
from camlib import *
|
from flatcamParsers.ParseGerber import *
|
||||||
#from matplotlib.figure import Figure
|
from flatcamParsers.ParseExcellon import *
|
||||||
|
|
||||||
from matplotlib import pyplot
|
from matplotlib import pyplot
|
||||||
|
|
||||||
# Gerber. To see if the Excellon is correct
|
# Gerber. To see if the Excellon is correct
|
||||||
project_dir = "tests/"
|
project_dir = "tests/gerber_files"
|
||||||
gerber_filename = project_dir + "KiCad_Squarer-F_Cu.gtl"
|
gerber_filename = project_dir + "KiCad_Squarer-F_Cu.gtl"
|
||||||
g = Gerber()
|
g = Gerber()
|
||||||
g.parse_file(gerber_filename)
|
g.parse_file(gerber_filename)
|
||||||
|
@ -32,7 +33,7 @@ ax.set_aspect(1)
|
||||||
# Plot gerber
|
# Plot gerber
|
||||||
for geo in g.solid_geometry:
|
for geo in g.solid_geometry:
|
||||||
x, y = geo.exterior.coords.xy
|
x, y = geo.exterior.coords.xy
|
||||||
plot(x, y, 'k-')
|
pyplot.plot(x, y, 'k-')
|
||||||
for ints in geo.interiors:
|
for ints in geo.interiors:
|
||||||
x, y = ints.coords.xy
|
x, y = ints.coords.xy
|
||||||
ax.plot(x, y, 'k-')
|
ax.plot(x, y, 'k-')
|
||||||
|
@ -40,7 +41,7 @@ for geo in g.solid_geometry:
|
||||||
# Plot excellon
|
# Plot excellon
|
||||||
for geo in ex.solid_geometry:
|
for geo in ex.solid_geometry:
|
||||||
x, y = geo.exterior.coords.xy
|
x, y = geo.exterior.coords.xy
|
||||||
plot(x, y, 'r-')
|
pyplot.plot(x, y, 'r-')
|
||||||
for ints in geo.interiors:
|
for ints in geo.interiors:
|
||||||
x, y = ints.coords.xy
|
x, y = ints.coords.xy
|
||||||
ax.plot(x, y, 'g-')
|
ax.plot(x, y, 'g-')
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
from shapely.geometry import LineString, Polygon
|
from shapely.geometry import LineString, Polygon
|
||||||
from shapely.ops import cascaded_union, unary_union
|
from shapely.ops import cascaded_union, unary_union
|
||||||
from matplotlib.pyplot import plot, subplot, show
|
from matplotlib.pyplot import plot, subplot, show, axes
|
||||||
|
from matplotlib.axes import *
|
||||||
from camlib import *
|
from camlib import *
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import unittest
|
import unittest
|
||||||
import camlib
|
from flatcamParsers.ParseExcellon import Excellon
|
||||||
|
from flatcamParsers.ParseGerber import Gerber
|
||||||
|
|
||||||
|
|
||||||
class ExcellonNumberParseTestInch(unittest.TestCase):
|
class ExcellonNumberParseTestInch(unittest.TestCase):
|
||||||
|
@ -16,39 +17,39 @@ class ExcellonNumberParseTestInch(unittest.TestCase):
|
||||||
# of digits you typed and automatically fill in the missing zeros.
|
# of digits you typed and automatically fill in the missing zeros.
|
||||||
|
|
||||||
def test_inch_leading_6digit(self):
|
def test_inch_leading_6digit(self):
|
||||||
excellon = camlib.Excellon()
|
excellon = Excellon()
|
||||||
self.assertEqual(excellon.zeros, "L")
|
self.assertEqual(excellon.zeros, "L")
|
||||||
self.assertEqual(excellon.parse_number("123456"), 12.3456)
|
self.assertEqual(excellon.parse_number("123456"), 12.3456)
|
||||||
|
|
||||||
def test_inch_leading_5digit(self):
|
def test_inch_leading_5digit(self):
|
||||||
excellon = camlib.Excellon()
|
excellon = Excellon()
|
||||||
self.assertEqual(excellon.parse_number("12345"), 12.345)
|
self.assertEqual(excellon.parse_number("12345"), 12.345)
|
||||||
|
|
||||||
def test_inch_leading_15digit(self):
|
def test_inch_leading_15digit(self):
|
||||||
excellon = camlib.Excellon()
|
excellon = Excellon()
|
||||||
self.assertEqual(excellon.parse_number("012345"), 1.2345)
|
self.assertEqual(excellon.parse_number("012345"), 1.2345)
|
||||||
|
|
||||||
def test_inch_leading_51digit(self):
|
def test_inch_leading_51digit(self):
|
||||||
excellon = camlib.Excellon()
|
excellon = Excellon()
|
||||||
self.assertEqual(excellon.parse_number("123450"), 12.345)
|
self.assertEqual(excellon.parse_number("123450"), 12.345)
|
||||||
|
|
||||||
def test_inch_trailing_6digit(self):
|
def test_inch_trailing_6digit(self):
|
||||||
excellon = camlib.Excellon()
|
excellon = Excellon()
|
||||||
excellon.zeros = "T"
|
excellon.zeros = "T"
|
||||||
self.assertEqual(excellon.parse_number("123456"), 12.3456)
|
self.assertEqual(excellon.parse_number("123456"), 12.3456)
|
||||||
|
|
||||||
def test_inch_trailing_5digit(self):
|
def test_inch_trailing_5digit(self):
|
||||||
excellon = camlib.Excellon()
|
excellon = Excellon()
|
||||||
excellon.zeros = "T"
|
excellon.zeros = "T"
|
||||||
self.assertEqual(excellon.parse_number("12345"), 1.2345)
|
self.assertEqual(excellon.parse_number("12345"), 1.2345)
|
||||||
|
|
||||||
def test_inch_trailing_15digit(self):
|
def test_inch_trailing_15digit(self):
|
||||||
excellon = camlib.Excellon()
|
excellon = Excellon()
|
||||||
excellon.zeros = "T"
|
excellon.zeros = "T"
|
||||||
self.assertEqual(excellon.parse_number("012345"), 1.2345)
|
self.assertEqual(excellon.parse_number("012345"), 1.2345)
|
||||||
|
|
||||||
def test_inch_trailing_51digit(self):
|
def test_inch_trailing_51digit(self):
|
||||||
excellon = camlib.Excellon()
|
excellon = Excellon()
|
||||||
excellon.zeros = "T"
|
excellon.zeros = "T"
|
||||||
self.assertEqual(excellon.parse_number("123450"), 12.345)
|
self.assertEqual(excellon.parse_number("123450"), 12.345)
|
||||||
|
|
||||||
|
@ -67,45 +68,45 @@ class ExcellonNumberParseTestMetric(unittest.TestCase):
|
||||||
# of digits you typed and automatically fill in the missing zeros.
|
# of digits you typed and automatically fill in the missing zeros.
|
||||||
|
|
||||||
def test_inch_leading_6digit(self):
|
def test_inch_leading_6digit(self):
|
||||||
excellon = camlib.Excellon()
|
excellon = Excellon()
|
||||||
excellon.units = "mm"
|
excellon.units = "mm"
|
||||||
self.assertEqual(excellon.parse_number("123456"), 123.456)
|
self.assertEqual(excellon.parse_number("123456"), 123.456)
|
||||||
|
|
||||||
def test_inch_leading_5digit(self):
|
def test_inch_leading_5digit(self):
|
||||||
excellon = camlib.Excellon()
|
excellon = Excellon()
|
||||||
excellon.units = "mm"
|
excellon.units = "mm"
|
||||||
self.assertEqual(excellon.parse_number("12345"), 123.45)
|
self.assertEqual(excellon.parse_number("12345"), 123.45)
|
||||||
|
|
||||||
def test_inch_leading_15digit(self):
|
def test_inch_leading_15digit(self):
|
||||||
excellon = camlib.Excellon()
|
excellon = Excellon()
|
||||||
excellon.units = "mm"
|
excellon.units = "mm"
|
||||||
self.assertEqual(excellon.parse_number("012345"), 12.345)
|
self.assertEqual(excellon.parse_number("012345"), 12.345)
|
||||||
|
|
||||||
def test_inch_leading_51digit(self):
|
def test_inch_leading_51digit(self):
|
||||||
excellon = camlib.Excellon()
|
excellon = Excellon()
|
||||||
excellon.units = "mm"
|
excellon.units = "mm"
|
||||||
self.assertEqual(excellon.parse_number("123450"), 123.45)
|
self.assertEqual(excellon.parse_number("123450"), 123.45)
|
||||||
|
|
||||||
def test_inch_trailing_6digit(self):
|
def test_inch_trailing_6digit(self):
|
||||||
excellon = camlib.Excellon()
|
excellon = Excellon()
|
||||||
excellon.units = "mm"
|
excellon.units = "mm"
|
||||||
excellon.zeros = "T"
|
excellon.zeros = "T"
|
||||||
self.assertEqual(excellon.parse_number("123456"), 123.456)
|
self.assertEqual(excellon.parse_number("123456"), 123.456)
|
||||||
|
|
||||||
def test_inch_trailing_5digit(self):
|
def test_inch_trailing_5digit(self):
|
||||||
excellon = camlib.Excellon()
|
excellon = Excellon()
|
||||||
excellon.units = "mm"
|
excellon.units = "mm"
|
||||||
excellon.zeros = "T"
|
excellon.zeros = "T"
|
||||||
self.assertEqual(excellon.parse_number("12345"), 12.345)
|
self.assertEqual(excellon.parse_number("12345"), 12.345)
|
||||||
|
|
||||||
def test_inch_trailing_15digit(self):
|
def test_inch_trailing_15digit(self):
|
||||||
excellon = camlib.Excellon()
|
excellon = Excellon()
|
||||||
excellon.units = "mm"
|
excellon.units = "mm"
|
||||||
excellon.zeros = "T"
|
excellon.zeros = "T"
|
||||||
self.assertEqual(excellon.parse_number("012345"), 12.345)
|
self.assertEqual(excellon.parse_number("012345"), 12.345)
|
||||||
|
|
||||||
def test_inch_trailing_51digit(self):
|
def test_inch_trailing_51digit(self):
|
||||||
excellon = camlib.Excellon()
|
excellon = Excellon()
|
||||||
excellon.units = "mm"
|
excellon.units = "mm"
|
||||||
excellon.zeros = "T"
|
excellon.zeros = "T"
|
||||||
self.assertEqual(excellon.parse_number("123450"), 123.45)
|
self.assertEqual(excellon.parse_number("123450"), 123.45)
|
||||||
|
@ -114,7 +115,7 @@ class ExcellonNumberParseTestMetric(unittest.TestCase):
|
||||||
class ExcellonFormatM72Test(unittest.TestCase):
|
class ExcellonFormatM72Test(unittest.TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.excellon = camlib.Excellon()
|
self.excellon = Excellon()
|
||||||
code = """
|
code = """
|
||||||
M48
|
M48
|
||||||
M72
|
M72
|
||||||
|
@ -141,7 +142,7 @@ class ExcellonFormatM72Test(unittest.TestCase):
|
||||||
class ExcellonFormatM71Test(unittest.TestCase):
|
class ExcellonFormatM71Test(unittest.TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.excellon = camlib.Excellon()
|
self.excellon = Excellon()
|
||||||
code = """
|
code = """
|
||||||
M48
|
M48
|
||||||
M71
|
M71
|
||||||
|
@ -168,7 +169,7 @@ class ExcellonFormatM71Test(unittest.TestCase):
|
||||||
class ExcellonFormatINCHLZTest(unittest.TestCase):
|
class ExcellonFormatINCHLZTest(unittest.TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.excellon = camlib.Excellon()
|
self.excellon = Excellon()
|
||||||
code = """
|
code = """
|
||||||
M48
|
M48
|
||||||
INCH,LZ
|
INCH,LZ
|
||||||
|
@ -195,7 +196,7 @@ class ExcellonFormatINCHLZTest(unittest.TestCase):
|
||||||
class ExcellonFormatINCHTest(unittest.TestCase):
|
class ExcellonFormatINCHTest(unittest.TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.excellon = camlib.Excellon()
|
self.excellon = Excellon()
|
||||||
code = """
|
code = """
|
||||||
M48
|
M48
|
||||||
INCH,LZ
|
INCH,LZ
|
||||||
|
@ -222,7 +223,7 @@ class ExcellonFormatINCHTest(unittest.TestCase):
|
||||||
class ExcellonFormatINCHTZTest(unittest.TestCase):
|
class ExcellonFormatINCHTZTest(unittest.TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.excellon = camlib.Excellon()
|
self.excellon = Excellon()
|
||||||
code = """
|
code = """
|
||||||
M48
|
M48
|
||||||
INCH,TZ
|
INCH,TZ
|
||||||
|
@ -249,7 +250,7 @@ class ExcellonFormatINCHTZTest(unittest.TestCase):
|
||||||
class ExcellonFormatMETRICLZTest(unittest.TestCase):
|
class ExcellonFormatMETRICLZTest(unittest.TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.excellon = camlib.Excellon()
|
self.excellon = Excellon()
|
||||||
code = """
|
code = """
|
||||||
M48
|
M48
|
||||||
METRIC,LZ
|
METRIC,LZ
|
||||||
|
@ -276,7 +277,7 @@ class ExcellonFormatMETRICLZTest(unittest.TestCase):
|
||||||
class ExcellonFormatMETRICTest(unittest.TestCase):
|
class ExcellonFormatMETRICTest(unittest.TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.excellon = camlib.Excellon()
|
self.excellon = Excellon()
|
||||||
code = """
|
code = """
|
||||||
M48
|
M48
|
||||||
METRIC,LZ
|
METRIC,LZ
|
||||||
|
@ -303,7 +304,7 @@ class ExcellonFormatMETRICTest(unittest.TestCase):
|
||||||
class ExcellonFormatMETRICTZTest(unittest.TestCase):
|
class ExcellonFormatMETRICTZTest(unittest.TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.excellon = camlib.Excellon()
|
self.excellon = Excellon()
|
||||||
code = """
|
code = """
|
||||||
M48
|
M48
|
||||||
METRIC,TZ
|
METRIC,TZ
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import unittest
|
import unittest
|
||||||
from PyQt4 import QtGui
|
from PyQt5 import QtGui, QtWidgets
|
||||||
import sys
|
import sys
|
||||||
from FlatCAMApp import App
|
from FlatCAMApp import App
|
||||||
from FlatCAMObj import FlatCAMExcellon, FlatCAMCNCjob
|
from FlatCAMObj import FlatCAMExcellon, FlatCAMCNCjob
|
||||||
from ObjectUI import ExcellonObjectUI
|
from flatcamGUI.ObjectUI import ExcellonObjectUI
|
||||||
import tempfile
|
import tempfile
|
||||||
import os
|
import os
|
||||||
from time import sleep
|
from time import sleep
|
||||||
|
@ -21,7 +21,7 @@ class ExcellonFlowTestCase(unittest.TestCase):
|
||||||
filename = 'case1.drl'
|
filename = 'case1.drl'
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.app = QtGui.QApplication(sys.argv)
|
self.app = QtWidgets.QApplication(sys.argv)
|
||||||
|
|
||||||
# Create App, keep app defaults (do not load
|
# Create App, keep app defaults (do not load
|
||||||
# user-defined defaults).
|
# user-defined defaults).
|
||||||
|
@ -79,19 +79,19 @@ class ExcellonFlowTestCase(unittest.TestCase):
|
||||||
option, value, form_field.get_value()
|
option, value, form_field.get_value()
|
||||||
))
|
))
|
||||||
|
|
||||||
#--------------------------------------------------
|
# --------------------------------------------------
|
||||||
# Changes in the GUI should be read in when
|
# Changes in the GUI should be read in when
|
||||||
# running any process. Changing something here.
|
# running any process. Changing something here.
|
||||||
#--------------------------------------------------
|
# --------------------------------------------------
|
||||||
|
|
||||||
form_field = excellon_obj.form_fields['feedrate']
|
form_field = excellon_obj.form_fields['feedrate']
|
||||||
value = form_field.get_value()
|
value = form_field.get_value()
|
||||||
form_field.set_value(value * 1.1) # Increase by 10%
|
form_field.set_value(value * 1.1) # Increase by 10%
|
||||||
print(("'feedrate' == {}".format(value)))
|
print(("'feedrate' == {}".format(value)))
|
||||||
|
|
||||||
#--------------------------------------------------
|
# --------------------------------------------------
|
||||||
# Create GCode using all tools.
|
# Create GCode using all tools.
|
||||||
#--------------------------------------------------
|
# --------------------------------------------------
|
||||||
|
|
||||||
assert isinstance(excellon_obj, FlatCAMExcellon) # Just for the IDE
|
assert isinstance(excellon_obj, FlatCAMExcellon) # Just for the IDE
|
||||||
ui = excellon_obj.ui
|
ui = excellon_obj.ui
|
||||||
|
@ -110,9 +110,9 @@ class ExcellonFlowTestCase(unittest.TestCase):
|
||||||
sleep(0.1)
|
sleep(0.1)
|
||||||
self.app.processEvents()
|
self.app.processEvents()
|
||||||
|
|
||||||
#---------------------------------------------
|
# ---------------------------------------------
|
||||||
# Check that GUI has been read in.
|
# Check that GUI has been read in.
|
||||||
#---------------------------------------------
|
# ---------------------------------------------
|
||||||
|
|
||||||
value = excellon_obj.options['feedrate']
|
value = excellon_obj.options['feedrate']
|
||||||
form_value = form_field.get_value()
|
form_value = form_field.get_value()
|
||||||
|
@ -121,33 +121,33 @@ class ExcellonFlowTestCase(unittest.TestCase):
|
||||||
"which has {}".format('feedrate', form_value, value))
|
"which has {}".format('feedrate', form_value, value))
|
||||||
print(("'feedrate' == {}".format(value)))
|
print(("'feedrate' == {}".format(value)))
|
||||||
|
|
||||||
#---------------------------------------------
|
# ---------------------------------------------
|
||||||
# Check that only 1 object has been created.
|
# Check that only 1 object has been created.
|
||||||
#---------------------------------------------
|
# ---------------------------------------------
|
||||||
|
|
||||||
names = self.fc.collection.get_names()
|
names = self.fc.collection.get_names()
|
||||||
self.assertEqual(len(names), 2,
|
self.assertEqual(len(names), 2,
|
||||||
"Expected 2 objects, found %d" % len(names))
|
"Expected 2 objects, found %d" % len(names))
|
||||||
|
|
||||||
#-------------------------------------------------------
|
# -------------------------------------------------------
|
||||||
# Make sure the CNCJob Object has the correct name
|
# Make sure the CNCJob Object has the correct name
|
||||||
#-------------------------------------------------------
|
# -------------------------------------------------------
|
||||||
|
|
||||||
cncjob_name = excellon_name + "_cnc"
|
cncjob_name = excellon_name + "_cnc"
|
||||||
self.assertTrue(cncjob_name in names,
|
self.assertTrue(cncjob_name in names,
|
||||||
"Object named %s not found." % cncjob_name)
|
"Object named %s not found." % cncjob_name)
|
||||||
|
|
||||||
#-------------------------------------------------------
|
# -------------------------------------------------------
|
||||||
# Get the object make sure it's a cncjob object
|
# Get the object make sure it's a cncjob object
|
||||||
#-------------------------------------------------------
|
# -------------------------------------------------------
|
||||||
|
|
||||||
cncjob_obj = self.fc.collection.get_by_name(cncjob_name)
|
cncjob_obj = self.fc.collection.get_by_name(cncjob_name)
|
||||||
self.assertTrue(isinstance(cncjob_obj, FlatCAMCNCjob),
|
self.assertTrue(isinstance(cncjob_obj, FlatCAMCNCjob),
|
||||||
"Expected a FlatCAMCNCjob, got %s" % type(cncjob_obj))
|
"Expected a FlatCAMCNCjob, got %s" % type(cncjob_obj))
|
||||||
|
|
||||||
#-----------------------------------------
|
# -----------------------------------------
|
||||||
# Export G-Code, check output
|
# Export G-Code, check output
|
||||||
#-----------------------------------------
|
# -----------------------------------------
|
||||||
assert isinstance(cncjob_obj, FlatCAMCNCjob) # For IDE
|
assert isinstance(cncjob_obj, FlatCAMCNCjob) # For IDE
|
||||||
|
|
||||||
# get system temporary file(try create it and delete)
|
# get system temporary file(try create it and delete)
|
||||||
|
|
|
@ -1,15 +1,16 @@
|
||||||
import unittest
|
import unittest
|
||||||
import camlib
|
import camlib
|
||||||
|
from flatcamParsers.ParseGerber import Gerber
|
||||||
|
|
||||||
|
|
||||||
class GerberBuffer(unittest.TestCase):
|
class GerberBuffer(unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.gerber1 = camlib.Gerber()
|
self.gerber1 = Gerber()
|
||||||
self.gerber1.use_buffer_for_union = True
|
self.gerber1.use_buffer_for_union = True
|
||||||
self.gerber1.parse_file("tests/gerber_files/STM32F4-spindle.cmp")
|
self.gerber1.parse_file("tests/gerber_files/STM32F4-spindle.cmp")
|
||||||
geometry1 = self.gerber1.solid_geometry
|
geometry1 = self.gerber1.solid_geometry
|
||||||
self.geometry1_area = self.compute_area(geometry1)
|
self.geometry1_area = self.compute_area(geometry1)
|
||||||
self.gerber2 = camlib.Gerber()
|
self.gerber2 = Gerber()
|
||||||
self.gerber2.use_buffer_for_union = False
|
self.gerber2.use_buffer_for_union = False
|
||||||
self.gerber2.parse_file("tests/gerber_files/STM32F4-spindle.cmp")
|
self.gerber2.parse_file("tests/gerber_files/STM32F4-spindle.cmp")
|
||||||
geometry2 = self.gerber2.solid_geometry
|
geometry2 = self.gerber2.solid_geometry
|
||||||
|
@ -21,7 +22,7 @@ class GerberBuffer(unittest.TestCase):
|
||||||
for geo in geometry:
|
for geo in geometry:
|
||||||
area += geo.area
|
area += geo.area
|
||||||
|
|
||||||
## Not iterable, do the actual indexing and add.
|
# Not iterable, do the actual indexing and add.
|
||||||
except TypeError:
|
except TypeError:
|
||||||
area = geometry.area
|
area = geometry.area
|
||||||
return area
|
return area
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import sys
|
import sys
|
||||||
import unittest
|
import unittest
|
||||||
from PyQt4 import QtGui
|
from PyQt5 import QtGui, QtWidgets
|
||||||
from FlatCAMApp import App, tclCommands
|
from FlatCAMApp import App, tclCommands
|
||||||
from FlatCAMObj import FlatCAMGerber, FlatCAMGeometry, FlatCAMCNCjob
|
from FlatCAMObj import FlatCAMGerber, FlatCAMGeometry, FlatCAMCNCjob
|
||||||
from ObjectUI import GerberObjectUI, GeometryObjectUI
|
from flatcamGUI.ObjectUI import GerberObjectUI, GeometryObjectUI
|
||||||
from time import sleep
|
from time import sleep
|
||||||
import os
|
import os
|
||||||
import tempfile
|
import tempfile
|
||||||
|
@ -21,7 +21,7 @@ class GerberFlowTestCase(unittest.TestCase):
|
||||||
filename = 'simple1.gbr'
|
filename = 'simple1.gbr'
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.app = QtGui.QApplication(sys.argv)
|
self.app = QtWidgets.QApplication(sys.argv)
|
||||||
|
|
||||||
# Create App, keep app defaults (do not load
|
# Create App, keep app defaults (do not load
|
||||||
# user-defined defaults).
|
# user-defined defaults).
|
||||||
|
@ -38,30 +38,30 @@ class GerberFlowTestCase(unittest.TestCase):
|
||||||
names = self.fc.collection.get_names()
|
names = self.fc.collection.get_names()
|
||||||
print(names)
|
print(names)
|
||||||
|
|
||||||
#--------------------------------------
|
# --------------------------------------
|
||||||
# Total of 1 objects.
|
# Total of 1 objects.
|
||||||
#--------------------------------------
|
# --------------------------------------
|
||||||
self.assertEqual(len(names), 1,
|
self.assertEqual(len(names), 1,
|
||||||
"Expected 1 object, found %d" % len(names))
|
"Expected 1 object, found %d" % len(names))
|
||||||
|
|
||||||
#--------------------------------------
|
# --------------------------------------
|
||||||
# Object's name matches the file name.
|
# Object's name matches the file name.
|
||||||
#--------------------------------------
|
# --------------------------------------
|
||||||
self.assertEqual(names[0], self.filename,
|
self.assertEqual(names[0], self.filename,
|
||||||
"Expected name == %s, got %s" % (self.filename, names[0]))
|
"Expected name == %s, got %s" % (self.filename, names[0]))
|
||||||
|
|
||||||
#---------------------------------------
|
# ---------------------------------------
|
||||||
# Get object by that name, make sure it's a FlatCAMGerber.
|
# Get object by that name, make sure it's a FlatCAMGerber.
|
||||||
#---------------------------------------
|
# ---------------------------------------
|
||||||
gerber_name = names[0]
|
gerber_name = names[0]
|
||||||
gerber_obj = self.fc.collection.get_by_name(gerber_name)
|
gerber_obj = self.fc.collection.get_by_name(gerber_name)
|
||||||
self.assertTrue(isinstance(gerber_obj, FlatCAMGerber),
|
self.assertTrue(isinstance(gerber_obj, FlatCAMGerber),
|
||||||
"Expected FlatCAMGerber, instead, %s is %s" %
|
"Expected FlatCAMGerber, instead, %s is %s" %
|
||||||
(gerber_name, type(gerber_obj)))
|
(gerber_name, type(gerber_obj)))
|
||||||
|
|
||||||
#----------------------------------------
|
# ----------------------------------------
|
||||||
# Object's GUI matches Object's options
|
# Object's GUI matches Object's options
|
||||||
#----------------------------------------
|
# ----------------------------------------
|
||||||
# TODO: Open GUI with double-click on object.
|
# TODO: Open GUI with double-click on object.
|
||||||
# Opens the Object's GUI, populates it.
|
# Opens the Object's GUI, populates it.
|
||||||
gerber_obj.build_ui()
|
gerber_obj.build_ui()
|
||||||
|
@ -79,20 +79,20 @@ class GerberFlowTestCase(unittest.TestCase):
|
||||||
option, value, form_field.get_value()
|
option, value, form_field.get_value()
|
||||||
))
|
))
|
||||||
|
|
||||||
#--------------------------------------------------
|
# --------------------------------------------------
|
||||||
# Changes in the GUI should be read in when
|
# Changes in the GUI should be read in when
|
||||||
# running any process. Changing something here.
|
# running any process. Changing something here.
|
||||||
#--------------------------------------------------
|
# --------------------------------------------------
|
||||||
|
|
||||||
form_field = gerber_obj.form_fields['isotooldia']
|
form_field = gerber_obj.form_fields['isotooldia']
|
||||||
value = form_field.get_value()
|
value = form_field.get_value()
|
||||||
form_field.set_value(value * 1.1) # Increase by 10%
|
form_field.set_value(value * 1.1) # Increase by 10%
|
||||||
print(("'isotooldia' == {}".format(value)))
|
print(("'isotooldia' == {}".format(value)))
|
||||||
|
|
||||||
#--------------------------------------------------
|
# --------------------------------------------------
|
||||||
# Create isolation routing using default values
|
# Create isolation routing using default values
|
||||||
# and by clicking on the button.
|
# and by clicking on the button.
|
||||||
#--------------------------------------------------
|
# --------------------------------------------------
|
||||||
# Get the object's GUI and click on "Generate Geometry" under
|
# Get the object's GUI and click on "Generate Geometry" under
|
||||||
# "Isolation Routing"
|
# "Isolation Routing"
|
||||||
assert isinstance(gerber_obj, FlatCAMGerber) # Just for the IDE
|
assert isinstance(gerber_obj, FlatCAMGerber) # Just for the IDE
|
||||||
|
@ -102,9 +102,9 @@ class GerberFlowTestCase(unittest.TestCase):
|
||||||
assert isinstance(ui, GerberObjectUI)
|
assert isinstance(ui, GerberObjectUI)
|
||||||
ui.generate_iso_button.click() # Click
|
ui.generate_iso_button.click() # Click
|
||||||
|
|
||||||
#---------------------------------------------
|
# ---------------------------------------------
|
||||||
# Check that GUI has been read in.
|
# Check that GUI has been read in.
|
||||||
#---------------------------------------------
|
# ---------------------------------------------
|
||||||
value = gerber_obj.options['isotooldia']
|
value = gerber_obj.options['isotooldia']
|
||||||
form_value = form_field.get_value()
|
form_value = form_field.get_value()
|
||||||
self.assertEqual(value, form_value,
|
self.assertEqual(value, form_value,
|
||||||
|
@ -112,30 +112,30 @@ class GerberFlowTestCase(unittest.TestCase):
|
||||||
"which has {}".format('isotooldia', form_value, value))
|
"which has {}".format('isotooldia', form_value, value))
|
||||||
print(("'isotooldia' == {}".format(value)))
|
print(("'isotooldia' == {}".format(value)))
|
||||||
|
|
||||||
#---------------------------------------------
|
# ---------------------------------------------
|
||||||
# Check that only 1 object has been created.
|
# Check that only 1 object has been created.
|
||||||
#---------------------------------------------
|
# ---------------------------------------------
|
||||||
names = self.fc.collection.get_names()
|
names = self.fc.collection.get_names()
|
||||||
self.assertEqual(len(names), 2,
|
self.assertEqual(len(names), 2,
|
||||||
"Expected 2 objects, found %d" % len(names))
|
"Expected 2 objects, found %d" % len(names))
|
||||||
|
|
||||||
#-------------------------------------------------------
|
# -------------------------------------------------------
|
||||||
# Make sure the Geometry Object has the correct name
|
# Make sure the Geometry Object has the correct name
|
||||||
#-------------------------------------------------------
|
# -------------------------------------------------------
|
||||||
geo_name = gerber_name + "_iso"
|
geo_name = gerber_name + "_iso"
|
||||||
self.assertTrue(geo_name in names,
|
self.assertTrue(geo_name in names,
|
||||||
"Object named %s not found." % geo_name)
|
"Object named %s not found." % geo_name)
|
||||||
|
|
||||||
#-------------------------------------------------------
|
# -------------------------------------------------------
|
||||||
# Get the object make sure it's a geometry object
|
# Get the object make sure it's a geometry object
|
||||||
#-------------------------------------------------------
|
# -------------------------------------------------------
|
||||||
geo_obj = self.fc.collection.get_by_name(geo_name)
|
geo_obj = self.fc.collection.get_by_name(geo_name)
|
||||||
self.assertTrue(isinstance(geo_obj, FlatCAMGeometry),
|
self.assertTrue(isinstance(geo_obj, FlatCAMGeometry),
|
||||||
"Expected a FlatCAMGeometry, got %s" % type(geo_obj))
|
"Expected a FlatCAMGeometry, got %s" % type(geo_obj))
|
||||||
|
|
||||||
#------------------------------------
|
# ------------------------------------
|
||||||
# Open the UI, make CNCObject
|
# Open the UI, make CNCObject
|
||||||
#------------------------------------
|
# ------------------------------------
|
||||||
geo_obj.build_ui()
|
geo_obj.build_ui()
|
||||||
ui = geo_obj.ui
|
ui = geo_obj.ui
|
||||||
assert isinstance(ui, GeometryObjectUI) # Just for the IDE
|
assert isinstance(ui, GeometryObjectUI) # Just for the IDE
|
||||||
|
@ -152,30 +152,30 @@ class GerberFlowTestCase(unittest.TestCase):
|
||||||
sleep(0.1)
|
sleep(0.1)
|
||||||
self.app.processEvents()
|
self.app.processEvents()
|
||||||
|
|
||||||
#---------------------------------------------
|
# ---------------------------------------------
|
||||||
# Check that only 1 object has been created.
|
# Check that only 1 object has been created.
|
||||||
#---------------------------------------------
|
# ---------------------------------------------
|
||||||
names = self.fc.collection.get_names()
|
names = self.fc.collection.get_names()
|
||||||
self.assertEqual(len(names), 3,
|
self.assertEqual(len(names), 3,
|
||||||
"Expected 3 objects, found %d" % len(names))
|
"Expected 3 objects, found %d" % len(names))
|
||||||
|
|
||||||
#-------------------------------------------------------
|
# -------------------------------------------------------
|
||||||
# Make sure the CNC Job Object has the correct name
|
# Make sure the CNC Job Object has the correct name
|
||||||
#-------------------------------------------------------
|
# -------------------------------------------------------
|
||||||
cnc_name = geo_name + "_cnc"
|
cnc_name = geo_name + "_cnc"
|
||||||
self.assertTrue(cnc_name in names,
|
self.assertTrue(cnc_name in names,
|
||||||
"Object named %s not found." % geo_name)
|
"Object named %s not found." % geo_name)
|
||||||
|
|
||||||
#-------------------------------------------------------
|
# -------------------------------------------------------
|
||||||
# Get the object make sure it's a CNC Job object
|
# Get the object make sure it's a CNC Job object
|
||||||
#-------------------------------------------------------
|
# -------------------------------------------------------
|
||||||
cnc_obj = self.fc.collection.get_by_name(cnc_name)
|
cnc_obj = self.fc.collection.get_by_name(cnc_name)
|
||||||
self.assertTrue(isinstance(cnc_obj, FlatCAMCNCjob),
|
self.assertTrue(isinstance(cnc_obj, FlatCAMCNCjob),
|
||||||
"Expected a FlatCAMCNCJob, got %s" % type(geo_obj))
|
"Expected a FlatCAMCNCJob, got %s" % type(geo_obj))
|
||||||
|
|
||||||
#-----------------------------------------
|
# -----------------------------------------
|
||||||
# Export G-Code, check output
|
# Export G-Code, check output
|
||||||
#-----------------------------------------
|
# -----------------------------------------
|
||||||
assert isinstance(cnc_obj, FlatCAMCNCjob)
|
assert isinstance(cnc_obj, FlatCAMCNCjob)
|
||||||
output_filename = ""
|
output_filename = ""
|
||||||
# get system temporary file(try create it and delete also)
|
# get system temporary file(try create it and delete also)
|
||||||
|
|
|
@ -3,6 +3,7 @@ import unittest
|
||||||
from shapely.geometry import LineString, Polygon
|
from shapely.geometry import LineString, Polygon
|
||||||
from shapely.ops import cascaded_union, unary_union
|
from shapely.ops import cascaded_union, unary_union
|
||||||
from matplotlib.pyplot import plot, subplot, show, cla, clf, xlim, ylim, title
|
from matplotlib.pyplot import plot, subplot, show, cla, clf, xlim, ylim, title
|
||||||
|
from matplotlib.axes import *
|
||||||
from camlib import *
|
from camlib import *
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
|
|
||||||
|
|
|
@ -84,5 +84,6 @@ class PathConnectTest1(unittest.TestCase):
|
||||||
matches = [p for p in result if p.equals(LineString([[0, 0], [1, 1], [2, 1]]))]
|
matches = [p for p in result if p.equals(LineString([[0, 0], [1, 1], [2, 1]]))]
|
||||||
self.assertEqual(len(matches), 1)
|
self.assertEqual(len(matches), 1)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import sys
|
import sys
|
||||||
import unittest
|
import unittest
|
||||||
from PyQt4 import QtGui
|
from PyQt5 import QtGui, QtWidgets
|
||||||
from FlatCAMApp import App
|
from FlatCAMApp import App
|
||||||
from FlatCAMObj import FlatCAMGeometry, FlatCAMCNCjob
|
from FlatCAMObj import FlatCAMGeometry, FlatCAMCNCjob
|
||||||
from ObjectUI import GerberObjectUI, GeometryObjectUI
|
from flatcamGUI.ObjectUI import GerberObjectUI, GeometryObjectUI
|
||||||
from time import sleep
|
from time import sleep
|
||||||
import os
|
import os
|
||||||
import tempfile
|
import tempfile
|
||||||
|
@ -13,7 +13,7 @@ from shapely.geometry import LineString, LinearRing, Polygon, MultiPolygon
|
||||||
class PolyPaintTestCase(unittest.TestCase):
|
class PolyPaintTestCase(unittest.TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.app = QtGui.QApplication(sys.argv)
|
self.app = QtWidgets.QApplication(sys.argv)
|
||||||
|
|
||||||
# Create App, keep app defaults (do not load
|
# Create App, keep app defaults (do not load
|
||||||
# user-defined defaults).
|
# user-defined defaults).
|
||||||
|
@ -218,10 +218,3 @@ class PolyPaintTestCase(unittest.TestCase):
|
||||||
self.assertTrue(isinstance(geo, LineString))
|
self.assertTrue(isinstance(geo, LineString))
|
||||||
# Lots of points (Should be 1000s)
|
# Lots of points (Should be 1000s)
|
||||||
self.assertGreater(len(geo.coords), 2)
|
self.assertGreater(len(geo.coords), 2)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import sys
|
import sys
|
||||||
import unittest
|
import unittest
|
||||||
from PyQt4 import QtGui
|
from PyQt5 import QtWidgets
|
||||||
from FlatCAMApp import App
|
from FlatCAMApp import App
|
||||||
from FlatCAMObj import FlatCAMGeometry, FlatCAMCNCjob
|
from FlatCAMObj import FlatCAMGeometry, FlatCAMCNCjob
|
||||||
from ObjectUI import GerberObjectUI, GeometryObjectUI
|
from flatcamGUI.ObjectUI import GerberObjectUI, GeometryObjectUI
|
||||||
from time import sleep
|
from time import sleep
|
||||||
import os
|
import os
|
||||||
import tempfile
|
import tempfile
|
||||||
|
@ -12,7 +12,7 @@ import tempfile
|
||||||
class SVGFlowTestCase(unittest.TestCase):
|
class SVGFlowTestCase(unittest.TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.app = QtGui.QApplication(sys.argv)
|
self.app = QtWidgets.QApplication(sys.argv)
|
||||||
|
|
||||||
# Create App, keep app defaults (do not load
|
# Create App, keep app defaults (do not load
|
||||||
# user-defined defaults).
|
# user-defined defaults).
|
||||||
|
@ -31,30 +31,28 @@ class SVGFlowTestCase(unittest.TestCase):
|
||||||
names = self.fc.collection.get_names()
|
names = self.fc.collection.get_names()
|
||||||
print(names)
|
print(names)
|
||||||
|
|
||||||
#--------------------------------------
|
# --------------------------------------
|
||||||
# Total of 1 objects.
|
# Total of 1 objects.
|
||||||
#--------------------------------------
|
# --------------------------------------
|
||||||
self.assertEqual(len(names), 1,
|
self.assertEqual(len(names), 1, "Expected 1 object, found %d" % len(names))
|
||||||
"Expected 1 object, found %d" % len(names))
|
|
||||||
|
|
||||||
#--------------------------------------
|
# --------------------------------------
|
||||||
# Object's name matches the file name.
|
# Object's name matches the file name.
|
||||||
#--------------------------------------
|
# --------------------------------------
|
||||||
self.assertEqual(names[0], self.filename,
|
self.assertEqual(names[0], self.filename, "Expected name == %s, got %s" % (self.filename, names[0]))
|
||||||
"Expected name == %s, got %s" % (self.filename, names[0]))
|
|
||||||
|
|
||||||
#---------------------------------------
|
# ---------------------------------------
|
||||||
# Get object by that name, make sure it's a FlatCAMGerber.
|
# Get object by that name, make sure it's a FlatCAMGerber.
|
||||||
#---------------------------------------
|
# ---------------------------------------
|
||||||
geo_name = names[0]
|
geo_name = names[0]
|
||||||
geo_obj = self.fc.collection.get_by_name(geo_name)
|
geo_obj = self.fc.collection.get_by_name(geo_name)
|
||||||
self.assertTrue(isinstance(geo_obj, FlatCAMGeometry),
|
self.assertTrue(isinstance(geo_obj, FlatCAMGeometry),
|
||||||
"Expected FlatCAMGeometry, instead, %s is %s" %
|
"Expected FlatCAMGeometry, instead, %s is %s" %
|
||||||
(geo_name, type(geo_obj)))
|
(geo_name, type(geo_obj)))
|
||||||
|
|
||||||
#----------------------------------------
|
# ----------------------------------------
|
||||||
# Object's GUI matches Object's options
|
# Object's GUI matches Object's options
|
||||||
#----------------------------------------
|
# ----------------------------------------
|
||||||
# TODO: Open GUI with double-click on object.
|
# TODO: Open GUI with double-click on object.
|
||||||
# Opens the Object's GUI, populates it.
|
# Opens the Object's GUI, populates it.
|
||||||
geo_obj.build_ui()
|
geo_obj.build_ui()
|
||||||
|
@ -72,9 +70,9 @@ class SVGFlowTestCase(unittest.TestCase):
|
||||||
option, value, form_field.get_value()
|
option, value, form_field.get_value()
|
||||||
))
|
))
|
||||||
|
|
||||||
#------------------------------------
|
# ------------------------------------
|
||||||
# Open the UI, make CNCObject
|
# Open the UI, make CNCObject
|
||||||
#------------------------------------
|
# ------------------------------------
|
||||||
geo_obj.build_ui()
|
geo_obj.build_ui()
|
||||||
ui = geo_obj.ui
|
ui = geo_obj.ui
|
||||||
assert isinstance(ui, GeometryObjectUI) # Just for the IDE
|
assert isinstance(ui, GeometryObjectUI) # Just for the IDE
|
||||||
|
@ -91,30 +89,30 @@ class SVGFlowTestCase(unittest.TestCase):
|
||||||
sleep(0.1)
|
sleep(0.1)
|
||||||
self.app.processEvents()
|
self.app.processEvents()
|
||||||
|
|
||||||
#---------------------------------------------
|
# ---------------------------------------------
|
||||||
# Check that only 1 object has been created.
|
# Check that only 1 object has been created.
|
||||||
#---------------------------------------------
|
# ---------------------------------------------
|
||||||
names = self.fc.collection.get_names()
|
names = self.fc.collection.get_names()
|
||||||
self.assertEqual(len(names), 2,
|
self.assertEqual(len(names), 2,
|
||||||
"Expected 2 objects, found %d" % len(names))
|
"Expected 2 objects, found %d" % len(names))
|
||||||
|
|
||||||
#-------------------------------------------------------
|
# -------------------------------------------------------
|
||||||
# Make sure the CNC Job Object has the correct name
|
# Make sure the CNC Job Object has the correct name
|
||||||
#-------------------------------------------------------
|
# -------------------------------------------------------
|
||||||
cnc_name = geo_name + "_cnc"
|
cnc_name = geo_name + "_cnc"
|
||||||
self.assertTrue(cnc_name in names,
|
self.assertTrue(cnc_name in names,
|
||||||
"Object named %s not found." % geo_name)
|
"Object named %s not found." % geo_name)
|
||||||
|
|
||||||
#-------------------------------------------------------
|
# -------------------------------------------------------
|
||||||
# Get the object make sure it's a CNC Job object
|
# Get the object make sure it's a CNC Job object
|
||||||
#-------------------------------------------------------
|
# -------------------------------------------------------
|
||||||
cnc_obj = self.fc.collection.get_by_name(cnc_name)
|
cnc_obj = self.fc.collection.get_by_name(cnc_name)
|
||||||
self.assertTrue(isinstance(cnc_obj, FlatCAMCNCjob),
|
self.assertTrue(isinstance(cnc_obj, FlatCAMCNCjob),
|
||||||
"Expected a FlatCAMCNCJob, got %s" % type(geo_obj))
|
"Expected a FlatCAMCNCJob, got %s" % type(geo_obj))
|
||||||
|
|
||||||
#-----------------------------------------
|
# -----------------------------------------
|
||||||
# Export G-Code, check output
|
# Export G-Code, check output
|
||||||
#-----------------------------------------
|
# -----------------------------------------
|
||||||
assert isinstance(cnc_obj, FlatCAMCNCjob)
|
assert isinstance(cnc_obj, FlatCAMCNCjob)
|
||||||
output_filename = ""
|
output_filename = ""
|
||||||
# get system temporary file(try create it and delete also)
|
# get system temporary file(try create it and delete also)
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
import sys
|
import sys
|
||||||
import unittest
|
import unittest
|
||||||
from PyQt4 import QtGui
|
from PyQt5 import QtWidgets, QtGui
|
||||||
from PyQt4.QtCore import QThread
|
from PyQt5.QtCore import QThread
|
||||||
|
|
||||||
from FlatCAMApp import App
|
from FlatCAMApp import App
|
||||||
from os import listdir
|
from os import listdir
|
||||||
from os.path import isfile
|
from os.path import isfile
|
||||||
from FlatCAMObj import FlatCAMGerber, FlatCAMGeometry, FlatCAMCNCjob, FlatCAMExcellon
|
from FlatCAMObj import FlatCAMGerber, FlatCAMGeometry, FlatCAMCNCjob, FlatCAMExcellon
|
||||||
from ObjectUI import GerberObjectUI, GeometryObjectUI
|
from flatcamGUI.ObjectUI import GerberObjectUI, GeometryObjectUI
|
||||||
from time import sleep
|
from time import sleep
|
||||||
import os
|
import os
|
||||||
import tempfile
|
import tempfile
|
||||||
|
@ -36,13 +36,13 @@ class TclShellTest(unittest.TestCase):
|
||||||
# reason for this is reuse one test window only,
|
# reason for this is reuse one test window only,
|
||||||
|
|
||||||
# CANNOT DO THIS HERE!!!
|
# CANNOT DO THIS HERE!!!
|
||||||
#from tests.test_tclCommands import *
|
# from tests.test_tclCommands import *
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpClass(cls):
|
def setUpClass(cls):
|
||||||
|
|
||||||
cls.setup = True
|
cls.setup = True
|
||||||
cls.app = QtGui.QApplication(sys.argv)
|
cls.app = QtWidgets.QApplication(sys.argv)
|
||||||
|
|
||||||
# Create App, keep app defaults (do not load
|
# Create App, keep app defaults (do not load
|
||||||
# user-defined defaults).
|
# user-defined defaults).
|
||||||
|
@ -78,9 +78,9 @@ class TclShellTest(unittest.TestCase):
|
||||||
self.fc.exec_command_test('set_sys units IN')
|
self.fc.exec_command_test('set_sys units IN')
|
||||||
self.fc.exec_command_test('new')
|
self.fc.exec_command_test('new')
|
||||||
|
|
||||||
#----------------------------------------
|
# ----------------------------------------
|
||||||
# Units must be IN
|
# Units must be IN
|
||||||
#----------------------------------------
|
# ----------------------------------------
|
||||||
units = self.fc.exec_command_test('get_sys units')
|
units = self.fc.exec_command_test('get_sys units')
|
||||||
self.assertEqual(units, "IN")
|
self.assertEqual(units, "IN")
|
||||||
|
|
||||||
|
@ -88,9 +88,9 @@ class TclShellTest(unittest.TestCase):
|
||||||
self.fc.exec_command_test('set_sys units MM')
|
self.fc.exec_command_test('set_sys units MM')
|
||||||
self.fc.exec_command_test('new')
|
self.fc.exec_command_test('new')
|
||||||
|
|
||||||
#----------------------------------------
|
# ----------------------------------------
|
||||||
# Units must be MM
|
# Units must be MM
|
||||||
#----------------------------------------
|
# ----------------------------------------
|
||||||
units = self.fc.exec_command_test('get_sys units')
|
units = self.fc.exec_command_test('get_sys units')
|
||||||
self.assertEqual(units, "MM")
|
self.assertEqual(units, "MM")
|
||||||
|
|
||||||
|
@ -103,9 +103,9 @@ class TclShellTest(unittest.TestCase):
|
||||||
|
|
||||||
gbr_cmd = 'open_gerber {path}/{filename} -outname {outname}'
|
gbr_cmd = 'open_gerber {path}/{filename} -outname {outname}'
|
||||||
|
|
||||||
#-----------------------------------------
|
# -----------------------------------------
|
||||||
# Open top layer and check for object type
|
# Open top layer and check for object type
|
||||||
#-----------------------------------------
|
# -----------------------------------------
|
||||||
cmd = gbr_cmd.format(
|
cmd = gbr_cmd.format(
|
||||||
path=self.gerber_files,
|
path=self.gerber_files,
|
||||||
filename=self.copper_top_filename,
|
filename=self.copper_top_filename,
|
||||||
|
@ -116,9 +116,9 @@ class TclShellTest(unittest.TestCase):
|
||||||
"Expected FlatCAMGerber, instead, %s is %s" %
|
"Expected FlatCAMGerber, instead, %s is %s" %
|
||||||
(self.gerber_top_name, type(gerber_top_obj)))
|
(self.gerber_top_name, type(gerber_top_obj)))
|
||||||
|
|
||||||
#--------------------------------------------
|
# --------------------------------------------
|
||||||
# Open bottom layer and check for object type
|
# Open bottom layer and check for object type
|
||||||
#--------------------------------------------
|
# --------------------------------------------
|
||||||
cmd = gbr_cmd.format(
|
cmd = gbr_cmd.format(
|
||||||
path=self.gerber_files,
|
path=self.gerber_files,
|
||||||
filename=self.copper_bottom_filename,
|
filename=self.copper_bottom_filename,
|
||||||
|
@ -129,9 +129,9 @@ class TclShellTest(unittest.TestCase):
|
||||||
"Expected FlatCAMGerber, instead, %s is %s" %
|
"Expected FlatCAMGerber, instead, %s is %s" %
|
||||||
(self.gerber_bottom_name, type(gerber_bottom_obj)))
|
(self.gerber_bottom_name, type(gerber_bottom_obj)))
|
||||||
|
|
||||||
#--------------------------------------------
|
# --------------------------------------------
|
||||||
# Open cutout layer and check for object type
|
# Open cutout layer and check for object type
|
||||||
#--------------------------------------------
|
# --------------------------------------------
|
||||||
cmd = gbr_cmd.format(
|
cmd = gbr_cmd.format(
|
||||||
path=self.gerber_files,
|
path=self.gerber_files,
|
||||||
filename=self.cutout_filename,
|
filename=self.cutout_filename,
|
||||||
|
@ -160,9 +160,9 @@ class TclShellTest(unittest.TestCase):
|
||||||
|
|
||||||
# TODO: Check deleteb object is gone.
|
# TODO: Check deleteb object is gone.
|
||||||
|
|
||||||
#--------------------------------------------
|
# --------------------------------------------
|
||||||
# Exteriors of cutout layer, check type
|
# Exteriors of cutout layer, check type
|
||||||
#--------------------------------------------
|
# --------------------------------------------
|
||||||
obj = self.fc.collection.get_by_name(self.gerber_cutout_name + '_iso_exterior')
|
obj = self.fc.collection.get_by_name(self.gerber_cutout_name + '_iso_exterior')
|
||||||
self.assertTrue(isinstance(obj, FlatCAMGeometry),
|
self.assertTrue(isinstance(obj, FlatCAMGeometry),
|
||||||
"Expected FlatCAMGeometry, instead, %s is %s" %
|
"Expected FlatCAMGeometry, instead, %s is %s" %
|
||||||
|
@ -173,8 +173,14 @@ class TclShellTest(unittest.TestCase):
|
||||||
self.fc.exec_command_test('mirror %s -box %s -axis X' % (self.gerber_cutout_name, self.gerber_cutout_name))
|
self.fc.exec_command_test('mirror %s -box %s -axis X' % (self.gerber_cutout_name, self.gerber_cutout_name))
|
||||||
|
|
||||||
# exteriors delete and join geometries for bottom layer
|
# exteriors delete and join geometries for bottom layer
|
||||||
self.fc.exec_command_test('isolate %s -dia %f -outname %s' % (self.gerber_cutout_name, self.engraver_diameter, self.gerber_cutout_name + '_bottom_iso'))
|
self.fc.exec_command_test(
|
||||||
self.fc.exec_command_test('exteriors %s -outname %s' % (self.gerber_cutout_name + '_bottom_iso', self.gerber_cutout_name + '_bottom_iso_exterior'))
|
'isolate %s -dia %f -outname %s' %
|
||||||
|
(self.gerber_cutout_name, self.engraver_diameter, self.gerber_cutout_name + '_bottom_iso')
|
||||||
|
)
|
||||||
|
self.fc.exec_command_test(
|
||||||
|
'exteriors %s -outname %s' %
|
||||||
|
(self.gerber_cutout_name + '_bottom_iso', self.gerber_cutout_name + '_bottom_iso_exterior')
|
||||||
|
)
|
||||||
self.fc.exec_command_test('delete %s' % (self.gerber_cutout_name + '_bottom_iso'))
|
self.fc.exec_command_test('delete %s' % (self.gerber_cutout_name + '_bottom_iso'))
|
||||||
obj = self.fc.collection.get_by_name(self.gerber_cutout_name + '_bottom_iso_exterior')
|
obj = self.fc.collection.get_by_name(self.gerber_cutout_name + '_bottom_iso_exterior')
|
||||||
self.assertTrue(isinstance(obj, FlatCAMGeometry),
|
self.assertTrue(isinstance(obj, FlatCAMGeometry),
|
||||||
|
@ -187,12 +193,20 @@ class TclShellTest(unittest.TestCase):
|
||||||
"Expected 5 objects, found %d" % len(names))
|
"Expected 5 objects, found %d" % len(names))
|
||||||
|
|
||||||
# isolate traces
|
# isolate traces
|
||||||
self.fc.exec_command_test('isolate %s -dia %f' % (self.gerber_top_name, self.engraver_diameter))
|
self.fc.exec_command_test('isolate %s -dia %f' % (self.gerber_top_name, self.engraver_diameter))
|
||||||
self.fc.exec_command_test('isolate %s -dia %f' % (self.gerber_bottom_name, self.engraver_diameter))
|
self.fc.exec_command_test('isolate %s -dia %f' % (self.gerber_bottom_name, self.engraver_diameter))
|
||||||
|
|
||||||
# join isolated geometries for top and bottom
|
# join isolated geometries for top and bottom
|
||||||
self.fc.exec_command_test('join_geometries %s %s %s' % (self.gerber_top_name + '_join_iso', self.gerber_top_name + '_iso', self.gerber_cutout_name + '_iso_exterior'))
|
self.fc.exec_command_test(
|
||||||
self.fc.exec_command_test('join_geometries %s %s %s' % (self.gerber_bottom_name + '_join_iso', self.gerber_bottom_name + '_iso', self.gerber_cutout_name + '_bottom_iso_exterior'))
|
'join_geometries %s %s %s' %
|
||||||
|
(self.gerber_top_name + '_join_iso', self.gerber_top_name + '_iso',
|
||||||
|
self.gerber_cutout_name + '_iso_exterior')
|
||||||
|
)
|
||||||
|
self.fc.exec_command_test(
|
||||||
|
'join_geometries %s %s %s' %
|
||||||
|
(self.gerber_bottom_name + '_join_iso', self.gerber_bottom_name + '_iso',
|
||||||
|
self.gerber_cutout_name + '_bottom_iso_exterior')
|
||||||
|
)
|
||||||
|
|
||||||
# at this stage we should have 9 objects
|
# at this stage we should have 9 objects
|
||||||
names = self.fc.collection.get_names()
|
names = self.fc.collection.get_names()
|
||||||
|
@ -211,14 +225,21 @@ class TclShellTest(unittest.TestCase):
|
||||||
"Expected 5 objects, found %d" % len(names))
|
"Expected 5 objects, found %d" % len(names))
|
||||||
|
|
||||||
# geocutout bottom test (it cuts to same object)
|
# geocutout bottom test (it cuts to same object)
|
||||||
self.fc.exec_command_test('isolate %s -dia %f -outname %s' % (self.gerber_cutout_name, self.cutout_diameter, self.gerber_cutout_name + '_bottom_iso'))
|
self.fc.exec_command_test(
|
||||||
self.fc.exec_command_test('exteriors %s -outname %s' % (self.gerber_cutout_name + '_bottom_iso', self.gerber_cutout_name + '_bottom_iso_exterior'))
|
'isolate %s -dia %f -outname %s' %
|
||||||
|
(self.gerber_cutout_name, self.cutout_diameter, self.gerber_cutout_name + '_bottom_iso')
|
||||||
|
)
|
||||||
|
self.fc.exec_command_test(
|
||||||
|
'exteriors %s -outname %s' %
|
||||||
|
(self.gerber_cutout_name + '_bottom_iso', self.gerber_cutout_name + '_bottom_iso_exterior')
|
||||||
|
)
|
||||||
self.fc.exec_command_test('delete %s' % (self.gerber_cutout_name + '_bottom_iso'))
|
self.fc.exec_command_test('delete %s' % (self.gerber_cutout_name + '_bottom_iso'))
|
||||||
obj = self.fc.collection.get_by_name(self.gerber_cutout_name + '_bottom_iso_exterior')
|
obj = self.fc.collection.get_by_name(self.gerber_cutout_name + '_bottom_iso_exterior')
|
||||||
self.assertTrue(isinstance(obj, FlatCAMGeometry),
|
self.assertTrue(isinstance(obj, FlatCAMGeometry),
|
||||||
"Expected FlatCAMGeometry, instead, %s is %s" %
|
"Expected FlatCAMGeometry, instead, %s is %s" %
|
||||||
(self.gerber_cutout_name + '_bottom_iso_exterior', type(obj)))
|
(self.gerber_cutout_name + '_bottom_iso_exterior', type(obj)))
|
||||||
self.fc.exec_command_test('geocutout %s -dia %f -gapsize 0.3 -gaps 4' % (self.gerber_cutout_name + '_bottom_iso_exterior', self.cutout_diameter))
|
self.fc.exec_command_test('geocutout %s -dia %f -gapsize 0.3 -gaps 4' %
|
||||||
|
(self.gerber_cutout_name + '_bottom_iso_exterior', self.cutout_diameter))
|
||||||
|
|
||||||
# at this stage we should have 6 objects
|
# at this stage we should have 6 objects
|
||||||
names = self.fc.collection.get_names()
|
names = self.fc.collection.get_names()
|
||||||
|
@ -229,7 +250,8 @@ class TclShellTest(unittest.TestCase):
|
||||||
|
|
||||||
def test_open_gerber(self):
|
def test_open_gerber(self):
|
||||||
|
|
||||||
self.fc.exec_command_test('open_gerber %s/%s -outname %s' % (self.gerber_files, self.copper_top_filename, self.gerber_top_name))
|
self.fc.exec_command_test('open_gerber %s/%s -outname %s' %
|
||||||
|
(self.gerber_files, self.copper_top_filename, self.gerber_top_name))
|
||||||
gerber_top_obj = self.fc.collection.get_by_name(self.gerber_top_name)
|
gerber_top_obj = self.fc.collection.get_by_name(self.gerber_top_name)
|
||||||
self.assertTrue(isinstance(gerber_top_obj, FlatCAMGerber),
|
self.assertTrue(isinstance(gerber_top_obj, FlatCAMGerber),
|
||||||
"Expected FlatCAMGerber, instead, %s is %s" %
|
"Expected FlatCAMGerber, instead, %s is %s" %
|
||||||
|
@ -237,7 +259,8 @@ class TclShellTest(unittest.TestCase):
|
||||||
|
|
||||||
def test_excellon_flow(self):
|
def test_excellon_flow(self):
|
||||||
|
|
||||||
self.fc.exec_command_test('open_excellon %s/%s -outname %s' % (self.gerber_files, self.excellon_filename, self.excellon_name))
|
self.fc.exec_command_test('open_excellon %s/%s -outname %s' %
|
||||||
|
(self.gerber_files, self.excellon_filename, self.excellon_name))
|
||||||
excellon_obj = self.fc.collection.get_by_name(self.excellon_name)
|
excellon_obj = self.fc.collection.get_by_name(self.excellon_name)
|
||||||
self.assertTrue(isinstance(excellon_obj, FlatCAMExcellon),
|
self.assertTrue(isinstance(excellon_obj, FlatCAMExcellon),
|
||||||
"Expected FlatCAMExcellon, instead, %s is %s" %
|
"Expected FlatCAMExcellon, instead, %s is %s" %
|
||||||
|
|
Loading…
Reference in New Issue