diff --git a/camlib.py b/camlib.py index f231ffbc..83e3e70b 100644 --- a/camlib.py +++ b/camlib.py @@ -107,6 +107,9 @@ class Gerber (Geometry): # Flashes [{'loc':[float,float], 'aperture':dict}] self.flashes = [] + # Geometry from flashes + self.flash_geometry = [] + def fix_regions(self): ''' Overwrites the region polygons with fixed @@ -227,17 +230,14 @@ class Gerber (Geometry): # EOF, create shapely LineString if something in path self.paths.append({"linestring":LineString(path), "aperture":last_path_aperture}) - - def create_geometry(self): - if len(self.buffered_paths) == 0: - self.buffer_paths() - self.fix_regions() - flash_polys = [] + + def do_flashes(self): + self.flash_geometry = [] for flash in self.flashes: aperture = self.apertures[flash['aperture']] if aperture['type'] == 'C': # Circles circle = Point(flash['loc']).buffer(aperture['size']/2) - flash_polys.append(circle) + self.flash_geometry.append(circle) continue if aperture['type'] == 'R': # Rectangles loc = flash['loc'] @@ -248,14 +248,20 @@ class Gerber (Geometry): miny = loc[1] - height/2 maxy = loc[1] + height/2 rectangle = shply_box(minx, miny, maxx, maxy) - flash_polys.append(rectangle) + self.flash_geometry.append(rectangle) continue - print "WARNING: Aperture type %s not implemented"%(aperture['type']) #TODO: Add support for type='O' + print "WARNING: Aperture type %s not implemented"%(aperture['type']) + + def create_geometry(self): + if len(self.buffered_paths) == 0: + self.buffer_paths() + self.fix_regions() + self.do_flashes() self.solid_geometry = cascaded_union( self.buffered_paths + [poly['polygon'] for poly in self.regions] + - flash_polys) + self.flash_geometry) class CNCjob: def __init__(self, units="in", kind="generic", z_move = 0.1, @@ -546,75 +552,8 @@ class Excellon(Geometry): for drill in self.drills: poly = Point(drill['point']).buffer(sizes[drill['tool']]/2.0) self.solid_geometry.append(poly) - -def fix_poly(poly): - ''' - Fixes polygons with internal cutouts by identifying - loops and touching segments. Loops are extracted - as individual polygons. If a smaller loop is still - not a valid Polygon, fix_poly2() adds vertices such - that fix_poly() can continue to extract smaller loops. - ''' - if poly.is_valid: # Nothing to do - return [poly] - - coords = poly.exterior.coords[:] - n_points = len(coords) - i = 0 - result = [] - while i1: # points do not repeat in 1 step - sub_poly = Polygon(coords[j:i+1]) - if sub_poly.is_valid: - result.append(sub_poly) - elif sub_poly.area > 0: - sub_poly = fix_poly2(sub_poly) - result += fix_poly(sub_poly) # try again - - # Preserve the repeated point such as not to break the - # remaining geometry - remaining = coords[:j+1]+coords[i+1:n_points+1] - rem_poly = Polygon(remaining) - if len(remaining)>2 and rem_poly.area > 0: - result += fix_poly(rem_poly) - break - i += 1 - return result - -def fix_poly2(poly): - coords = poly.exterior.coords[:] - n_points = len(coords) - ls = None - i = 1 - while i xmax: - xmax = mot.end[X] - if mot.end[X] > xmax: - xmax = mot.end[X] - if mot.start[Y] < ymin: - ymin = mot.start[Y] - if mot.end[Y] < ymin: - ymin = mot.end[Y] - if mot.start[Y] > ymax: - ymax = mot.end[Y] - if mot.end[Y] > ymax: - ymax = mot.end[Y] - width = xmax - xmin - height = ymax - ymin - print "x in", xmin, xmax - print "y in", ymin, ymax - print "width", width - print "heigh", height - - # Create surface if it doesn't exist - if self.surface == None: - self.pixel_width = int(width*self.resolution + 2*margin) - self.pixel_height = int(height*self.resolution + 2*margin) - self.create_surface(self.pixel_width, self.pixel_height) - self.origin = [int(-xmin*self.resolution + margin), - int(-ymin*self.resolution + margin)] - print "Created surface: %d x %d"%(self.pixel_width, self.pixel_height) - print "Origin: %d, %d"%(self.origin[X], self.origin[Y]) - - # Context - # Flip and shift - self.context = cairo.Context(self.surface) - cr = self.context - - cr.set_source_rgb(0.9, 0.9, 0.9) - cr.rectangle(0,0, self.pixel_width, self.pixel_height) - cr.fill() - - cr.scale(self.resolution, -self.resolution) - cr.translate(self.origin[X]/self.resolution, - (-self.pixel_height+self.origin[Y])/self.resolution) - - # Draw - cr.move_to(0,0) - cr.set_line_width (linewidth) - cr.set_line_cap(cairo.LINE_CAP_ROUND) - n = len(motions) - for i in range(0, n): - - - #if motions[i].depth<0 and i>0 and motions[i-1].depth>0: - if motions[i].depth <= 0: - # change to cutting - #print "x", - # Draw previous travel - cr.set_source_rgba (0.3, 0.2, 0.1, 1.0) # Solid color - #cr.stroke() - #if motions[i].depth >0 and i>0 and motions[i-1].depth<0: - if motions[i].depth > 0: - # change to cutting - #print "-", - # Draw previous cut - cr.set_source_rgba (0.3, 0.2, 0.5, 0.2) - #cr.stroke() - - if motions[i].typ == 'line': - cr.move_to(motions[i].start[0], motions[i].start[1]) - cr.line_to(motions[i].end[0], motions[i].end[1]) - cr.stroke() - #print 'cr.line_to(%f, %f)'%(motions[i].end[0], motions[i].end[0]), - - if motions[i].typ == 'arcacw': - c = (motions[i].offset[0]+motions[i].start[0], - motions[i].offset[1]+motions[i].start[1]) - r = sqrt(motions[i].offset[0]**2 + motions[i].offset[1]**2) - ts = arctan2(-motions[i].offset[1], -motions[i].offset[0]) - te = arctan2(-c[1]+motions[i].end[1], -c[0]+motions[i].end[0]) - if te <= ts: - te += 2*pi - cr.arc(c[0], c[1], r, ts, te) - cr.stroke() - - if motions[i].typ == 'arccw': - c = (motions[i].offset[0]+motions[i].start[0], - motions[i].offset[1]+motions[i].start[1]) - r = sqrt(motions[i].offset[0]**2 + motions[i].offset[1]**2) - ts = arctan2(-motions[i].offset[1], -motions[i].offset[0]) - te = arctan2(-c[1]+motions[i].end[1], -c[0]+motions[i].end[0]) - if te <= ts: - te += 2*pi - cr.arc(c[0], c[1], r, te, ts) - cr.stroke() - - def draw_boundaries(self, boundaries, linewidth, margin=10): - ''' - margin Margin in pixels. - ''' - # Analyze boundaries - X = 0 - Y = 1 - #Z = 2 - xmin = Inf - xmax = -Inf - ymin = Inf - ymax = -Inf - for seg in boundaries[0]: - for vertex in seg: - try: - if vertex[X] < xmin: - xmin = vertex[X] - if vertex[X] > xmax: - xmax = vertex[X] - if vertex[Y] < ymin: - ymin = vertex[Y] - if vertex[Y] > ymax: - ymax = vertex[Y] - except: - print "Woops! vertex = [", [x for x in vertex], "]" - width = xmax - xmin - height = ymax - ymin - print "x in", xmin, xmax - print "y in", ymin, ymax - print "width", width - print "heigh", height - - # Create surface if it doesn't exist - if self.surface == None: - self.pixel_width = int(width*self.resolution + 2*margin) - self.pixel_height = int(height*self.resolution + 2*margin) - self.create_surface(self.pixel_width, self.pixel_height) - self.origin = [int(-xmin*self.resolution + margin), - int(-ymin*self.resolution + margin)] - print "Created surface: %d x %d"%(self.pixel_width, self.pixel_height) - print "Origin: %d, %d"%(self.origin[X], self.origin[Y]) - - # Context - # Flip and shift - self.context = cairo.Context(self.surface) - cr = self.context - - cr.set_source_rgb(0.9, 0.9, 0.9) - cr.rectangle(0,0, self.pixel_width, self.pixel_height) - cr.fill() - - cr.scale(self.resolution, -self.resolution) - cr.translate(self.origin[X]/self.resolution, - (-self.pixel_height+self.origin[Y])/self.resolution) - - # Draw - - cr.set_line_width (linewidth) - cr.set_line_cap(cairo.LINE_CAP_ROUND) - cr.set_source_rgba (0.3, 0.2, 0.5, 1) - for seg in boundaries[0]: - #print "segment" - cr.move_to(seg[0][X],seg[0][Y]) - for i in range(1,len(seg)): - #print seg[i][X],seg[i][Y] - cr.line_to(seg[i][X], seg[i][Y]) - cr.stroke() - -def plotby(b, res, linewidth, ims=None): - ''' - Creates a Cairo image object for the "boundarys" object - generated by read_gerber(). - ''' - X = 0 - Y = 1 - xmin = Inf - xmax = -Inf - ymin = Inf - ymax = -Inf - for seg in b[0]: - for vertex in seg: - try: - if vertex[X] < xmin: - xmin = vertex[X] - if vertex[X] > xmax: - xmax = vertex[X] - if vertex[Y] < ymin: - ymin = vertex[Y] - if vertex[Y] > ymax: - ymax = vertex[Y] - except: - print "Woops! vertex = [", [x for x in vertex], "]" - - width = xmax - xmin - height = ymax - ymin - print "x in", xmin, xmax - print "y in", ymin, ymax - print "width", width - print "heigh", height - - WIDTH = int((xmax-xmin)*res) - HEIGHT = int((ymax-ymin)*res) - # Create a new image if none given - if ims == None: - ims = cairo.ImageSurface(cairo.FORMAT_ARGB32, WIDTH, HEIGHT) - cr = cairo.Context(ims) - cr.scale(res, -res) - #cr.scale(res, res) - #cr.translate(0, -(ymax-ymin)) - #cr.translate(-xmin, -ymin) - cr.translate(-xmin, -(ymax)) - cr.set_line_width (linewidth) - cr.set_line_cap(cairo.LINE_CAP_ROUND) - cr.set_source_rgba (0.3, 0.2, 0.5, 1) - - - - - for seg in b[0]: - #print "segment" - cr.move_to(seg[0][X],seg[0][Y]) - for i in range(1,len(seg)): - #print seg[i][X],seg[i][Y] - cr.line_to(seg[i][X], seg[i][Y]) - cr.stroke() - - - cr.scale(1,-1) - cr.translate(-xmin, -(ymax)) - cr.set_source_rgba (1, 0, 0, 1) - cr.select_font_face("Arial", cairo.FONT_SLANT_NORMAL, - cairo.FONT_WEIGHT_NORMAL) - cr.set_font_size(0.1) - cr.move_to(0, 0) - cr.show_text("(0,0)") - cr.move_to(1, 1) - cr.show_text("(1,1)") - - return ims - ############### cam.py #################### def coord(gstr,digits,fraction): ''' @@ -1108,1397 +776,5 @@ def coord(gstr,digits,fraction): gerby = y return [x,y] -def read_Gerber_Shapely(filename, nverts=10): - ''' - Gerber parser. - ''' - EPS = 1e-20 - TYPE = 0 - SIZE = 1 - WIDTH = 1 - HEIGHT = 2 - - gfile = open(filename, 'r') - gstr = gfile.readlines() - gfile.close() - segment = -1 - xold = [] - yold = [] - boundary = [] - macros = [] - N_macros = 0 - apertures = [[] for i in range(1000)] - for gline in gstr: - if (find(gline, "%FS") != -1): - ### format statement ### - index = find(gline, "X") - digits = int(gline[index + 1]) - fraction = int(gline[index + 2]) - continue - elif (find(gline, "%AM") != -1): - ### aperture macro ### - index = find(gline, "%AM") - index1 = find(gline, "*") - macros.append([]) - macros[-1] = gline[index + 3:index1] - N_macros += 1 - continue - elif (find(gline, "%MOIN*%") != -1): - # inches - continue - elif (find(gline, "G01*") != -1): - ### linear interpolation ### - continue - elif (find(gline, "G70*") != -1): - ### inches ### - continue - elif (find(gline, "G75*") != -1): - ### circular interpolation ### - continue - elif (find(gline, "%ADD") != -1): - ### aperture definition ### - index = find(gline, "%ADD") - parse = 0 - if (find(gline, "C,") != -1): - ## circle ## - index = find(gline, "C,") - index1 = find(gline, "*") - aperture = int(gline[4:index]) - size = float(gline[index + 2:index1]) - apertures[aperture] = ["C", size] - print " read aperture", aperture, ": circle diameter", size - continue - elif (find(gline, "O,") != -1): - ## obround ## - index = find(gline, "O,") - aperture = int(gline[4:index]) - index1 = find(gline, ",", index) - index2 = find(gline, "X", index) - index3 = find(gline, "*", index) - width = float(gline[index1 + 1:index2]) - height = float(gline[index2 + 1:index3]) - apertures[aperture] = ["O", width, height] - print " read aperture", aperture, ": obround", width, "x", height - continue - elif (find(gline, "R,") != -1): - ## rectangle ## - index = find(gline, "R,") - aperture = int(gline[4:index]) - index1 = find(gline, ",", index) - index2 = find(gline, "X", index) - index3 = find(gline, "*", index) - width = float(gline[index1 + 1:index2]) - height = float(gline[index2 + 1:index3]) - apertures[aperture] = ["R", width, height] - print " read aperture", aperture, ": rectangle", width, "x", height - continue - for macro in range(N_macros): - ## macros ## - index = find(gline, macros[macro] + ',') - if (index != -1): - # hack: assume macros can be approximated by - # a circle, and has a size parameter - aperture = int(gline[4:index]) - index1 = find(gline, ",", index) - index2 = find(gline, "*", index) - size = float(gline[index1 + 1:index2]) - apertures[aperture] = ["C", size] - print " read aperture", aperture, ": macro (assuming circle) diameter", size - parse = 1 - continue - if (parse == 0): - print " aperture not implemented:", gline - return - # End of if aperture definition - elif (find(gline, "D01*") != -1): - ### pen down ### - [xnew, ynew] = coord(gline, digits, fraction) - if (size > EPS): - if ((abs(xnew - xold) > EPS) | (abs(ynew - yold) > EPS)): - newpath = stroke(xold, yold, xnew, ynew, size, nverts=nverts) - boundary.append(newpath) - segment += 1 - else: - boundary[segment].append([xnew, ynew, []]) - xold = xnew - yold = ynew - continue - elif (find(gline, "D02*") != -1): - ### pen up ### - [xold, yold] = coord(gline, digits, fraction) - if (size < EPS): - boundary.append([]) - segment += 1 - boundary[segment].append([xold, yold, []]) - newpath = [] - continue - elif (find(gline, "D03*") != -1): - ### flash ### - if (find(gline, "D03*") == 0): - # coordinates on preceeding line - [xnew, ynew] = [xold, yold] - else: - # coordinates on this line - [xnew, ynew] = coord(gline, digits, fraction) - if (apertures[aperture][TYPE] == "C"): - # circle - boundary.append([]) - segment += 1 - size = apertures[aperture][SIZE] - for i in range(nverts): - angle = i * 2.0 * pi / (nverts - 1.0) - x = xnew + (size / 2.0) * cos(angle) - y = ynew + (size / 2.0) * sin(angle) - boundary[segment].append([x, y, []]) - elif (apertures[aperture][TYPE] == "R"): - # rectangle - boundary.append([]) - segment += 1 - width = apertures[aperture][WIDTH] / 2.0 - height = apertures[aperture][HEIGHT] / 2.0 - boundary[segment].append([xnew - width, ynew - height, []]) - boundary[segment].append([xnew + width, ynew - height, []]) - boundary[segment].append([xnew + width, ynew + height, []]) - boundary[segment].append([xnew - width, ynew + height, []]) - boundary[segment].append([xnew - width, ynew - height, []]) - elif (apertures[aperture][TYPE] == "O"): - # obround - boundary.append([]) - segment += 1 - width = apertures[aperture][WIDTH] - height = apertures[aperture][HEIGHT] - if (width > height): - for i in range(nverts / 2): - angle = i * pi / (nverts / 2 - 1.0) + pi / 2.0 - x = xnew - (width - height) / 2.0 + (height / 2.0) * cos(angle) - y = ynew + (height / 2.0) * sin(angle) - boundary[segment].append([x, y, []]) - for i in range(nverts / 2): - angle = i * pi / (nverts / 2 - 1.0) - pi / 2.0 - x = xnew + (width - height) / 2.0 + (height / 2.0) * cos(angle) - y = ynew + (height / 2.0) * sin(angle) - boundary[segment].append([x, y, []]) - else: - for i in range(nverts / 2): - angle = i * pi / (nverts / 2 - 1.0) + pi - x = xnew + (width / 2.0) * cos(angle) - y = ynew - (height - width) / 2.0 + (width / 2.0) * sin(angle) - boundary[segment].append([x, y, []]) - for i in range(nverts / 2): - angle = i * pi / (nverts / 2 - 1.0) - x = xnew + (width / 2.0) * cos(angle) - y = ynew + (height - width) / 2.0 + (width / 2.0) * sin(angle) - boundary[segment].append([x, y, []]) - boundary[segment].append(boundary[segment][0]) - else: - print " aperture", apertures[aperture][TYPE], "is not implemented" - return - xold = xnew - yold = ynew - continue # End of flash - elif (find(gline, "D") == 0): - ### change aperture ### - index = find(gline, '*') - aperture = int(gline[1:index]) - size = apertures[aperture][SIZE] - continue - elif (find(gline, "G54D") == 0): - ### change aperture ### - index = find(gline, '*') - aperture = int(gline[4:index]) - size = apertures[aperture][SIZE] - continue - else: - print " not parsed:", gline - boundarys[0] = boundary - - -def read_Gerber(filename, nverts=10): - ''' - Gerber parser. - ''' - global boundarys - EPS = 1e-20 - TYPE = 0 - SIZE = 1 - WIDTH = 1 - HEIGHT = 2 - - gfile = open(filename, 'r') - gstr = gfile.readlines() - gfile.close() - segment = -1 - xold = [] - yold = [] - boundary = [] - macros = [] - N_macros = 0 - apertures = [[] for i in range(1000)] - for gline in gstr: - if (find(gline, "%FS") != -1): - ### format statement ### - index = find(gline, "X") - digits = int(gline[index + 1]) - fraction = int(gline[index + 2]) - continue - elif (find(gline, "%AM") != -1): - ### aperture macro ### - index = find(gline, "%AM") - index1 = find(gline, "*") - macros.append([]) - macros[-1] = gline[index + 3:index1] - N_macros += 1 - continue - elif (find(gline, "%MOIN*%") != -1): - # inches - continue - elif (find(gline, "G01*") != -1): - ### linear interpolation ### - continue - elif (find(gline, "G70*") != -1): - ### inches ### - continue - elif (find(gline, "G75*") != -1): - ### circular interpolation ### - continue - elif (find(gline, "%ADD") != -1): - ### aperture definition ### - index = find(gline, "%ADD") - parse = 0 - if (find(gline, "C,") != -1): - ## circle ## - index = find(gline, "C,") - index1 = find(gline, "*") - aperture = int(gline[4:index]) - size = float(gline[index + 2:index1]) - apertures[aperture] = ["C", size] - print " read aperture", aperture, ": circle diameter", size - continue - elif (find(gline, "O,") != -1): - ## obround ## - index = find(gline, "O,") - aperture = int(gline[4:index]) - index1 = find(gline, ",", index) - index2 = find(gline, "X", index) - index3 = find(gline, "*", index) - width = float(gline[index1 + 1:index2]) - height = float(gline[index2 + 1:index3]) - apertures[aperture] = ["O", width, height] - print " read aperture", aperture, ": obround", width, "x", height - continue - elif (find(gline, "R,") != -1): - ## rectangle ## - index = find(gline, "R,") - aperture = int(gline[4:index]) - index1 = find(gline, ",", index) - index2 = find(gline, "X", index) - index3 = find(gline, "*", index) - width = float(gline[index1 + 1:index2]) - height = float(gline[index2 + 1:index3]) - apertures[aperture] = ["R", width, height] - print " read aperture", aperture, ": rectangle", width, "x", height - continue - for macro in range(N_macros): - ## macros ## - index = find(gline, macros[macro] + ',') - if (index != -1): - # hack: assume macros can be approximated by - # a circle, and has a size parameter - aperture = int(gline[4:index]) - index1 = find(gline, ",", index) - index2 = find(gline, "*", index) - size = float(gline[index1 + 1:index2]) - apertures[aperture] = ["C", size] - print " read aperture", aperture, ": macro (assuming circle) diameter", size - parse = 1 - continue - if (parse == 0): - print " aperture not implemented:", gline - return - # End of if aperture definition - elif (find(gline, "D01*") != -1): - ### pen down ### - [xnew, ynew] = coord(gline, digits, fraction) - if (size > EPS): - if ((abs(xnew - xold) > EPS) | (abs(ynew - yold) > EPS)): - newpath = stroke(xold, yold, xnew, ynew, size, nverts=nverts) - boundary.append(newpath) - segment += 1 - else: - boundary[segment].append([xnew, ynew, []]) - xold = xnew - yold = ynew - continue - elif (find(gline, "D02*") != -1): - ### pen up ### - [xold, yold] = coord(gline, digits, fraction) - if (size < EPS): - boundary.append([]) - segment += 1 - boundary[segment].append([xold, yold, []]) - newpath = [] - continue - elif (find(gline, "D03*") != -1): - ### flash ### - if (find(gline, "D03*") == 0): - # coordinates on preceeding line - [xnew, ynew] = [xold, yold] - else: - # coordinates on this line - [xnew, ynew] = coord(gline, digits, fraction) - if (apertures[aperture][TYPE] == "C"): - # circle - boundary.append([]) - segment += 1 - size = apertures[aperture][SIZE] - for i in range(nverts): - angle = i * 2.0 * pi / (nverts - 1.0) - x = xnew + (size / 2.0) * cos(angle) - y = ynew + (size / 2.0) * sin(angle) - boundary[segment].append([x, y, []]) - elif (apertures[aperture][TYPE] == "R"): - # rectangle - boundary.append([]) - segment += 1 - width = apertures[aperture][WIDTH] / 2.0 - height = apertures[aperture][HEIGHT] / 2.0 - boundary[segment].append([xnew - width, ynew - height, []]) - boundary[segment].append([xnew + width, ynew - height, []]) - boundary[segment].append([xnew + width, ynew + height, []]) - boundary[segment].append([xnew - width, ynew + height, []]) - boundary[segment].append([xnew - width, ynew - height, []]) - elif (apertures[aperture][TYPE] == "O"): - # obround - boundary.append([]) - segment += 1 - width = apertures[aperture][WIDTH] - height = apertures[aperture][HEIGHT] - if (width > height): - for i in range(nverts / 2): - angle = i * pi / (nverts / 2 - 1.0) + pi / 2.0 - x = xnew - (width - height) / 2.0 + (height / 2.0) * cos(angle) - y = ynew + (height / 2.0) * sin(angle) - boundary[segment].append([x, y, []]) - for i in range(nverts / 2): - angle = i * pi / (nverts / 2 - 1.0) - pi / 2.0 - x = xnew + (width - height) / 2.0 + (height / 2.0) * cos(angle) - y = ynew + (height / 2.0) * sin(angle) - boundary[segment].append([x, y, []]) - else: - for i in range(nverts / 2): - angle = i * pi / (nverts / 2 - 1.0) + pi - x = xnew + (width / 2.0) * cos(angle) - y = ynew - (height - width) / 2.0 + (width / 2.0) * sin(angle) - boundary[segment].append([x, y, []]) - for i in range(nverts / 2): - angle = i * pi / (nverts / 2 - 1.0) - x = xnew + (width / 2.0) * cos(angle) - y = ynew + (height - width) / 2.0 + (width / 2.0) * sin(angle) - boundary[segment].append([x, y, []]) - boundary[segment].append(boundary[segment][0]) - else: - print " aperture", apertures[aperture][TYPE], "is not implemented" - return - xold = xnew - yold = ynew - continue # End of flash - elif (find(gline, "D") == 0): - ### change aperture ### - index = find(gline, '*') - aperture = int(gline[1:index]) - size = apertures[aperture][SIZE] - continue - elif (find(gline, "G54D") == 0): - ### change aperture ### - index = find(gline, '*') - aperture = int(gline[4:index]) - size = apertures[aperture][SIZE] - continue - else: - print " not parsed:", gline - - boundarys[0] = boundary - -def read_Excellon(filename): - global boundarys - # - # Excellon parser - # - file = open(filename,'r') - str = file.readlines() - file.close() - segment = -1 - line = 0 - nlines = len(str) - boundary = [] - #header = TRUE - drills = [[] for i in range(1000)] - while line < nlines: - if ((find(str[line],"T") != -1) & (find(str[line],"C") != -1) \ - & (find(str[line],"F") != -1)): - # - # alternate drill definition style - # - index = find(str[line],"T") - index1 = find(str[line],"C") - index2 = find(str[line],"F") - drill = int(str[line][1:index1]) - print str[line][index1+1:index2] - size = float(str[line][index1+1:index2]) - drills[drill] = ["C",size] - print " read drill",drill,"size:",size - line += 1 - continue - if ((find(str[line],"T") != -1) & (find(str[line]," ") != -1) \ - & (find(str[line],"in") != -1)): - # - # alternate drill definition style - # - index = find(str[line],"T") - index1 = find(str[line]," ") - index2 = find(str[line],"in") - drill = int(str[line][1:index1]) - print str[line][index1+1:index2] - size = float(str[line][index1+1:index2]) - drills[drill] = ["C",size] - print " read drill",drill,"size:",size - line += 1 - continue - elif ((find(str[line],"T") != -1) & (find(str[line],"C") != -1)): - # - # alternate drill definition style - # - index = find(str[line],"T") - index1 = find(str[line],"C") - drill = int(str[line][1:index1]) - size = float(str[line][index1+1:-1]) - drills[drill] = ["C",size] - print " read drill",drill,"size:",size - line += 1 - continue - elif (find(str[line],"T") == 0): - # - # change drill - # - index = find(str[line],'T') - drill = int(str[line][index+1:-1]) - size = drills[drill][SIZE] - line += 1 - continue - elif (find(str[line],"X") != -1): - # - # drill location - # - index = find(str[line],"X") - index1 = find(str[line],"Y") - x0 = float(int(str[line][index+1:index1])/10000.0) - y0 = float(int(str[line][index1+1:-1])/10000.0) - line += 1 - boundary.append([]) - segment += 1 - size = drills[drill][SIZE] - for i in range(nverts): - angle = -i*2.0*pi/(nverts-1.0) - x = x0 + (size/2.0)*cos(angle) - y = y0 + (size/2.0)*sin(angle) - boundary[segment].append([x,y,[]]) - continue - else: - print " not parsed:",str[line] - line += 1 - boundarys[0] = boundary - -def stroke(x0,y0,x1,y1,width, nverts=10): - # - # stroke segment with width - # - #print "stroke:",x0,y0,x1,y1,width - X = 0 - Y = 1 - dx = x1 - x0 - dy = y1 - y0 - d = sqrt(dx*dx + dy*dy) - dxpar = dx / d - dypar = dy / d - dxperp = dypar - dyperp = -dxpar - dx = -dxperp * width/2.0 - dy = -dyperp * width/2.0 - angle = pi/(nverts/2-1.0) - c = cos(angle) - s = sin(angle) - newpath = [] - for i in range(nverts/2): - newpath.append([x0+dx,y0+dy,0]) - [dx,dy] = [c*dx-s*dy, s*dx+c*dy] - dx = dxperp * width/2.0 - dy = dyperp * width/2.0 - for i in range(nverts/2): - newpath.append([x1+dx,y1+dy,0]) - [dx,dy] = [c*dx-s*dy, s*dx+c*dy] - x0 = newpath[0][X] - y0 = newpath[0][Y] - newpath.append([x0,y0,0]) - return newpath - -def contour(event): - ''' - Uses displace() and adjust_contours() - ''' - global boundarys, toolpaths, contours - # - # contour boundary to find toolpath - # - print "contouring boundary ..." - xyscale = float(sxyscale.get()) - undercut = float(sundercut.get()) - if (undercut != 0.0): - print " undercutting contour by",undercut - N_contour = 1 - if (len(boundarys) == 1): - # - # 2D contour - # - toolpaths[0] = [] - for n in range(N_contour): - toolrad = (n+1)*(float(sdia.get())/2.0-undercut)/xyscale - contours[0] = displace(boundarys[0],toolrad) - altern = ialtern.get(); - if (altern == TRUE): - contours[0] = adjust_contour(contours[0],boundarys[0],toolrad) - else: - contours[0] = prune(contours[0],-1,event) - toolpaths[0].extend(contours[0]) - plot(event) - else: - # - # 3D contour - # - for layer in range(len(boundarys)): - toolpaths[layer] = [] - contours[layer] = [] - if (boundarys[layer] != []): - [xindex,yindex,zindex,z] = orient(boundarys[layer]) - for n in range(N_contour): - toolrad = (n+1)*(float(sdia.get())/2.0-undercut)/xyscale - path = project(boundarys[layer],xindex,yindex) - contour = displace(path,toolrad) - contour = prune(contour,-1,event) - contours[layer] = lift(contour,xindex,yindex,zindex,z) - toolpaths[layer].extend(contours[layer]) - plot(event) - print " done" - -def adjust_contour(path, boundary, toolrad): - print " adjust_contour ..." - newpath = [] - for seg in range(len(path)): - newpath.append([]) -# print "points" -# for vert in range(len(path[seg])): -# Px = boundary[seg][vert][X] -# Py = boundary[seg][vert][Y] -# print "%2i : %5.2f,%5.2f" % (vert, Px, Py) - -# print "len(path[seg]): ", len(path[seg]) -# print "len(boundary[seg]: ", len(boundary[seg]) - for vert in range(len(path[seg])): - Px = path[seg][vert][X] - Py = path[seg][vert][Y] - avgvalue = [] - avgvalue.append(0.0) - avgvalue.append(0.0) - changed = 1 - iteration = 0 - avg = [] - while ((iteration < MAXITER) & (changed != 0)): - changed = 0 - - for orgvert in range(len(boundary[seg]) - 1): -# if (orgvert == 0): -# x0 = boundary[seg][len(boundary[seg]) - 1][X] -# y0 = boundary[seg][len(boundary[seg]) - 1][Y] -# else: - x0 = boundary[seg][orgvert][X] - y0 = boundary[seg][orgvert][Y] - - x1 = boundary[seg][orgvert + 1][X] - y1 = boundary[seg][orgvert + 1][Y] - - #print ' A %5.2f,%5.2f B %5.2f,%5.2f' % (x0, y0, x1, y1) - - dx = x1 - x0 - dy = y1 - y0 - - nx = dy; - ny = -dx; - - d = abs(((nx * Px + ny * Py) - (nx * x0 + ny * y0) ) / \ - sqrt( nx * nx + ny * ny )) - - pre = orgvert - 1 - - if (pre < 0): - pre = len(boundary[seg]) - 2 - post = orgvert + 2 - if (post == len(boundary[seg])): - post = 1 - - - #print " distance %5.2f" % d - #print "toolrad ", toolrad - if (d - toolrad < - NOISE): -# if (x0 < 1000000000): - #print " low distance" - # check if inside - pre = orgvert - 1 - if (pre < 0): - pre = len(boundary[seg]) - 2 - post = orgvert + 2 - if (post == len(boundary[seg])): - post = 1 - - diff_d_pre_x = x1 - boundary[seg][pre][X] - diff_d_pre_y = y1 - boundary[seg][pre][Y] - diff_d_post_x = boundary[seg][post][X] - x0 - diff_d_post_y = boundary[seg][post][Y] - y0 - - #print "diff_pre %5.2f,%5.2f" % (diff_d_pre_x, diff_d_pre_y) - #print "diff_post %5.2f,%5.2f" % (diff_d_post_x, diff_d_post_y) - - #n_pre_x = diff_d_pre_y - #n_pre_y = -diff_d_pre_x - #n_post_x = diff_d_post_y - #n_post_y = -diff_d_post_x - - - diff_px0 = Px - x0 - diff_py0 = Py - y0 - diff_px1 = Px - x1 - diff_py1 = Py - y1 - - #print "diff p0 %5.2f,%5.2f" % (diff_px0, diff_py0) - #print "diff p1 %5.2f,%5.2f" % (diff_px1, diff_py1) - - pre_x = boundary[seg][pre][X] - pre_y = boundary[seg][pre][Y] - post_x = boundary[seg][post][X] - post_y = boundary[seg][post][Y] - - v0_x = x0 - pre_x - v0_y = y0 - pre_y - - v1_x = post_x - x0 - v1_y = post_y - y0 - - - if ((v0_x * nx + v0_y * ny) > -NOISE): #angle > 180 - #print "XXXXXXXXXXXXXXXXXXX pre > 180" - value0 = diff_d_pre_x * diff_px0 + diff_d_pre_y * diff_py0 - #value0 = diff_px0 * dx + diff_py0 * dy - else: - value0 = diff_px0 * dx + diff_py0 * dy - - if (-(v1_x * nx + v1_y * ny) > -NOISE): #angle > 180 - #print "XXXXXXXXXXXXXXXXXXX post > 180" - value1 = diff_d_post_x * diff_px1 + diff_d_post_y * diff_py1 - #value1 = diff_px1 * dx + diff_py1 * dy - else: - value1 = diff_px1 * dx + diff_py1 * dy - - #if ((value0 > -NOISE) & (value1 < NOISE)): - #print " P %5.2f,%5.2f a %5.2f,%5.2f b %5.2f,%5.2f - inside (%8.5f & %8.5f)" % (Px, Py, x0, y0, x1, y1, value0, value1) - #else: - #print " P %5.2f,%5.2f a %5.2f,%5.2f b %5.2f,%5.2f - outside (%8.5f & %8.5f)" % (Px, Py, x0, y0, x1, y1, value0, value1) - -# if (vert == 3) & (orgvert == 2): -# print "-p1 %5.2f,%5.2f p2 %5.2f,%5.2f P %5.2f,%5.2f " % (x0, y0, x1, y1, Px, Py) -# print "d %5.2f,%5.2f" % (dx, dy) -# print "n %5.2f,%5.2f" % (nx, ny) -# print "di0 %5.2f,%5.2f" % (diff_px0, diff_py0) -# print "di1 %5.2f,%5.2f" % (diff_px1, diff_py1) -# print "val %5.2f,%5.2f" % (value0, value1) - - -# if ((value0 == 0) | (value1 == 0)): -# #print " fix me" -# value = value1 -# else: - if ((value0 > -NOISE) & (value1 < NOISE)): - #value = value1 * value0; - #if (value < 0 ): - #print 'P %5.2f,%5.2f' % (Px, Py) - #print ' A %5.2f,%5.2f B %5.2f,%5.2f' % (x0, y0, x1, y1) - #print " distance %5.2f" % d - #print " move" - ln = sqrt((nx * nx) + (ny * ny)) - Px = Px + (nx / ln) * (toolrad - d); - Py = Py + (ny / ln) * (toolrad - d); - changed += 1 - iteration += 1 -# print ' new %5.2f,%5.2f' % (Px, Py) - if (iteration > MAXITER - AVGITER): -# print "ii %2i %7.4f,%7.4f" % (iteration, Px,Py) - avgvalue[X] += Px - avgvalue[Y] += Py -# if (iteration > 1): -# print iteration - if (iteration >= MAXITER): -# print " diff", (iteration - (MAXITER - AVGITER)) - avgvalue[X] /= float(iteration - (MAXITER - AVGITER)) - avgvalue[Y] /= float(iteration - (MAXITER - AVGITER)) - newpath[seg].append([avgvalue[X],avgvalue[Y],[]]) -# print "NEW : %7.4f,%7.4f" % (avgvalue[X], avgvalue[Y]) - else: - newpath[seg].append([Px,Py,[]]) - -# for vert in range(len(path[seg])): -# Px = newpath[seg][vert][X] -# Py = newpath[seg][vert][Y] -# print "NEW %2i : %5.2f,%5.2f" % (vert, Px, Py) - - return newpath - -def displace(path,toolrad): - ''' - Uses offset() - ''' - # - # displace path inwards by tool radius - # - print " displacing ..." - newpath = [] - for seg in range(len(path)): - newpath.append([]) - if (len(path[seg]) > 2): - for vert1 in range(len(path[seg])-1): - if (vert1 == 0): - vert0 = len(path[seg]) - 2 - else: - vert0 = vert1 - 1 - vert2 = vert1 + 1 - x0 = path[seg][vert0][X] - x1 = path[seg][vert1][X] - x2 = path[seg][vert2][X] - y0 = path[seg][vert0][Y] - y1 = path[seg][vert1][Y] - y2 = path[seg][vert2][Y] - [dx,dy] = offset(x0,x1,x2,y0,y1,y2,toolrad) - if (dx != []): - newpath[seg].append([(x1+dx),(y1+dy),[]]) - x0 = newpath[seg][0][X] - y0 = newpath[seg][0][Y] - newpath[seg].append([x0,y0,[]]) - elif (len(path[seg]) == 2): - x0 = path[seg][0][X] - y0 = path[seg][0][Y] - x1 = path[seg][1][X] - y1 = path[seg][1][Y] - x2 = 2*x1 - x0 - y2 = 2*y1 - y0 - [dx,dy] = offset(x0,x1,x2,y0,y1,y2,toolrad) - if (dx != []): - newpath[seg].append([x0+dx,y0+dy,[]]) - newpath[seg].append([x1+dx,y1+dy,[]]) - else: - newpath[seg].append([x0,y0,[]]) - newpath[seg].append([x1,y1,[]]) - else: - print " displace: shouldn't happen" - return newpath - -def offset(x0,x1,x2,y0,y1,y2,r): - # - # calculate offset by r for vertex 1 - # - dx0 = x1 - x0 - dx1 = x2 - x1 - dy0 = y1 - y0 - dy1 = y2 - y1 - d0 = sqrt(dx0*dx0 + dy0*dy0) - d1 = sqrt(dx1*dx1 + dy1*dy1) - if ((d0 == 0) | (d1 == 0)): - return [[],[]] - dx0par = dx0 / d0 - dy0par = dy0 / d0 - dx0perp = dy0 / d0 - dy0perp = -dx0 / d0 - dx1perp = dy1 / d1 - dy1perp = -dx1 / d1 - #print "offset points:",x0,x1,x2,y0,y1,y2 - #print "offset normals:",dx0perp,dx1perp,dy0perp,dy1perp - if ((abs(dx0perp*dy1perp - dx1perp*dy0perp) < EPS) | \ - (abs(dy0perp*dx1perp - dy1perp*dx0perp) < EPS)): - dx = r * dx1perp - dy = r * dy1perp - #print " offset planar:",dx,dy - elif ((abs(dx0perp+dx1perp) < EPS) & (abs(dy0perp+dy1perp) < EPS)): - dx = r * dx1par - dy = r * dy1par - #print " offset hairpin:",dx,dy - else: - dx = r*(dy1perp - dy0perp) / \ - (dx0perp*dy1perp - dx1perp*dy0perp) - dy = r*(dx1perp - dx0perp) / \ - (dy0perp*dx1perp - dy1perp*dx0perp) - #print " offset OK:",dx,dy - return [dx,dy] - -def prune(path,sign,event): - ''' - Uses add_intersections() and union() - ''' - # - # prune path intersections - # - # first find the intersections - # - print " intersecting ..." - [path, intersections, seg_intersections] = add_intersections(path) - #print 'path:',path - #print 'intersections:',intersections - #print 'seg_intersections:',seg_intersections - # - # then copy non-intersecting segments to new path - # - newpath = [] - for seg in range(len(seg_intersections)): - #print "non-int" - if (seg_intersections[seg] == []): - newpath.append(path[seg]) - # - # finally follow and remove the intersections - # - print " pruning ..." - i = 0 - newseg = 0 - while (i < len(intersections)): - if (intersections[i] == []): - # - # skip null intersections - # - i += 1 - #print "null" - else: - istart = i - intersection = istart - # - # skip interior intersections - # - oldseg = -1 - interior = TRUE - while 1: - #print 'testing intersection',intersection,':',intersections[intersection] - if (intersections[intersection] == []): - #seg == oldseg - seg = oldseg - else: - [seg,vert] = union(intersection,path,intersections,sign) - #print ' seg',seg,'vert',vert,'oldseg',oldseg - if (seg == oldseg): - #print " remove interior intersection",istart - seg0 = intersections[istart][0][SEG] - vert0 = intersections[istart][0][VERT] - path[seg0][vert0][INTERSECT] = -1 - seg1 = intersections[istart][1][SEG] - vert1 = intersections[istart][1][VERT] - path[seg1][vert1][INTERSECT] = -1 - intersections[istart] = [] - break - elif (seg == []): - seg = intersections[intersection][0][SEG] - vert = intersections[intersection][0][SEG] - oldseg = [] - else: - oldseg = seg - intersection = [] - while (intersection == []): - if (vert < (len(path[seg])-1)): - vert += 1 - else: - vert = 0 - intersection = path[seg][vert][INTERSECT] - if (intersection == -1): - intersection = istart - break - elif (intersection == istart): - #print ' back to',istart - interior = FALSE - intersection = istart - break - # - # save path if valid boundary intersection - # - if (interior == FALSE): - newseg = len(newpath) - newpath.append([]) - while 1: - #print 'keeping intersection',intersection,':',intersections[intersection] - [seg,vert] = union(intersection,path,intersections,sign) - if (seg == []): - seg = intersections[intersection][0][SEG] - vert = intersections[intersection][0][VERT] - #print ' seg',seg,'vert',vert - intersections[intersection] = [] - intersection = [] - while (intersection == []): - if (vert < (len(path[seg])-1)): - x = path[seg][vert][X] - y = path[seg][vert][Y] - newpath[newseg].append([x,y,[]]) - vert += 1 - else: - vert = 0 - intersection = path[seg][vert][INTERSECT] - if (intersection == istart): - #print ' back to',istart - x = path[seg][vert][X] - y = path[seg][vert][Y] - newpath[newseg].append([x,y,[]]) - break - i += 1 - return newpath - -def add_intersections(path): - ''' - Uses intersect() and insert() (FIX THIS, BELONG TO OTHER LIBRARY) - ''' - # - # add vertices at path intersections - # - events = [] - active = [] - # - # lexicographic sort segments - # - for seg in range(len(path)): - nverts = len(path[seg]) - for vert in range(nverts-1): - x0 = path[seg][vert][X] - y0 = path[seg][vert][Y] - x1 = path[seg][vert+1][X] - y1 = path[seg][vert+1][Y] - if (x1 < x0): - [x0, x1] = [x1, x0] - [y0, y1] = [y1, y0] - if ((x1 == x0) & (y1 < y0)): - [y0, y1] = [y1, y0] - events.append([x0,y0,START,seg,vert]) - events.append([x1,y1,END,seg,vert]) - events.sort() - # - # find intersections with a sweep line - # - intersection = 0 - verts = [] - for event in range(len(events)): -# status.set(" edge "+str(event)+"/"+str(len(events)-1)+" ") -# outframe.update() - # - # loop over start/end points - # - type = events[event][INDEX] - seg0 = events[event][EVENT_SEG] - vert0 = events[event][EVENT_VERT] - n0 = len(path[seg0]) - if (events[event][INDEX] == START): - # - # loop over active points - # - for point in range(len(active)): - sega = active[point][SEG] - verta = active[point][VERT] - if ((sega == seg0) & \ - ((abs(vert0-verta) == 1) | (abs(vert0-verta) == (n0-2)))): - #print seg0,vert0,verta,n0 - continue - [xloc,yloc] = intersect(path,seg0,vert0,sega,verta) - if (xloc != []): - # - # found intersection, save it - # - d0 = (path[seg0][vert0][X]-xloc)**2 + (path[seg0][vert0][Y]-yloc)**2 - verts.append([seg0,vert0,d0,xloc,yloc,intersection]) - da = (path[sega][verta][X]-xloc)**2 + (path[sega][verta][Y]-yloc)**2 - verts.append([sega,verta,da,xloc,yloc,intersection]) - intersection += 1 - active.append([seg0,vert0]) - else: - active.remove([seg0,vert0]) - print " found",intersection,"intersections" - # - # add vertices at path intersections - # - verts.sort() - verts.reverse() - for vertex in range(len(verts)): - seg = verts[vertex][SEG] - vert = verts[vertex][VERT] - intersection = verts[vertex][IINTERSECT] - x = verts[vertex][XINTERSECT] - y = verts[vertex][YINTERSECT] - insert(path,x,y,seg,vert,intersection) - # - # make vertex table and segment list of intersections - # -# status.set(namedate) -# outframe.update() - nintersections = len(verts)/2 - intersections = [[] for i in range(nintersections)] - for seg in range(len(path)): - for vert in range(len(path[seg])): - intersection = path[seg][vert][INTERSECT] - if (intersection != []): - intersections[intersection].append([seg,vert]) - seg_intersections = [[] for i in path] - for i in range(len(intersections)): - if (len(intersections[i]) != 2): - print " shouldn't happen: i",i,intersections[i] - else: - seg_intersections[intersections[i][0][SEG]].append(i) - seg_intersections[intersections[i][A][SEG]].append(i) - return [path, intersections, seg_intersections] - -def intersect(path,seg0,vert0,sega,verta): - # - # test and return edge intersection - # - if ((seg0 == sega) & (vert0 == 0) & (verta == (len(path[sega])-2))): - #print " return (0-end)" - return [[],[]] - x0 = path[seg0][vert0][X] - y0 = path[seg0][vert0][Y] - x1 = path[seg0][vert0+1][X] - y1 = path[seg0][vert0+1][Y] - dx01 = x1 - x0 - dy01 = y1 - y0 - d01 = sqrt(dx01*dx01 + dy01*dy01) - if (d01 == 0): - # - # zero-length segment, return no intersection - # - #print "zero-length segment" - return [[],[]] - dxpar01 = dx01 / d01 - dypar01 = dy01 / d01 - dxperp01 = dypar01 - dyperp01 = -dxpar01 - xa = path[sega][verta][X] - ya = path[sega][verta][Y] - xb = path[sega][verta+1][X] - yb = path[sega][verta+1][Y] - dx0a = xa - x0 - dy0a = ya - y0 - dpar0a = dx0a*dxpar01 + dy0a*dypar01 - dperp0a = dx0a*dxperp01 + dy0a*dyperp01 - dx0b = xb - x0 - dy0b = yb - y0 - dpar0b = dx0b*dxpar01 + dy0b*dypar01 - dperp0b = dx0b*dxperp01 + dy0b*dyperp01 - #if (dperp0a*dperp0b > EPS): - if (((dperp0a > EPS) & (dperp0b > EPS)) | \ - ((dperp0a < -EPS) & (dperp0b < -EPS))): - # - # vertices on same side, return no intersection - # - #print " same side" - return [[],[]] - elif ((abs(dperp0a) < EPS) & (abs(dperp0b) < EPS)): - # - # edges colinear, return no intersection - # - #d0a = (xa-x0)*dxpar01 + (ya-y0)*dypar01 - #d0b = (xb-x0)*dxpar01 + (yb-y0)*dypar01 - #print " colinear" - return [[],[]] - # - # calculation distance to intersection - # - d = (dpar0a*abs(dperp0b)+dpar0b*abs(dperp0a))/(abs(dperp0a)+abs(dperp0b)) - if ((d < -EPS) | (d > (d01+EPS))): - # - # intersection outside segment, return no intersection - # - #print " found intersection outside segment" - return [[],[]] - else: - # - # intersection in segment, return intersection - # - #print " found intersection in segment s0 v0 sa va",seg0,vert0,sega,verta - xloc = x0 + dxpar01*d - yloc = y0 + dypar01*d - return [xloc,yloc] - -def insert(path,x,y,seg,vert,intersection): - # - # insert a vertex at x,y in seg,vert, if needed - # - d0 = (path[seg][vert][X]-x)**2 + (path[seg][vert][Y]-y)**2 - d1 = (path[seg][vert+1][X]-x)**2 + (path[seg][vert+1][Y]-y)**2 - #print "check insert seg",seg,"vert",vert,"intersection",intersection - if ((d0 > EPS) & (d1 > EPS)): - #print " added intersection vertex",vert+1 - path[seg].insert((vert+1),[x,y,intersection]) - return 1 - elif (d0 < EPS): - if (path[seg][vert][INTERSECT] == []): - path[seg][vert][INTERSECT] = intersection - #print " added d0",vert - return 0 - elif (d1 < EPS): - if (path[seg][vert+1][INTERSECT] == []): - path[seg][vert+1][INTERSECT] = intersection - #print " added d1",vert+1 - return 0 - else: - #print " shouldn't happen: d0",d0,"d1",d1 - return 0 - -def union(i,path,intersections,sign): - # - # return edge to exit intersection i for a union - # - #print "union: intersection",i,"in",intersections - seg0 = intersections[i][0][SEG] - #print "seg0",seg0 - vert0 = intersections[i][0][VERT] - x0 = path[seg0][vert0][X] - y0 = path[seg0][vert0][Y] - if (vert0 < (len(path[seg0])-1)): - vert1 = vert0 + 1 - else: - vert1 = 0 - x1 = path[seg0][vert1][X] - y1 = path[seg0][vert1][Y] - dx01 = x1-x0 - dy01 = y1-y0 - sega = intersections[i][A][SEG] - verta = intersections[i][A][VERT] - xa = path[sega][verta][X] - ya = path[sega][verta][Y] - if (verta < (len(path[sega])-1)): - vertb = verta + 1 - else: - vertb = 0 - xb = path[sega][vertb][X] - yb = path[sega][vertb][Y] - dxab = xb-xa - dyab = yb-ya - dot = dxab*dy01 - dyab*dx01 - #print " dot",dot - if (abs(dot) <= EPS): - print " colinear" - seg = [] - vert= [] - elif (dot > EPS): - seg = intersections[i][(1-sign)/2][SEG] - vert = intersections[i][(1-sign)/2][VERT] - else: - seg = intersections[i][(1+sign)/2][SEG] - vert = intersections[i][(1+sign)/2][VERT] - return [seg,vert] - -# MODIFIED -def read(filename): #MOD - event = None #MOD - print "read(event)" - global vertices, faces, boundarys, toolpaths, contours, slices,\ - xmin, xmax, ymin, ymax, zmin, zmax, noise_flag - # - # read file - # - faces = [] - contours = [[]] - boundarys = [[]] - toolpaths = [[]] - slices = [[]] - #filename = infile.get() #MOD - if ((find(filename,".cmp") != -1) | (find(filename,".CMP")!= -1) \ - | (find(filename,".sol")!= -1) | (find(filename,".SOL") != -1) \ - | (find(filename,".plc")!= -1) | (find(filename,".PLC")!= -1) \ - | (find(filename,".sts")!= -1) | (find(filename,".STS")!= -1) \ - | (find(filename,".gtl")!= -1) | (find(filename,".GTL")!= -1) \ - | (find(filename,".stc")!= -1) | (find(filename,".STC")!= -1)): - print "reading Gerber file",filename - read_Gerber(filename) - elif ((find(filename,".drl") != -1) | (find(filename,".DRL") != -1) | \ - (find(filename,".drd") != -1) | (find(filename,".DRD") != -1)): - print "reading Excellon file",filename - read_Excellon(filename) - elif ((find(filename,".dxf") != -1) | (find(filename,".DXF") != -1)): - print "reading DXF file",filename - read_DXF(filename) - elif (find(filename,".stl") != -1): - print "reading STL file",filename - read_STL(filename) - elif (find(filename,".jpg") != -1): - print "reading image file",filename - read_image(filename) - elif (find(filename,".svg") != -1): - print "reading SVG file",filename - read_SVG(filename) - else: - print "unsupported file type" - return - xmin = HUGE - xmax = -HUGE - ymin = HUGE - ymax = -HUGE - zmin = HUGE - zmax = -HUGE - if (len(boundarys) == 1): - # - # 2D file - # - boundary = boundarys[0] - sum = 0 - for segment in range(len(boundary)): - sum += len(boundary[segment]) - for vertex in range(len(boundary[segment])): - x = boundary[segment][vertex][X] - y = boundary[segment][vertex][Y] - if (x < xmin): xmin = x - if (x > xmax): xmax = x - if (y < ymin): ymin = y - if (y > ymax): ymax = y - print " found",len(boundary),"polygons,",sum,"vertices" - print " xmin: %0.3g "%xmin,"xmax: %0.3g "%xmax,"dx: %0.3g "%(xmax-xmin) - print " ymin: %0.3g "%ymin,"ymax: %0.3g "%ymax,"dy: %0.3g "%(ymax-ymin) - if (noise_flag == 1): - if ((xmax-xmin) < (ymax-ymin)): - delta = (xmax-xmin)*NOISE - else: - delta = (ymax-ymin)*NOISE - for segment in range(len(boundary)): - for vertex in range(len(boundary[segment])): - boundary[segment][vertex][X] += gauss(0,delta) - boundary[segment][vertex][Y] += gauss(0,delta) - print " added %.3g perturbation"%delta - boundarys[0] = boundary - elif (len(boundarys) > 1): - # - # 3D layers - # - for layer in range(len(boundarys)): - boundary = boundarys[layer] - sum = 0 - for segment in range(len(boundary)): - sum += len(boundary[segment]) - for vertex in range(len(boundary[segment])): - x = boundary[segment][vertex][X3] - y = boundary[segment][vertex][Y3] - z = boundary[segment][vertex][Z3] - if (x < xmin): xmin = x - if (x > xmax): xmax = x - if (y < ymin): ymin = y - if (y > ymax): ymax = y - if (z < zmin): zmin = z - if (z > zmax): zmax = z - print " layer",layer,"found",len(boundary),"polygon(s),",sum,"vertices" - if (noise_flag == 1): - if ((xmax-xmin) < (ymax-ymin)): - delta = (xmax-xmin)*NOISE - else: - delta = (ymax-ymin)*NOISE - for segment in range(len(boundary)): - for vertex in range(len(boundary[segment])): - boundary[segment][vertex][X3] += gauss(0,delta) - boundary[segment][vertex][Y3] += gauss(0,delta) - boundary[segment][vertex][Z3] += gauss(0,delta) - boundarys[layer] = boundary - print " xmin: %0.3g "%xmin,"xmax: %0.3g "%xmax,"dx: %0.3g "%(xmax-xmin) - print " ymin: %0.3g "%ymin,"ymax: %0.3g "%ymax,"dy: %0.3g "%(ymax-ymin) - print " zmin: %0.3g "%zmin,"zmax: %0.3g "%zmax,"dy: %0.3g "%(zmax-zmin) - print " added %.3g perturbation"%delta - elif (faces != []): - # - # 3D faces - # - for vertex in range(len(vertices)): - x = vertices[vertex][X] - y = vertices[vertex][Y] - z = vertices[vertex][Z] - if (x < xmin): xmin = x - if (x > xmax): xmax = x - if (y < ymin): ymin = y - if (y > ymax): ymax = y - if (z < zmin): zmin = z - if (z > zmax): zmax = z - print " found",len(vertices),"vertices,",len(faces),"faces" - print " xmin: %0.3g "%xmin,"xmax: %0.3g "%xmax,"dx: %0.3g "%(xmax-xmin) - print " ymin: %0.3g "%ymin,"ymax: %0.3g "%ymax,"dy: %0.3g "%(ymax-ymin) - print " zmin: %0.3g "%zmin,"zmax: %0.3g "%zmax,"dz: %0.3g "%(zmax-zmin) - if (noise_flag == 1): - delta = (zmax-zmin)*NOISE - for vertex in range(len(vertices)): - vertices[vertex][X] += gauss(0,delta) - vertices[vertex][Y] += gauss(0,delta) - vertices[vertex][Z] += gauss(0,delta) - print " added %.3g perturbation"%delta - else: - print "shouldn't happen in read" - #camselect(event) MOD - print "End read(event)" - -def write_G(boundarys, toolpaths, scale=1.0, thickness=1.0, feed=1, zclear=0.1, zcut=-0.005): - X = 0 - Y = 1 - #global boundarys, toolpaths, xmin, ymin, zmin, zmax - # - # G code output - # - #xyscale = float(sxyscale.get()) - xyscale = scale - #zscale = float(sxyscale.get()) - #zscale = scale - #dlayer = float(sthickness.get())/zscale - #dlayer = thickness/zscale - #feed = float(sfeed.get()) - #xoff = float(sxmin.get()) - xmin*xyscale - #yoff = float(symin.get()) - ymin*xyscale - #cool = icool.get() - #text = outfile.get() - - output = "" #file = open(text, 'w') - output += "%\n" #file.write("%\n") - output += "O1234\n" #file.write("O1234\n") - #file.write("T"+stool.get()+"M06\n") # tool - output += "G90G54\n" #file.write("G90G54\n") # absolute positioning with respect to set origin - output += "F%0.3f\n"%feed #file.write("F%0.3f\n"%feed) # feed rate - #file.write("S"+sspindle.get()+"\n") # spindle speed - #if (cool == TRUE): file.write("M08\n") # coolant on - output += "G00Z%.4f\n"%zclear #file.write("G00Z"+szup.get()+"\n") # move up before starting spindle - output += "M03\n" #file.write("M03\n") # spindle on clockwise - nsegment = 0 - for layer in range((len(boundarys)-1),-1,-1): - if (toolpaths[layer] == []): - path = boundarys[layer] - else: - path = toolpaths[layer] - #if (szdown.get() == " "): - # zdown = zoff + zmin + (layer-0.50)*dlayer - #else: - # zdown = float(szdown.get()) - for segment in range(len(path)): - nsegment += 1 - vertex = 0 - x = path[segment][vertex][X]*xyscale #+ xoff - y = path[segment][vertex][Y]*xyscale #+ yoff - output += "G00X%0.4f"%x+"Y%0.4f"%y+"Z%.4f"%zclear+"\n" #file.write("G00X%0.4f"%x+"Y%0.4f"%y+"Z"+szup.get()+"\n") # rapid motion - output += "G01Z%0.4f"%zcut+"\n" #file.write("G01Z%0.4f"%zdown+"\n") # linear motion - for vertex in range(1,len(path[segment])): - x = path[segment][vertex][X]*xyscale #+ xoff - y = path[segment][vertex][Y]*xyscale #+ yoff - output += "X%0.4f"%x+"Y%0.4f"%y+"\n" #file.write("X%0.4f"%x+"Y%0.4f"%y+"\n") - output += "Z%.4f\n"%zclear #file.write("Z"+szup.get()+"\n") - output += "G00Z%.4f\n"%zclear #file.write("G00Z"+szup.get()+"\n") # move up before stopping spindle - output += "M05\n" #file.write("M05\n") # spindle stop - #if (cool == TRUE): file.write("M09\n") # coolant off - output += "M30\n" #file.write("M30\n") # program end and reset - output += "%\n" #file.write("%\n") - #file.close() - print "wrote",nsegment,"G code toolpath segments" - return output ################ end of cam.py ############# diff --git a/camlib.pyc b/camlib.pyc index 0697d73b..04f9034e 100644 Binary files a/camlib.pyc and b/camlib.pyc differ diff --git a/cirkuix.py b/cirkuix.py index d0c0aa8b..1ddd289e 100644 --- a/cirkuix.py +++ b/cirkuix.py @@ -15,69 +15,66 @@ from camlib import * class App: def __init__(self): + + ######################################## + ## GUI ## + ######################################## self.gladefile = "cirkuix.ui" self.builder = Gtk.Builder() self.builder.add_from_file(self.gladefile) self.window = self.builder.get_object("window1") - self.positionLabel = self.builder.get_object("label3") - #self.drawingarea = self.builder.get_object("drawingarea1") - #self.drawingarea.connect("draw", self.cairopaint) + self.positionLabel = self.builder.get_object("label3") + self.grid = self.builder.get_object("grid1") - self.grid = self.builder.get_object("grid1") + ## Event handling ## self.builder.connect_signals(self) + + self.figure = None + self.axes = None + self.canvas = None + + ## Make plot area ## self.mplpaint() self.window.show_all() - self.gerbers = [] + ######################################## + ## DATA ## + ######################################## + self.gerbers = [] + self.excellons = [] + + self.mouse = None Gtk.main() def mplpaint(self): - f = Figure(dpi=50) - a = f.add_subplot(111) - a.set_aspect(1) - t = arange(0.0,5.0,0.01) - s = sin(2*pi*t) - a.plot(t,s) - a.grid() + self.figure = Figure(dpi=50) + #self.axes = self.figure.add_subplot(111) + self.axes = self.figure.add_axes([0.05,0.05,0.9,0.9]) + self.axes.set_aspect(1) + #t = arange(0.0,5.0,0.01) + #s = sin(2*pi*t) + #self.axes.plot(t,s) + self.axes.grid() #a.patch.set_visible(False) Background of the axes - f.patch.set_visible(False) - f.tight_layout() + self.figure.patch.set_visible(False) + #self.figure.tight_layout() - canvas = FigureCanvas(f) # a Gtk.DrawingArea - canvas.set_size_request(600,400) - canvas.mpl_connect('button_press_event', self.on_click_over_plot) - canvas.mpl_connect('motion_notify_event', self.on_mouse_move_over_plot) - self.grid.attach(canvas,1,1,600,400) + self.canvas = FigureCanvas(self.figure) # a Gtk.DrawingArea + #self.canvas.set_size_request(600,400) + self.canvas.mpl_connect('button_press_event', self.on_click_over_plot) + self.canvas.mpl_connect('motion_notify_event', self.on_mouse_move_over_plot) + ##self.canvas.mpl_connect('scroll_event', self.on_scroll_over_plot) + ##self.canvas.mpl_connect('key_press_event', self.on_key_over_plot) + + + self.canvas.set_hexpand(1) + self.canvas.set_vexpand(1) + + #self.builder.get_object("viewport2").add(self.canvas) + self.grid.attach(self.canvas,0,0,600,400) + #self.builder.get_object("scrolledwindow1").add(self.canvas) - def cairopaint(self, da, cr): - width = 200 - height = 200 - #cr = widget.window.cairo_create() # Context - cr.set_source_rgb(0.5, 0.5, 0.5) - cr.rectangle(0, 0, width, height) - cr.fill() - - # draw a rectangle - cr.set_source_rgb(1.0, 1.0, 1.0) - cr.rectangle(10, 10, width - 20, height - 20) - cr.fill() - - # draw lines - cr.set_source_rgb(0.0, 0.0, 0.8) - cr.move_to(width / 3.0, height / 3.0) - cr.rel_line_to(0, height / 6.0) - cr.move_to(2 * width / 3.0, height / 3.0) - cr.rel_line_to(0, height / 6.0) - cr.stroke() - - # and a circle - cr.set_source_rgb(1.0, 0.0, 0.0) - radius = min(width, height) - cr.arc(width / 2.0, height / 2.0, radius / 2.0 - 20, 0, 2 * pi) - cr.stroke() - cr.arc(width / 2.0, height / 2.0, radius / 3.0 - 10, pi / 3, 2 * pi / 3) - cr.stroke() def on_filequit(self, param): print "quit from menu" @@ -89,49 +86,190 @@ class App: self.window.destroy() Gtk.main_quit() - def on_fileopengeometry(self, param): - print "File->Open Geometry" + def on_fileopengerber(self, param): + print "File->Open Gerber" dialog = Gtk.FileChooserDialog("Please choose a file", self.window, Gtk.FileChooserAction.OPEN, (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OPEN, Gtk.ResponseType.OK)) response = dialog.run() if response == Gtk.ResponseType.OK: + ## Load the file ## print("Open clicked") print("File selected: " + dialog.get_filename()) gerber = Gerber() gerber.parse_file(dialog.get_filename()) - gerber.create_geometry() self.gerbers.append(gerber) self.plot_gerber(gerber) + ## End ## + elif response == Gtk.ResponseType.CANCEL: + print("Cancel clicked") + dialog.destroy() + + def on_fileopenexcellon(self, param): + print "File->Open Excellon" + dialog = Gtk.FileChooserDialog("Please choose a file", self.window, + Gtk.FileChooserAction.OPEN, + (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, + Gtk.STOCK_OPEN, Gtk.ResponseType.OK)) + response = dialog.run() + if response == Gtk.ResponseType.OK: + ## Load the file ## + print("Open clicked") + print("File selected: " + dialog.get_filename()) + excellon = Excellon() + excellon.parse_file(dialog.get_filename()) + self.excellons.append(excellon) + self.plot_excellon(excellon) + ## End ## elif response == Gtk.ResponseType.CANCEL: print("Cancel clicked") dialog.destroy() def plot_gerber(self, gerber): + gerber.create_geometry() - f = Figure(dpi=75) - a = f.add_subplot(111) - a.set_aspect(1) - for poly in gerber.solid_geometry: + # Options + mergepolys = self.builder.get_object("cb_mergepolys").get_active() + multicolored = self.builder.get_object("cb_multicolored").get_active() + + geometry = None + if mergepolys: + geometry = gerber.solid_geometry + else: + geometry = gerber.buffered_paths + \ + [poly['polygon'] for poly in gerber.regions] + \ + gerber.flash_geometry + + linespec = None + if multicolored: + linespec = '-' + else: + linespec = 'k-' + #f = Figure(dpi=75) + #a = f.add_subplot(111) + #a.set_aspect(1) + for poly in geometry: x, y = poly.exterior.xy - a.plot(x, y) + #a.plot(x, y) + self.axes.plot(x, y, linespec) for ints in poly.interiors: x, y = ints.coords.xy - a.plot(x, y) - a.grid() - f.tight_layout() - canvas = FigureCanvas(f) # a Gtk.DrawingArea - canvas.set_size_request(600,400) - self.grid.attach(canvas,1,1,600,400) - self.window.show_all() + self.axes.plot(x, y, linespec) + + #f.tight_layout() + #canvas = FigureCanvas(f) # a Gtk.DrawingArea + #canvas.set_size_request(600,400) + #self.grid.attach(canvas,1,1,600,400) + #self.window.show_all() + + def plot_excellon(self, excellon): + excellon.create_geometry() + + # Plot excellon + for geo in excellon.solid_geometry: + x, y = geo.exterior.coords.xy + self.axes.plot(x, y, 'r-') + for ints in geo.interiors: + x, y = ints.coords.xy + self.axes.plot(x, y, 'g-') def on_mouse_move_over_plot(self, event): - self.positionLabel.set_label("X: %.4f Y: %.4f"%(event.xdata, event.ydata)) + try: # May fail in case mouse not within axes + self.positionLabel.set_label("X: %.4f Y: %.4f"%( + event.xdata, event.ydata)) + self.mouse = [event.xdata, event.ydata] + except: + self.positionLabel.set_label("X: --- Y: ---") + self.mouse = None def on_click_over_plot(self, event): print 'button=%d, x=%d, y=%d, xdata=%f, ydata=%f'%( event.button, event.x, event.y, event.xdata, event.ydata) + def get_bounds(self): + xmin = Inf + ymin = Inf + xmax = -Inf + ymax = -Inf + + geometry_sets = [self.gerbers, self.excellons] + + for gs in geometry_sets: + for g in gs: + gxmin, gymin, gxmax, gymax = g.solid_geometry.bounds + xmin = min([xmin, gxmin]) + ymin = min([ymin, gymin]) + xmax = max([xmax, gxmax]) + ymax = max([ymax, gymax]) + + return [xmin, ymin, xmax, ymax] + + def on_zoom_in(self, event): + self.zoom(1.5) + return + + def on_zoom_out(self, event): + self.zoom(1/1.5) + + def on_zoom_fit(self, event): + xmin, ymin, xmax, ymax = self.get_bounds() + width = xmax-xmin + height = ymax-ymin + self.axes.set_xlim((xmin-0.05*width, xmax+0.05*width)) + self.axes.set_ylim((ymin-0.05*height, ymax+0.05*height)) + self.canvas.queue_draw() + return + + def zoom(self, factor, center=None): + xmin, xmax = self.axes.get_xlim() + ymin, ymax = self.axes.get_ylim() + width = xmax-xmin + height = ymax-ymin + + if center == None: + center = [(xmin+xmax)/2.0, (ymin+ymax)/2.0] + + # For keeping the point at the pointer location + relx = (xmax-center[0])/width + rely = (ymax-center[1])/height + + new_width = width/factor + new_height = height/factor + + self.axes.set_xlim((center[0]-new_width*(1-relx), center[0]+new_width*relx)) + self.axes.set_ylim((center[1]-new_height*(1-rely), center[1]+new_height*rely)) + + self.canvas.queue_draw() + +# def on_scroll_over_plot(self, event): +# print "Scroll" +# center = [event.xdata, event.ydata] +# if sign(event.step): +# self.zoom(1.5, center=center) +# else: +# self.zoom(1/1.5, center=center) +# +# def on_window_scroll(self, event): +# print "Scroll" +# +# def on_key_over_plot(self, event): +# print 'you pressed', event.key, event.xdata, event.ydata + + def on_window_key_press(self, widget, event): + print event.get_keycode(), event.get_keyval() + val = int(event.get_keyval()[1]) + + if val == 49: # 1 + self.on_zoom_fit(None) + return + + if val == 50: # 2 + self.zoom(1/1.5, self.mouse) + return + + if val == 51: # 3 + self.zoom(1.5, self.mouse) + return app = App() diff --git a/cirkuix.ui b/cirkuix.ui index 3cea5bb8..2bc82838 100644 --- a/cirkuix.ui +++ b/cirkuix.ui @@ -1,6 +1,11 @@ + + True + False + gtk-open + True False @@ -11,6 +16,7 @@ 400 False + True @@ -41,21 +47,22 @@ - Open geometry + Open Gerber True False image2 False - + - gtk-save + Open Excellon True False - True - True + image1 + False + @@ -175,6 +182,63 @@ 0 + + + True + False + icons + + + True + False + Zoom Fit + Fit + True + gtk-zoom-100 + + + + False + True + + + + + True + False + Zoom+ + Zoom+ + True + gtk-zoom-in + + + + False + True + + + + + True + False + Zoom- + Zoom- + True + gtk-zoom-out + + + + False + True + + + + + False + True + 1 + + True @@ -196,12 +260,13 @@ 3 vertical - - Merge Geometry + + Merge Polygons True True False 0 + True True @@ -211,8 +276,8 @@ - - Different Colors + + Multi-colored True True False @@ -226,42 +291,7 @@ - - True - False - - - True - False - Scale (pix./unit): - - - False - True - 0 - - - - - True - True - True - - 10 - True - - - False - True - 1 - - - - - False - True - 2 - + @@ -269,7 +299,7 @@ True False - Gerber + Options False @@ -294,50 +324,45 @@ - + True - True - in + False - + + + + + + + + 25 True False True - True - - - True - False - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + True + + 0 + 1 + 1 + 1 + + + + + 25 + True + False + True + vertical + True + + + 1 + 0 + 1 + 1 + @@ -349,7 +374,7 @@ False True - 1 + 2 @@ -404,7 +429,7 @@ False True - 2 + 3