- fixed Excellon parser to detect correctly the units and zeros for Excellon's generated by Eagle 9.3.0

- modified the initial size of the canvas on startup
- modified the build file (make_win.py) to solve the issue with suddenly not accepting the version as Beta
This commit is contained in:
Marius Stanciu 2019-02-16 14:43:26 +02:00 committed by Marius S
parent b6d36bb86d
commit 71d8b2df36
4 changed files with 217 additions and 215 deletions

View File

@ -20,6 +20,9 @@ CAD program, and create G-Code for Isolation routing.
- fixed DblSided Tool crash when trying to create Alignment Drills object without a Tool diameter specified - fixed DblSided Tool crash when trying to create Alignment Drills object without a Tool diameter specified
- fixed DblSided Tool issue when entering Tool diameter values with comma decimal separator instead of decimal dot separator - fixed DblSided Tool issue when entering Tool diameter values with comma decimal separator instead of decimal dot separator
- fixed Cutout Tool Freeform to generate cutouts with options: LR, TB. 2LR, 2TB which didn't worked previously - fixed Cutout Tool Freeform to generate cutouts with options: LR, TB. 2LR, 2TB which didn't worked previously
- fixed Excellon parser to detect correctly the units and zeros for Excellon's generated by Eagle 9.3.0
- modified the initial size of the canvas on startup
- modified the build file (make_win.py) to solve the issue with suddenly not accepting the version as Beta
15.02.2019 15.02.2019

View File

@ -40,7 +40,7 @@ class VisPyCanvas(scene.SceneCanvas):
right_padding.width_max = 0 right_padding.width_max = 0
view = self.grid_widget.add_view(row=1, col=1, border_color='black', bgcolor='white') view = self.grid_widget.add_view(row=1, col=1, border_color='black', bgcolor='white')
view.camera = Camera(aspect=1, rect=(-100,-100,500,500)) view.camera = Camera(aspect=1, rect=(-25,-25,150,150))
# Following function was removed from 'prepare_draw()' of 'Grid' class by patch, # Following function was removed from 'prepare_draw()' of 'Grid' class by patch,
# it is necessary to call manually # it is necessary to call manually

417
camlib.py
View File

@ -43,7 +43,7 @@ from rasterio.features import shapes
from xml.dom.minidom import parseString as parse_xml_string from xml.dom.minidom import parseString as parse_xml_string
from scipy.spatial import KDTree, Delaunay # from scipy.spatial import KDTree, Delaunay
from ParseSVG import * from ParseSVG import *
from ParseDXF import * from ParseDXF import *
@ -3348,7 +3348,7 @@ class Excellon(Geometry):
# Number format and units # Number format and units
# INCH uses 6 digits # INCH uses 6 digits
# METRIC uses 5/6 # METRIC uses 5/6
self.units_re = re.compile(r'^(INCH|METRIC)(?:,([TL])Z)?$') self.units_re = re.compile(r'^(INCH|METRIC)(?:,([TL])Z)?.*$')
# Tool definition/parameters (?= is look-ahead # Tool definition/parameters (?= is look-ahead
# NOTE: This might be an overkill! # NOTE: This might be an overkill!
@ -3968,7 +3968,6 @@ class Excellon(Geometry):
#FlatCAMApp.App.inform.emit("Detected INLINE: %s" % str(eline)) #FlatCAMApp.App.inform.emit("Detected INLINE: %s" % str(eline))
continue continue
# Search for zeros type again because it might be alone on the line # Search for zeros type again because it might be alone on the line
match = re.search(r'[LT]Z',eline) match = re.search(r'[LT]Z',eline)
if match: if match:
@ -6551,212 +6550,212 @@ def parse_gerber_number(strnumber, int_digits, frac_digits, zeros):
return ret_val return ret_val
def voronoi(P): # def voronoi(P):
""" # """
Returns a list of all edges of the voronoi diagram for the given input points. # Returns a list of all edges of the voronoi diagram for the given input points.
""" # """
delauny = Delaunay(P) # delauny = Delaunay(P)
triangles = delauny.points[delauny.vertices] # triangles = delauny.points[delauny.vertices]
#
circum_centers = np.array([triangle_csc(tri) for tri in triangles]) # circum_centers = np.array([triangle_csc(tri) for tri in triangles])
long_lines_endpoints = [] # long_lines_endpoints = []
#
lineIndices = [] # lineIndices = []
for i, triangle in enumerate(triangles): # for i, triangle in enumerate(triangles):
circum_center = circum_centers[i] # circum_center = circum_centers[i]
for j, neighbor in enumerate(delauny.neighbors[i]): # for j, neighbor in enumerate(delauny.neighbors[i]):
if neighbor != -1: # if neighbor != -1:
lineIndices.append((i, neighbor)) # lineIndices.append((i, neighbor))
else: # else:
ps = triangle[(j+1)%3] - triangle[(j-1)%3] # ps = triangle[(j+1)%3] - triangle[(j-1)%3]
ps = np.array((ps[1], -ps[0])) # ps = np.array((ps[1], -ps[0]))
#
middle = (triangle[(j+1)%3] + triangle[(j-1)%3]) * 0.5 # middle = (triangle[(j+1)%3] + triangle[(j-1)%3]) * 0.5
di = middle - triangle[j] # di = middle - triangle[j]
#
ps /= np.linalg.norm(ps) # ps /= np.linalg.norm(ps)
di /= np.linalg.norm(di) # di /= np.linalg.norm(di)
#
if np.dot(di, ps) < 0.0: # if np.dot(di, ps) < 0.0:
ps *= -1000.0 # ps *= -1000.0
else: # else:
ps *= 1000.0 # ps *= 1000.0
#
long_lines_endpoints.append(circum_center + ps) # long_lines_endpoints.append(circum_center + ps)
lineIndices.append((i, len(circum_centers) + len(long_lines_endpoints)-1)) # lineIndices.append((i, len(circum_centers) + len(long_lines_endpoints)-1))
#
vertices = np.vstack((circum_centers, long_lines_endpoints)) # vertices = np.vstack((circum_centers, long_lines_endpoints))
#
# filter out any duplicate lines # # filter out any duplicate lines
lineIndicesSorted = np.sort(lineIndices) # make (1,2) and (2,1) both (1,2) # lineIndicesSorted = np.sort(lineIndices) # make (1,2) and (2,1) both (1,2)
lineIndicesTupled = [tuple(row) for row in lineIndicesSorted] # lineIndicesTupled = [tuple(row) for row in lineIndicesSorted]
lineIndicesUnique = np.unique(lineIndicesTupled) # lineIndicesUnique = np.unique(lineIndicesTupled)
#
return vertices, lineIndicesUnique # return vertices, lineIndicesUnique
#
#
def triangle_csc(pts): # def triangle_csc(pts):
rows, cols = pts.shape # rows, cols = pts.shape
#
A = np.bmat([[2 * np.dot(pts, pts.T), np.ones((rows, 1))], # A = np.bmat([[2 * np.dot(pts, pts.T), np.ones((rows, 1))],
[np.ones((1, rows)), np.zeros((1, 1))]]) # [np.ones((1, rows)), np.zeros((1, 1))]])
#
b = np.hstack((np.sum(pts * pts, axis=1), np.ones((1)))) # b = np.hstack((np.sum(pts * pts, axis=1), np.ones((1))))
x = np.linalg.solve(A,b) # x = np.linalg.solve(A,b)
bary_coords = x[:-1] # bary_coords = x[:-1]
return np.sum(pts * np.tile(bary_coords.reshape((pts.shape[0], 1)), (1, pts.shape[1])), axis=0) # return np.sum(pts * np.tile(bary_coords.reshape((pts.shape[0], 1)), (1, pts.shape[1])), axis=0)
#
#
def voronoi_cell_lines(points, vertices, lineIndices): # def voronoi_cell_lines(points, vertices, lineIndices):
""" # """
Returns a mapping from a voronoi cell to its edges. # Returns a mapping from a voronoi cell to its edges.
#
:param points: shape (m,2) # :param points: shape (m,2)
:param vertices: shape (n,2) # :param vertices: shape (n,2)
:param lineIndices: shape (o,2) # :param lineIndices: shape (o,2)
:rtype: dict point index -> list of shape (n,2) with vertex indices # :rtype: dict point index -> list of shape (n,2) with vertex indices
""" # """
kd = KDTree(points) # kd = KDTree(points)
#
cells = collections.defaultdict(list) # cells = collections.defaultdict(list)
for i1, i2 in lineIndices: # for i1, i2 in lineIndices:
v1, v2 = vertices[i1], vertices[i2] # v1, v2 = vertices[i1], vertices[i2]
mid = (v1+v2)/2 # mid = (v1+v2)/2
_, (p1Idx, p2Idx) = kd.query(mid, 2) # _, (p1Idx, p2Idx) = kd.query(mid, 2)
cells[p1Idx].append((i1, i2)) # cells[p1Idx].append((i1, i2))
cells[p2Idx].append((i1, i2)) # cells[p2Idx].append((i1, i2))
#
return cells # return cells
#
#
def voronoi_edges2polygons(cells): # def voronoi_edges2polygons(cells):
""" # """
Transforms cell edges into polygons. # Transforms cell edges into polygons.
#
:param cells: as returned from voronoi_cell_lines # :param cells: as returned from voronoi_cell_lines
:rtype: dict point index -> list of vertex indices which form a polygon # :rtype: dict point index -> list of vertex indices which form a polygon
""" # """
#
# first, close the outer cells # # first, close the outer cells
for pIdx, lineIndices_ in cells.items(): # for pIdx, lineIndices_ in cells.items():
dangling_lines = [] # dangling_lines = []
for i1, i2 in lineIndices_: # for i1, i2 in lineIndices_:
p = (i1, i2) # p = (i1, i2)
connections = filter(lambda k: p != k and (p[0] == k[0] or p[0] == k[1] or p[1] == k[0] or p[1] == k[1]), lineIndices_) # connections = filter(lambda k: p != k and (p[0] == k[0] or p[0] == k[1] or p[1] == k[0] or p[1] == k[1]), lineIndices_)
# connections = filter(lambda (i1_, i2_): (i1, i2) != (i1_, i2_) and (i1 == i1_ or i1 == i2_ or i2 == i1_ or i2 == i2_), lineIndices_) # # connections = filter(lambda (i1_, i2_): (i1, i2) != (i1_, i2_) and (i1 == i1_ or i1 == i2_ or i2 == i1_ or i2 == i2_), lineIndices_)
assert 1 <= len(connections) <= 2 # assert 1 <= len(connections) <= 2
if len(connections) == 1: # if len(connections) == 1:
dangling_lines.append((i1, i2)) # dangling_lines.append((i1, i2))
assert len(dangling_lines) in [0, 2] # assert len(dangling_lines) in [0, 2]
if len(dangling_lines) == 2: # if len(dangling_lines) == 2:
(i11, i12), (i21, i22) = dangling_lines # (i11, i12), (i21, i22) = dangling_lines
s = (i11, i12) # s = (i11, i12)
t = (i21, i22) # t = (i21, i22)
#
# determine which line ends are unconnected # # determine which line ends are unconnected
connected = filter(lambda k: k != s and (k[0] == s[0] or k[1] == s[0]), lineIndices_) # connected = filter(lambda k: k != s and (k[0] == s[0] or k[1] == s[0]), lineIndices_)
# connected = filter(lambda (i1,i2): (i1,i2) != (i11,i12) and (i1 == i11 or i2 == i11), lineIndices_) # # connected = filter(lambda (i1,i2): (i1,i2) != (i11,i12) and (i1 == i11 or i2 == i11), lineIndices_)
i11Unconnected = len(connected) == 0 # i11Unconnected = len(connected) == 0
#
connected = filter(lambda k: k != t and (k[0] == t[0] or k[1] == t[0]), lineIndices_) # connected = filter(lambda k: k != t and (k[0] == t[0] or k[1] == t[0]), lineIndices_)
# connected = filter(lambda (i1,i2): (i1,i2) != (i21,i22) and (i1 == i21 or i2 == i21), lineIndices_) # # connected = filter(lambda (i1,i2): (i1,i2) != (i21,i22) and (i1 == i21 or i2 == i21), lineIndices_)
i21Unconnected = len(connected) == 0 # i21Unconnected = len(connected) == 0
#
startIdx = i11 if i11Unconnected else i12 # startIdx = i11 if i11Unconnected else i12
endIdx = i21 if i21Unconnected else i22 # endIdx = i21 if i21Unconnected else i22
#
cells[pIdx].append((startIdx, endIdx)) # cells[pIdx].append((startIdx, endIdx))
#
# then, form polygons by storing vertex indices in (counter-)clockwise order # # then, form polygons by storing vertex indices in (counter-)clockwise order
polys = dict() # polys = dict()
for pIdx, lineIndices_ in cells.items(): # for pIdx, lineIndices_ in cells.items():
# get a directed graph which contains both directions and arbitrarily follow one of both # # get a directed graph which contains both directions and arbitrarily follow one of both
directedGraph = lineIndices_ + [(i2, i1) for (i1, i2) in lineIndices_] # directedGraph = lineIndices_ + [(i2, i1) for (i1, i2) in lineIndices_]
directedGraphMap = collections.defaultdict(list) # directedGraphMap = collections.defaultdict(list)
for (i1, i2) in directedGraph: # for (i1, i2) in directedGraph:
directedGraphMap[i1].append(i2) # directedGraphMap[i1].append(i2)
orderedEdges = [] # orderedEdges = []
currentEdge = directedGraph[0] # currentEdge = directedGraph[0]
while len(orderedEdges) < len(lineIndices_): # while len(orderedEdges) < len(lineIndices_):
i1 = currentEdge[1] # i1 = currentEdge[1]
i2 = directedGraphMap[i1][0] if directedGraphMap[i1][0] != currentEdge[0] else directedGraphMap[i1][1] # i2 = directedGraphMap[i1][0] if directedGraphMap[i1][0] != currentEdge[0] else directedGraphMap[i1][1]
nextEdge = (i1, i2) # nextEdge = (i1, i2)
orderedEdges.append(nextEdge) # orderedEdges.append(nextEdge)
currentEdge = nextEdge # currentEdge = nextEdge
#
polys[pIdx] = [i1 for (i1, i2) in orderedEdges] # polys[pIdx] = [i1 for (i1, i2) in orderedEdges]
#
return polys # return polys
#
#
def voronoi_polygons(points): # def voronoi_polygons(points):
""" # """
Returns the voronoi polygon for each input point. # Returns the voronoi polygon for each input point.
#
:param points: shape (n,2) # :param points: shape (n,2)
:rtype: list of n polygons where each polygon is an array of vertices # :rtype: list of n polygons where each polygon is an array of vertices
""" # """
vertices, lineIndices = voronoi(points) # vertices, lineIndices = voronoi(points)
cells = voronoi_cell_lines(points, vertices, lineIndices) # cells = voronoi_cell_lines(points, vertices, lineIndices)
polys = voronoi_edges2polygons(cells) # polys = voronoi_edges2polygons(cells)
polylist = [] # polylist = []
for i in range(len(points)): # for i in range(len(points)):
poly = vertices[np.asarray(polys[i])] # poly = vertices[np.asarray(polys[i])]
polylist.append(poly) # polylist.append(poly)
return polylist # return polylist
#
#
class Zprofile: # class Zprofile:
def __init__(self): # def __init__(self):
#
# data contains lists of [x, y, z] # # data contains lists of [x, y, z]
self.data = [] # self.data = []
#
# Computed voronoi polygons (shapely) # # Computed voronoi polygons (shapely)
self.polygons = [] # self.polygons = []
pass # pass
#
# def plot_polygons(self): # # def plot_polygons(self):
# axes = plt.subplot(1, 1, 1) # # axes = plt.subplot(1, 1, 1)
# # #
# plt.axis([-0.05, 1.05, -0.05, 1.05]) # # plt.axis([-0.05, 1.05, -0.05, 1.05])
# # #
# for poly in self.polygons: # # for poly in self.polygons:
# p = PolygonPatch(poly, facecolor=np.random.rand(3, 1), alpha=0.3) # # p = PolygonPatch(poly, facecolor=np.random.rand(3, 1), alpha=0.3)
# axes.add_patch(p) # # axes.add_patch(p)
#
def init_from_csv(self, filename): # def init_from_csv(self, filename):
pass # pass
#
def init_from_string(self, zpstring): # def init_from_string(self, zpstring):
pass # pass
#
def init_from_list(self, zplist): # def init_from_list(self, zplist):
self.data = zplist # self.data = zplist
#
def generate_polygons(self): # def generate_polygons(self):
self.polygons = [Polygon(p) for p in voronoi_polygons(array([[x[0], x[1]] for x in self.data]))] # self.polygons = [Polygon(p) for p in voronoi_polygons(array([[x[0], x[1]] for x in self.data]))]
#
def normalize(self, origin): # def normalize(self, origin):
pass # pass
#
def paste(self, path): # def paste(self, path):
""" # """
Return a list of dictionaries containing the parts of the original # Return a list of dictionaries containing the parts of the original
path and their z-axis offset. # path and their z-axis offset.
""" # """
#
# At most one region/polygon will contain the path # # At most one region/polygon will contain the path
containing = [i for i in range(len(self.polygons)) if self.polygons[i].contains(path)] # containing = [i for i in range(len(self.polygons)) if self.polygons[i].contains(path)]
#
if len(containing) > 0: # if len(containing) > 0:
return [{"path": path, "z": self.data[containing[0]][2]}] # return [{"path": path, "z": self.data[containing[0]][2]}]
#
# All region indexes that intersect with the path # # All region indexes that intersect with the path
crossing = [i for i in range(len(self.polygons)) if self.polygons[i].intersects(path)] # crossing = [i for i in range(len(self.polygons)) if self.polygons[i].intersects(path)]
#
return [{"path": path.intersection(self.polygons[i]), # return [{"path": path.intersection(self.polygons[i]),
"z": self.data[i][2]} for i in crossing] # "z": self.data[i][2]} for i in crossing]
def autolist(obj): def autolist(obj):

View File

@ -63,8 +63,8 @@ if platform.architecture()[0] == '64bit':
include_files=include_files, include_files=include_files,
excludes=['scipy','pytz'], excludes=['scipy','pytz'],
# packages=['OpenGL','numpy','vispy','ortools','google'] # packages=['OpenGL','numpy','vispy','ortools','google']
packages=['numpy','google', 'rasterio'] # works for Python 3.7 # packages=['numpy','google', 'rasterio'] # works for Python 3.7
# packages = ['opengl', 'numpy', 'google', 'rasterio'] # works for Python 3.6.5 packages = ['opengl', 'numpy', 'google', 'rasterio'] # works for Python 3.6.5 and Python 3.7.1
) )
else: else:
@ -72,8 +72,8 @@ else:
include_files=include_files, include_files=include_files,
excludes=['scipy', 'pytz'], excludes=['scipy', 'pytz'],
# packages=['OpenGL','numpy','vispy','ortools','google'] # packages=['OpenGL','numpy','vispy','ortools','google']
packages=['numpy', 'rasterio'] # works for Python 3.7 # packages=['numpy', 'rasterio'] # works for Python 3.7
# packages = ['opengl', 'numpy', 'google', 'rasterio'] # works for Python 3.6.5 packages = ['opengl', 'numpy', 'google', 'rasterio'] # works for Python 3.6.5 and Python 3.7.1
) )
@ -84,7 +84,7 @@ print("INCLUDE_FILES", include_files)
setup( setup(
name="FlatCAM", name="FlatCAM",
author="Juan Pablo Caram", author="Juan Pablo Caram",
version="Beta", version="8.9",
description="FlatCAM: 2D Computer Aided PCB Manufacturing", description="FlatCAM: 2D Computer Aided PCB Manufacturing",
options=dict(build_exe=buildOptions), options=dict(build_exe=buildOptions),
executables=[Executable("FlatCAM.py", icon='share/flatcam_icon48.ico', base=base)] executables=[Executable("FlatCAM.py", icon='share/flatcam_icon48.ico', base=base)]