From 71d8b2df36b4081f73ebfd8ba6a87dfb4b2b4a7b Mon Sep 17 00:00:00 2001 From: Marius Stanciu Date: Sat, 16 Feb 2019 14:43:26 +0200 Subject: [PATCH] - 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 --- README.md | 3 + VisPyCanvas.py | 2 +- camlib.py | 417 ++++++++++++++++++++++++------------------------- make_win.py | 10 +- 4 files changed, 217 insertions(+), 215 deletions(-) diff --git a/README.md b/README.md index 888d20c0..42831abe 100644 --- a/README.md +++ b/README.md @@ -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 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 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 diff --git a/VisPyCanvas.py b/VisPyCanvas.py index 37394c08..a2d5b2c7 100644 --- a/VisPyCanvas.py +++ b/VisPyCanvas.py @@ -40,7 +40,7 @@ class VisPyCanvas(scene.SceneCanvas): right_padding.width_max = 0 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, # it is necessary to call manually diff --git a/camlib.py b/camlib.py index d161da65..bbe3de02 100644 --- a/camlib.py +++ b/camlib.py @@ -43,7 +43,7 @@ from rasterio.features import shapes 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 ParseDXF import * @@ -3348,7 +3348,7 @@ class Excellon(Geometry): # Number format and units # INCH uses 6 digits # 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 # NOTE: This might be an overkill! @@ -3968,7 +3968,6 @@ class Excellon(Geometry): #FlatCAMApp.App.inform.emit("Detected INLINE: %s" % str(eline)) continue - # Search for zeros type again because it might be alone on the line match = re.search(r'[LT]Z',eline) if match: @@ -6551,212 +6550,212 @@ def parse_gerber_number(strnumber, int_digits, frac_digits, zeros): return ret_val -def voronoi(P): - """ - Returns a list of all edges of the voronoi diagram for the given input points. - """ - delauny = Delaunay(P) - triangles = delauny.points[delauny.vertices] - - circum_centers = np.array([triangle_csc(tri) for tri in triangles]) - long_lines_endpoints = [] - - lineIndices = [] - for i, triangle in enumerate(triangles): - circum_center = circum_centers[i] - for j, neighbor in enumerate(delauny.neighbors[i]): - if neighbor != -1: - lineIndices.append((i, neighbor)) - else: - ps = triangle[(j+1)%3] - triangle[(j-1)%3] - ps = np.array((ps[1], -ps[0])) - - middle = (triangle[(j+1)%3] + triangle[(j-1)%3]) * 0.5 - di = middle - triangle[j] - - ps /= np.linalg.norm(ps) - di /= np.linalg.norm(di) - - if np.dot(di, ps) < 0.0: - ps *= -1000.0 - else: - ps *= 1000.0 - - long_lines_endpoints.append(circum_center + ps) - lineIndices.append((i, len(circum_centers) + len(long_lines_endpoints)-1)) - - vertices = np.vstack((circum_centers, long_lines_endpoints)) - - # filter out any duplicate lines - lineIndicesSorted = np.sort(lineIndices) # make (1,2) and (2,1) both (1,2) - lineIndicesTupled = [tuple(row) for row in lineIndicesSorted] - lineIndicesUnique = np.unique(lineIndicesTupled) - - return vertices, lineIndicesUnique - - -def triangle_csc(pts): - rows, cols = pts.shape - - A = np.bmat([[2 * np.dot(pts, pts.T), np.ones((rows, 1))], - [np.ones((1, rows)), np.zeros((1, 1))]]) - - b = np.hstack((np.sum(pts * pts, axis=1), np.ones((1)))) - x = np.linalg.solve(A,b) - bary_coords = x[:-1] - 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): - """ - Returns a mapping from a voronoi cell to its edges. - - :param points: shape (m,2) - :param vertices: shape (n,2) - :param lineIndices: shape (o,2) - :rtype: dict point index -> list of shape (n,2) with vertex indices - """ - kd = KDTree(points) - - cells = collections.defaultdict(list) - for i1, i2 in lineIndices: - v1, v2 = vertices[i1], vertices[i2] - mid = (v1+v2)/2 - _, (p1Idx, p2Idx) = kd.query(mid, 2) - cells[p1Idx].append((i1, i2)) - cells[p2Idx].append((i1, i2)) - - return cells - - -def voronoi_edges2polygons(cells): - """ - Transforms cell edges into polygons. - - :param cells: as returned from voronoi_cell_lines - :rtype: dict point index -> list of vertex indices which form a polygon - """ - - # first, close the outer cells - for pIdx, lineIndices_ in cells.items(): - dangling_lines = [] - for i1, i2 in lineIndices_: - 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 (i1_, i2_): (i1, i2) != (i1_, i2_) and (i1 == i1_ or i1 == i2_ or i2 == i1_ or i2 == i2_), lineIndices_) - assert 1 <= len(connections) <= 2 - if len(connections) == 1: - dangling_lines.append((i1, i2)) - assert len(dangling_lines) in [0, 2] - if len(dangling_lines) == 2: - (i11, i12), (i21, i22) = dangling_lines - s = (i11, i12) - t = (i21, i22) - - # 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 (i1,i2): (i1,i2) != (i11,i12) and (i1 == i11 or i2 == i11), lineIndices_) - i11Unconnected = len(connected) == 0 - - 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_) - i21Unconnected = len(connected) == 0 - - startIdx = i11 if i11Unconnected else i12 - endIdx = i21 if i21Unconnected else i22 - - cells[pIdx].append((startIdx, endIdx)) - - # then, form polygons by storing vertex indices in (counter-)clockwise order - polys = dict() - for pIdx, lineIndices_ in cells.items(): - # get a directed graph which contains both directions and arbitrarily follow one of both - directedGraph = lineIndices_ + [(i2, i1) for (i1, i2) in lineIndices_] - directedGraphMap = collections.defaultdict(list) - for (i1, i2) in directedGraph: - directedGraphMap[i1].append(i2) - orderedEdges = [] - currentEdge = directedGraph[0] - while len(orderedEdges) < len(lineIndices_): - i1 = currentEdge[1] - i2 = directedGraphMap[i1][0] if directedGraphMap[i1][0] != currentEdge[0] else directedGraphMap[i1][1] - nextEdge = (i1, i2) - orderedEdges.append(nextEdge) - currentEdge = nextEdge - - polys[pIdx] = [i1 for (i1, i2) in orderedEdges] - - return polys - - -def voronoi_polygons(points): - """ - Returns the voronoi polygon for each input point. - - :param points: shape (n,2) - :rtype: list of n polygons where each polygon is an array of vertices - """ - vertices, lineIndices = voronoi(points) - cells = voronoi_cell_lines(points, vertices, lineIndices) - polys = voronoi_edges2polygons(cells) - polylist = [] - for i in range(len(points)): - poly = vertices[np.asarray(polys[i])] - polylist.append(poly) - return polylist - - -class Zprofile: - def __init__(self): - - # data contains lists of [x, y, z] - self.data = [] - - # Computed voronoi polygons (shapely) - self.polygons = [] - pass - - # def plot_polygons(self): - # axes = plt.subplot(1, 1, 1) - # - # plt.axis([-0.05, 1.05, -0.05, 1.05]) - # - # for poly in self.polygons: - # p = PolygonPatch(poly, facecolor=np.random.rand(3, 1), alpha=0.3) - # axes.add_patch(p) - - def init_from_csv(self, filename): - pass - - def init_from_string(self, zpstring): - pass - - def init_from_list(self, zplist): - self.data = zplist - - def generate_polygons(self): - self.polygons = [Polygon(p) for p in voronoi_polygons(array([[x[0], x[1]] for x in self.data]))] - - def normalize(self, origin): - pass - - def paste(self, path): - """ - Return a list of dictionaries containing the parts of the original - path and their z-axis offset. - """ - - # At most one region/polygon will contain the path - containing = [i for i in range(len(self.polygons)) if self.polygons[i].contains(path)] - - if len(containing) > 0: - return [{"path": path, "z": self.data[containing[0]][2]}] - - # All region indexes that intersect with the path - crossing = [i for i in range(len(self.polygons)) if self.polygons[i].intersects(path)] - - return [{"path": path.intersection(self.polygons[i]), - "z": self.data[i][2]} for i in crossing] +# def voronoi(P): +# """ +# Returns a list of all edges of the voronoi diagram for the given input points. +# """ +# delauny = Delaunay(P) +# triangles = delauny.points[delauny.vertices] +# +# circum_centers = np.array([triangle_csc(tri) for tri in triangles]) +# long_lines_endpoints = [] +# +# lineIndices = [] +# for i, triangle in enumerate(triangles): +# circum_center = circum_centers[i] +# for j, neighbor in enumerate(delauny.neighbors[i]): +# if neighbor != -1: +# lineIndices.append((i, neighbor)) +# else: +# ps = triangle[(j+1)%3] - triangle[(j-1)%3] +# ps = np.array((ps[1], -ps[0])) +# +# middle = (triangle[(j+1)%3] + triangle[(j-1)%3]) * 0.5 +# di = middle - triangle[j] +# +# ps /= np.linalg.norm(ps) +# di /= np.linalg.norm(di) +# +# if np.dot(di, ps) < 0.0: +# ps *= -1000.0 +# else: +# ps *= 1000.0 +# +# long_lines_endpoints.append(circum_center + ps) +# lineIndices.append((i, len(circum_centers) + len(long_lines_endpoints)-1)) +# +# vertices = np.vstack((circum_centers, long_lines_endpoints)) +# +# # filter out any duplicate lines +# lineIndicesSorted = np.sort(lineIndices) # make (1,2) and (2,1) both (1,2) +# lineIndicesTupled = [tuple(row) for row in lineIndicesSorted] +# lineIndicesUnique = np.unique(lineIndicesTupled) +# +# return vertices, lineIndicesUnique +# +# +# def triangle_csc(pts): +# rows, cols = pts.shape +# +# A = np.bmat([[2 * np.dot(pts, pts.T), np.ones((rows, 1))], +# [np.ones((1, rows)), np.zeros((1, 1))]]) +# +# b = np.hstack((np.sum(pts * pts, axis=1), np.ones((1)))) +# x = np.linalg.solve(A,b) +# bary_coords = x[:-1] +# 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): +# """ +# Returns a mapping from a voronoi cell to its edges. +# +# :param points: shape (m,2) +# :param vertices: shape (n,2) +# :param lineIndices: shape (o,2) +# :rtype: dict point index -> list of shape (n,2) with vertex indices +# """ +# kd = KDTree(points) +# +# cells = collections.defaultdict(list) +# for i1, i2 in lineIndices: +# v1, v2 = vertices[i1], vertices[i2] +# mid = (v1+v2)/2 +# _, (p1Idx, p2Idx) = kd.query(mid, 2) +# cells[p1Idx].append((i1, i2)) +# cells[p2Idx].append((i1, i2)) +# +# return cells +# +# +# def voronoi_edges2polygons(cells): +# """ +# Transforms cell edges into polygons. +# +# :param cells: as returned from voronoi_cell_lines +# :rtype: dict point index -> list of vertex indices which form a polygon +# """ +# +# # first, close the outer cells +# for pIdx, lineIndices_ in cells.items(): +# dangling_lines = [] +# for i1, i2 in lineIndices_: +# 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 (i1_, i2_): (i1, i2) != (i1_, i2_) and (i1 == i1_ or i1 == i2_ or i2 == i1_ or i2 == i2_), lineIndices_) +# assert 1 <= len(connections) <= 2 +# if len(connections) == 1: +# dangling_lines.append((i1, i2)) +# assert len(dangling_lines) in [0, 2] +# if len(dangling_lines) == 2: +# (i11, i12), (i21, i22) = dangling_lines +# s = (i11, i12) +# t = (i21, i22) +# +# # 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 (i1,i2): (i1,i2) != (i11,i12) and (i1 == i11 or i2 == i11), lineIndices_) +# i11Unconnected = len(connected) == 0 +# +# 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_) +# i21Unconnected = len(connected) == 0 +# +# startIdx = i11 if i11Unconnected else i12 +# endIdx = i21 if i21Unconnected else i22 +# +# cells[pIdx].append((startIdx, endIdx)) +# +# # then, form polygons by storing vertex indices in (counter-)clockwise order +# polys = dict() +# for pIdx, lineIndices_ in cells.items(): +# # get a directed graph which contains both directions and arbitrarily follow one of both +# directedGraph = lineIndices_ + [(i2, i1) for (i1, i2) in lineIndices_] +# directedGraphMap = collections.defaultdict(list) +# for (i1, i2) in directedGraph: +# directedGraphMap[i1].append(i2) +# orderedEdges = [] +# currentEdge = directedGraph[0] +# while len(orderedEdges) < len(lineIndices_): +# i1 = currentEdge[1] +# i2 = directedGraphMap[i1][0] if directedGraphMap[i1][0] != currentEdge[0] else directedGraphMap[i1][1] +# nextEdge = (i1, i2) +# orderedEdges.append(nextEdge) +# currentEdge = nextEdge +# +# polys[pIdx] = [i1 for (i1, i2) in orderedEdges] +# +# return polys +# +# +# def voronoi_polygons(points): +# """ +# Returns the voronoi polygon for each input point. +# +# :param points: shape (n,2) +# :rtype: list of n polygons where each polygon is an array of vertices +# """ +# vertices, lineIndices = voronoi(points) +# cells = voronoi_cell_lines(points, vertices, lineIndices) +# polys = voronoi_edges2polygons(cells) +# polylist = [] +# for i in range(len(points)): +# poly = vertices[np.asarray(polys[i])] +# polylist.append(poly) +# return polylist +# +# +# class Zprofile: +# def __init__(self): +# +# # data contains lists of [x, y, z] +# self.data = [] +# +# # Computed voronoi polygons (shapely) +# self.polygons = [] +# pass +# +# # def plot_polygons(self): +# # axes = plt.subplot(1, 1, 1) +# # +# # plt.axis([-0.05, 1.05, -0.05, 1.05]) +# # +# # for poly in self.polygons: +# # p = PolygonPatch(poly, facecolor=np.random.rand(3, 1), alpha=0.3) +# # axes.add_patch(p) +# +# def init_from_csv(self, filename): +# pass +# +# def init_from_string(self, zpstring): +# pass +# +# def init_from_list(self, zplist): +# self.data = zplist +# +# def generate_polygons(self): +# self.polygons = [Polygon(p) for p in voronoi_polygons(array([[x[0], x[1]] for x in self.data]))] +# +# def normalize(self, origin): +# pass +# +# def paste(self, path): +# """ +# Return a list of dictionaries containing the parts of the original +# path and their z-axis offset. +# """ +# +# # At most one region/polygon will contain the path +# containing = [i for i in range(len(self.polygons)) if self.polygons[i].contains(path)] +# +# if len(containing) > 0: +# return [{"path": path, "z": self.data[containing[0]][2]}] +# +# # All region indexes that intersect with the path +# crossing = [i for i in range(len(self.polygons)) if self.polygons[i].intersects(path)] +# +# return [{"path": path.intersection(self.polygons[i]), +# "z": self.data[i][2]} for i in crossing] def autolist(obj): diff --git a/make_win.py b/make_win.py index 486270e4..fac62161 100644 --- a/make_win.py +++ b/make_win.py @@ -63,8 +63,8 @@ if platform.architecture()[0] == '64bit': include_files=include_files, excludes=['scipy','pytz'], # packages=['OpenGL','numpy','vispy','ortools','google'] - packages=['numpy','google', 'rasterio'] # works for Python 3.7 - # packages = ['opengl', 'numpy', 'google', 'rasterio'] # works for Python 3.6.5 + # packages=['numpy','google', 'rasterio'] # works for Python 3.7 + packages = ['opengl', 'numpy', 'google', 'rasterio'] # works for Python 3.6.5 and Python 3.7.1 ) else: @@ -72,8 +72,8 @@ else: include_files=include_files, excludes=['scipy', 'pytz'], # packages=['OpenGL','numpy','vispy','ortools','google'] - packages=['numpy', 'rasterio'] # works for Python 3.7 - # packages = ['opengl', 'numpy', 'google', 'rasterio'] # works for Python 3.6.5 + # packages=['numpy', 'rasterio'] # works for Python 3.7 + 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( name="FlatCAM", author="Juan Pablo Caram", - version="Beta", + version="8.9", description="FlatCAM: 2D Computer Aided PCB Manufacturing", options=dict(build_exe=buildOptions), executables=[Executable("FlatCAM.py", icon='share/flatcam_icon48.ico', base=base)]