Juan Pablo Caram пре 12 година
родитељ
комит
3cb9e444c0
4 измењених фајлова са 329 додато и 1890 уклоњено
  1. 17 1741
      camlib.py
  2. BIN
      camlib.pyc
  3. 202 64
      cirkuix.py
  4. 110 85
      cirkuix.ui

+ 17 - 1741
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 i<n_points:
-        if coords[i] in coords[:i]: # closed a loop
-            j = coords[:i].index(coords[i]) # index of repeated point
-            
-            if i-j>1: # 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<n_points:
-        ls = LineString(coords[i-1:i+1])
-        other_points = coords[:i-1]+coords[i+1:] # i=3 ... [0:2] + [4:]
-        if ls.intersects(MultiPoint(other_points)):
-            # Add a copy of that point to the segment
-            isect = ls.intersection(MultiPoint(other_points))
-            if type(isect) == Point:
-                if isect.coords[0] != coords[i-1] and isect.coords[0] != coords[i]:
-                    coords = coords[:i] + [isect.coords[0]] + coords[i:]
-            if type(isect) == MultiPoint:
-                for p in isect:
-                    if p.coords[0] != coords[i-1] and p.coords[0] != coords[i]:
-                        coords = coords[:i] + [p.coords[0]] + coords[i:]
-            return Polygon(coords)
-    return Polygon(coords)
+        self.solid_geometry = cascaded_union(self.solid_geometry)
 
-def fix_poly3(polylist):
-    mp = MultiPolygon(polylist)
-    interior = None
-    exterior = None
-    for i in range(len(polylist)):
-        if polylist[i].contains(mp):
-            exterior = polylist[i]
-            interior = polylist[:i]+polylist[i+1:]
-    return Polygon(exterior.exterior.coords[:], 
-                   [p.exterior.coords[:] for p in interior])
 
 
 class motion:
@@ -815,277 +754,6 @@ def gparse2(gcmds):
     
     return xmin, xmax, ymin, ymax, motions
 
-class canvas:
-    def __init__(self):
-        self.surface = None
-        self.context = None
-        self.origin = [0, 0]    # Pixel coordinate
-        self.resolution = 200.0 # Pixels per unit.
-        self.pixel_height = 0
-        self.pixel_width = 0
-        
-    def create_surface(self, width, height):
-        self.surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height)
-        
-    def crosshair(self, x, y, s):
-        cr = self.context
-        cr.set_line_width (1.0/self.resolution)
-        cr.set_line_cap(cairo.LINE_CAP_ROUND)
-        cr.set_source_rgba (1, 0, 0, 1)
-        cr.move_to(x-s, y-s)
-        cr.line_to(x+s, y+s)
-        cr.stroke()
-        cr.move_to(x-s, y+s)
-        cr.line_to(x+s, y-s)
-        cr.stroke()
-    
-    def draw_motions(self, motions, linewidth, margin=10):
-        # Has many things in common with draw boundaries, merge.
-        # Analyze motions
-        X = 0
-        Y = 1        
-        xmin = Inf
-        xmax = -Inf
-        ymin = Inf
-        ymax = -Inf
-        for mot in motions:
-            if mot.start[X] < xmin:
-                xmin = mot.start[X]
-            if mot.end[X] < xmin:
-                xmin = mot.end[X]
-            if mot.start[X] > 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 #############


+ 202 - 64
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()
-        
-        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.figure.patch.set_visible(False)
+        #self.figure.tight_layout()
+        
+        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()
+        
+        # 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
         
-        f = Figure(dpi=75)
-        a = f.add_subplot(111)
-        a.set_aspect(1)
-        for poly in gerber.solid_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()

+ 110 - 85
cirkuix.ui

@@ -1,6 +1,11 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <interface>
   <!-- interface-requires gtk+ 3.0 -->
+  <object class="GtkImage" id="image1">
+    <property name="visible">True</property>
+    <property name="can_focus">False</property>
+    <property name="stock">gtk-open</property>
+  </object>
   <object class="GtkImage" id="image2">
     <property name="visible">True</property>
     <property name="can_focus">False</property>
@@ -11,6 +16,7 @@
     <property name="height_request">400</property>
     <property name="can_focus">False</property>
     <signal name="destroy" handler="on_closewindow" swapped="no"/>
+    <signal name="key-press-event" handler="on_window_key_press" swapped="no"/>
     <child>
       <object class="GtkBox" id="box1">
         <property name="visible">True</property>
@@ -41,21 +47,22 @@
                     </child>
                     <child>
                       <object class="GtkImageMenuItem" id="imagemenuitem2">
-                        <property name="label">Open geometry</property>
+                        <property name="label">Open Gerber</property>
                         <property name="visible">True</property>
                         <property name="can_focus">False</property>
                         <property name="image">image2</property>
                         <property name="use_stock">False</property>
-                        <signal name="activate" handler="on_fileopengeometry" swapped="no"/>
+                        <signal name="activate" handler="on_fileopengerber" swapped="no"/>
                       </object>
                     </child>
                     <child>
                       <object class="GtkImageMenuItem" id="imagemenuitem3">
-                        <property name="label">gtk-save</property>
+                        <property name="label">Open Excellon</property>
                         <property name="visible">True</property>
                         <property name="can_focus">False</property>
-                        <property name="use_underline">True</property>
-                        <property name="use_stock">True</property>
+                        <property name="image">image1</property>
+                        <property name="use_stock">False</property>
+                        <signal name="activate" handler="on_fileopenexcellon" swapped="no"/>
                       </object>
                     </child>
                     <child>
@@ -175,6 +182,63 @@
             <property name="position">0</property>
           </packing>
         </child>
+        <child>
+          <object class="GtkToolbar" id="toolbar_main">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="toolbar_style">icons</property>
+            <child>
+              <object class="GtkToolButton" id="zoomfit_toolbutton">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="tooltip_text" translatable="yes">Zoom Fit</property>
+                <property name="label" translatable="yes">Fit</property>
+                <property name="use_underline">True</property>
+                <property name="stock_id">gtk-zoom-100</property>
+                <signal name="clicked" handler="on_zoom_fit" swapped="no"/>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="homogeneous">True</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkToolButton" id="zoomin_toolbutton">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="tooltip_text" translatable="yes">Zoom+</property>
+                <property name="label" translatable="yes">Zoom+</property>
+                <property name="use_underline">True</property>
+                <property name="stock_id">gtk-zoom-in</property>
+                <signal name="clicked" handler="on_zoom_in" swapped="no"/>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="homogeneous">True</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkToolButton" id="zoomout_toolbutton">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="tooltip_text" translatable="yes">Zoom-</property>
+                <property name="label" translatable="yes">Zoom-</property>
+                <property name="use_underline">True</property>
+                <property name="stock_id">gtk-zoom-out</property>
+                <signal name="clicked" handler="on_zoom_out" swapped="no"/>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="homogeneous">True</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
         <child>
           <object class="GtkPaned" id="paned1">
             <property name="visible">True</property>
@@ -196,12 +260,13 @@
                     <property name="margin_top">3</property>
                     <property name="orientation">vertical</property>
                     <child>
-                      <object class="GtkCheckButton" id="checkbutton1">
-                        <property name="label" translatable="yes">Merge Geometry</property>
+                      <object class="GtkCheckButton" id="cb_mergepolys">
+                        <property name="label" translatable="yes">Merge Polygons</property>
                         <property name="visible">True</property>
                         <property name="can_focus">True</property>
                         <property name="receives_default">False</property>
                         <property name="xalign">0</property>
+                        <property name="active">True</property>
                         <property name="draw_indicator">True</property>
                       </object>
                       <packing>
@@ -211,8 +276,8 @@
                       </packing>
                     </child>
                     <child>
-                      <object class="GtkCheckButton" id="checkbutton2">
-                        <property name="label" translatable="yes">Different Colors</property>
+                      <object class="GtkCheckButton" id="cb_multicolored">
+                        <property name="label" translatable="yes">Multi-colored</property>
                         <property name="visible">True</property>
                         <property name="can_focus">True</property>
                         <property name="receives_default">False</property>
@@ -226,42 +291,7 @@
                       </packing>
                     </child>
                     <child>
-                      <object class="GtkBox" id="box4">
-                        <property name="visible">True</property>
-                        <property name="can_focus">False</property>
-                        <child>
-                          <object class="GtkLabel" id="label2">
-                            <property name="visible">True</property>
-                            <property name="can_focus">False</property>
-                            <property name="label" translatable="yes">Scale (pix./unit): </property>
-                          </object>
-                          <packing>
-                            <property name="expand">False</property>
-                            <property name="fill">True</property>
-                            <property name="position">0</property>
-                          </packing>
-                        </child>
-                        <child>
-                          <object class="GtkSpinButton" id="spinbutton1">
-                            <property name="visible">True</property>
-                            <property name="can_focus">True</property>
-                            <property name="hexpand">True</property>
-                            <property name="invisible_char">●</property>
-                            <property name="climb_rate">10</property>
-                            <property name="snap_to_ticks">True</property>
-                          </object>
-                          <packing>
-                            <property name="expand">False</property>
-                            <property name="fill">True</property>
-                            <property name="position">1</property>
-                          </packing>
-                        </child>
-                      </object>
-                      <packing>
-                        <property name="expand">False</property>
-                        <property name="fill">True</property>
-                        <property name="position">2</property>
-                      </packing>
+                      <placeholder/>
                     </child>
                   </object>
                 </child>
@@ -269,7 +299,7 @@
                   <object class="GtkLabel" id="label1">
                     <property name="visible">True</property>
                     <property name="can_focus">False</property>
-                    <property name="label" translatable="yes">Gerber</property>
+                    <property name="label" translatable="yes">Options</property>
                   </object>
                   <packing>
                     <property name="tab_fill">False</property>
@@ -294,50 +324,45 @@
               </packing>
             </child>
             <child>
-              <object class="GtkScrolledWindow" id="scrolledwindow1">
+              <object class="GtkGrid" id="grid1">
                 <property name="visible">True</property>
-                <property name="can_focus">True</property>
-                <property name="shadow_type">in</property>
+                <property name="can_focus">False</property>
+                <child>
+                  <placeholder/>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
                 <child>
-                  <object class="GtkViewport" id="viewport2">
+                  <object class="GtkScrollbar" id="scrollbar1">
+                    <property name="height_request">25</property>
                     <property name="visible">True</property>
                     <property name="can_focus">False</property>
                     <property name="hexpand">True</property>
+                    <property name="show_fill_level">True</property>
+                  </object>
+                  <packing>
+                    <property name="left_attach">0</property>
+                    <property name="top_attach">1</property>
+                    <property name="width">1</property>
+                    <property name="height">1</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkScrollbar" id="scrollbar2">
+                    <property name="width_request">25</property>
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
                     <property name="vexpand">True</property>
-                    <child>
-                      <object class="GtkGrid" id="grid1">
-                        <property name="visible">True</property>
-                        <property name="can_focus">False</property>
-                        <child>
-                          <placeholder/>
-                        </child>
-                        <child>
-                          <placeholder/>
-                        </child>
-                        <child>
-                          <placeholder/>
-                        </child>
-                        <child>
-                          <placeholder/>
-                        </child>
-                        <child>
-                          <placeholder/>
-                        </child>
-                        <child>
-                          <placeholder/>
-                        </child>
-                        <child>
-                          <placeholder/>
-                        </child>
-                        <child>
-                          <placeholder/>
-                        </child>
-                        <child>
-                          <placeholder/>
-                        </child>
-                      </object>
-                    </child>
+                    <property name="orientation">vertical</property>
+                    <property name="show_fill_level">True</property>
                   </object>
+                  <packing>
+                    <property name="left_attach">1</property>
+                    <property name="top_attach">0</property>
+                    <property name="width">1</property>
+                    <property name="height">1</property>
+                  </packing>
                 </child>
               </object>
               <packing>
@@ -349,7 +374,7 @@
           <packing>
             <property name="expand">False</property>
             <property name="fill">True</property>
-            <property name="position">1</property>
+            <property name="position">2</property>
           </packing>
         </child>
         <child>
@@ -404,7 +429,7 @@
           <packing>
             <property name="expand">False</property>
             <property name="fill">True</property>
-            <property name="position">2</property>
+            <property name="position">3</property>
           </packing>
         </child>
       </object>