diff --git a/FlatCAMDraw.py b/FlatCAMDraw.py index 7e5f13fe..e0a60788 100644 --- a/FlatCAMDraw.py +++ b/FlatCAMDraw.py @@ -14,7 +14,7 @@ from shapely.geometry.base import BaseGeometry from numpy import arctan2, Inf, array, sqrt, pi, ceil, sin, cos, sign, dot from numpy.linalg import solve -from mpl_toolkits.axes_grid.anchored_artists import AnchoredDrawingArea +#from mpl_toolkits.axes_grid.anchored_artists import AnchoredDrawingArea from rtree import index as rtindex diff --git a/camlib.py b/camlib.py index 47de3a5e..62c1302f 100644 --- a/camlib.py +++ b/camlib.py @@ -17,8 +17,8 @@ import re import collections import numpy as np import matplotlib -import matplotlib.pyplot as plt -from scipy.spatial import Delaunay, KDTree +#import matplotlib.pyplot as plt +#from scipy.spatial import Delaunay, KDTree from rtree import index as rtindex @@ -2982,206 +2982,206 @@ def parse_gerber_number(strnumber, frac_digits): return int(strnumber)*(10**(-frac_digits)) -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_: - 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 - - # determine which line ends are unconnected - connected = filter(lambda (i1,i2): (i1,i2) != (i11,i12) and (i1 == i11 or i2 == i11), lineIndices_) - i11Unconnected = len(connected) == 0 - - 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 xrange(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_: +# 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 +# +# # determine which line ends are unconnected +# connected = filter(lambda (i1,i2): (i1,i2) != (i11,i12) and (i1 == i11 or i2 == i11), lineIndices_) +# i11Unconnected = len(connected) == 0 +# +# 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 xrange(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):