| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780 |
- import cairo
- #from string import *
- #from math import *
- #from random import *
- #from struct import *
- #import os
- #import sys
- from numpy import arctan2, Inf, array
- from matplotlib.figure import Figure
- # See: http://toblerity.org/shapely/manual.html
- from shapely.geometry import Polygon, LineString, Point
- from shapely.geometry import MultiPoint, MultiPolygon
- from shapely.geometry import box as shply_box
- from shapely.ops import cascaded_union
- class Geometry:
- def __init__(self):
- # Units (in or mm)
- self.units = 'in'
-
- # Final geometry: MultiPolygon
- self.solid_geometry = None
-
- def isolation_geometry(self, offset):
- '''
- Creates contours around geometry at a given
- offset distance.
- '''
- return self.solid_geometry.buffer(offset)
-
- def bounds(self):
- '''
- Returns coordinates of rectangular bounds
- of geometry: (xmin, ymin, xmax, ymax).
- '''
- if self.solid_geometry == None:
- print "Warning: solid_geometry not computed yet."
- return (0,0,0,0)
- return self.solid_geometry.bounds
-
- def size(self):
- '''
- Returns (width, height) of rectangular
- bounds of geometry.
- '''
- if self.solid_geometry == None:
- print "Warning: solid_geometry not computed yet."
- return 0
- bounds = self.bounds()
- return (bounds[2]-bounds[0], bounds[3]-bounds[1])
-
- def get_empty_area(self, boundary=None):
- '''
- Returns the complement of self.solid_geometry within
- the given boundary polygon. If not specified, it defaults to
- the rectangular bounding box of self.solid_geometry.
- '''
- if boundary == None:
- boundary = self.solid_geometry.envelope
- return boundary.difference(g.solid_geometry)
-
- def clear_polygon(self, polygon, tooldia, overlap = 0.15):
- '''
- Creates geometry inside a polygon for a tool to cover
- the whole area.
- '''
- poly_cuts = [polygon.buffer(-tooldia/2.0)]
- while(1):
- polygon = poly_cuts[-1].buffer(-tooldia*(1-overlap))
- if polygon.area > 0:
- poly_cuts.append(polygon)
- else:
- break
- return poly_cuts
-
- class Gerber (Geometry):
- def __init__(self):
- # Initialize parent
- Geometry.__init__(self)
-
- # Number format
- self.digits = 3
- self.fraction = 4
-
- ## Gerber elements ##
- # Apertures {'id':{'type':chr,
- # ['size':float], ['width':float],
- # ['height':float]}, ...}
- self.apertures = {}
-
- # Paths [{'linestring':LineString, 'aperture':dict}]
- self.paths = []
-
- # Buffered Paths [Polygon]
- # Paths transformed into Polygons by
- # offsetting the aperture size/2
- self.buffered_paths = []
-
- # Polygon regions [{'polygon':Polygon, 'aperture':dict}]
- self.regions = []
-
- # Flashes [{'loc':[float,float], 'aperture':dict}]
- self.flashes = []
-
- # Geometry from flashes
- self.flash_geometry = []
-
- def fix_regions(self):
- '''
- Overwrites the region polygons with fixed
- versions if found to be invalid (according to Shapely).
- '''
- for region in self.regions:
- if region['polygon'].is_valid == False:
- #polylist = fix_poly(region['polygon'])
- #region['polygon'] = fix_poly3(polylist)
- region['polygon'] = region['polygon'].buffer(0)
-
- def buffer_paths(self):
- self.buffered_paths = []
- for path in self.paths:
- width = self.apertures[path["aperture"]]["size"]
- self.buffered_paths.append(path["linestring"].buffer(width/2))
-
- def aperture_parse(self, gline):
- '''
- Parse gerber aperture definition
- into dictionary of apertures.
- '''
- indexstar = gline.find("*")
- indexC = gline.find("C,")
- if indexC != -1: # Circle, example: %ADD11C,0.1*%
- apid = gline[4:indexC]
- self.apertures[apid] = {"type":"C",
- "size":float(gline[indexC+2:indexstar])}
- return apid
- indexR = gline.find("R,")
- if indexR != -1: # Rectangle, example: %ADD15R,0.05X0.12*%
- apid = gline[4:indexR]
- indexX = gline.find("X")
- self.apertures[apid] = {"type":"R",
- "width":float(gline[indexR+2:indexX]),
- "height":float(gline[indexX+1:indexstar])}
- return apid
- indexO = gline.find("O,")
- if indexO != -1: # Obround
- apid = gline[4:indexO]
- indexX = gline.find("X")
- self.apertures[apid] = {"type":"O",
- "width":float(gline[indexO+2:indexX]),
- "height":float(gline[indexX+1:indexstar])}
- return apid
- print "WARNING: Aperture not implemented:", gline
- return None
-
- def parse_file(self, filename):
- '''
- Calls Gerber.parse_lines() with array of lines
- read from the given file.
- '''
- gfile = open(filename, 'r')
- gstr = gfile.readlines()
- gfile.close()
- self.parse_lines(gstr)
-
- def parse_lines(self, glines):
- '''
- Main Gerber parser.
- '''
- path = [] # Coordinates of the current path
- last_path_aperture = None
- current_aperture = None
-
- for gline in glines:
-
- if gline.find("D01*") != -1: # pen down
- path.append(coord(gline, self.digits, self.fraction))
- last_path_aperture = current_aperture
- continue
-
- if gline.find("D02*") != -1: # pen up
- if len(path) > 1:
- # Path completed, create shapely LineString
- self.paths.append({"linestring":LineString(path),
- "aperture":last_path_aperture})
- path = [coord(gline, self.digits, self.fraction)]
- continue
-
- indexD3 = gline.find("D03*")
- if indexD3 > 0: # Flash
- self.flashes.append({"loc":coord(gline, self.digits, self.fraction),
- "aperture":current_aperture})
- continue
- if indexD3 == 0: # Flash?
- print "WARNING: Uninplemented flash style:", gline
- continue
-
- if gline.find("G37*") != -1: # end region
- # Only one path defines region?
- self.regions.append({"polygon":Polygon(path),
- "aperture":last_path_aperture})
- path = []
- continue
-
- if gline.find("%ADD") != -1: # aperture definition
- self.aperture_parse(gline) # adds element to apertures
- continue
-
- indexstar = gline.find("*")
- if gline.find("D") == 0: # Aperture change
- current_aperture = gline[1:indexstar]
- continue
- if gline.find("G54D") == 0: # Aperture change (deprecated)
- current_aperture = gline[4:indexstar]
- continue
-
- if gline.find("%FS") != -1: # Format statement
- indexX = gline.find("X")
- self.digits = int(gline[indexX + 1])
- self.fraction = int(gline[indexX + 2])
- continue
- print "WARNING: Line ignored:", gline
-
- if len(path) > 1:
- # EOF, create shapely LineString if something in path
- self.paths.append({"linestring":LineString(path),
- "aperture":last_path_aperture})
-
- 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)
- self.flash_geometry.append(circle)
- continue
- if aperture['type'] == 'R': # Rectangles
- loc = flash['loc']
- width = aperture['width']
- height = aperture['height']
- minx = loc[0] - width/2
- maxx = loc[0] + width/2
- miny = loc[1] - height/2
- maxy = loc[1] + height/2
- rectangle = shply_box(minx, miny, maxx, maxy)
- self.flash_geometry.append(rectangle)
- continue
- #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] +
- self.flash_geometry)
- class CNCjob:
- def __init__(self, units="in", kind="generic", z_move = 0.1,
- feedrate = 3.0, z_cut = -0.002):
- # Options
- self.kind = kind
- self.units = units
- self.z_cut = z_cut
- self.z_move = z_move
- self.feedrate = feedrate
-
- # Constants
- self.unitcode = {"in": "G20", "mm": "G21"}
- self.pausecode = "G04 P1"
- self.feedminutecode = "G94"
- self.absolutecode = "G90"
-
- # Output G-Code
- self.gcode = ""
-
- # Bounds of geometry given to CNCjob.generate_from_geometry()
- self.input_geometry_bounds = None
-
- # Tool diameter given to CNCjob.generate_from_geometry()
- self.tooldia = 0
-
- # Output generated by CNCjob.create_gcode_geometry()
- self.G_geometry = None
-
- def generate_from_excellon(self, exobj):
- '''
- Generates G-code for drilling from excellon text.
- self.gcode becomes a list, each element is a
- different job for each tool in the excellon code.
- '''
- self.kind = "drill"
- self.gcode = []
-
- t = "G00 X%.4fY%.4f\n"
- down = "G01 Z%.4f\n"%self.z_cut
- up = "G01 Z%.4f\n"%self.z_move
-
- for tool in exobj.tools:
-
- points = []
- gcode = ""
-
- for drill in exobj.drill:
- if drill['tool'] == tool:
- points.append(drill['point'])
-
- gcode = self.unitcode[self.units] + "\n"
- gcode += self.absolutecode + "\n"
- gcode += self.feedminutecode + "\n"
- gcode += "F%.2f\n"%self.feedrate
- gcode += "G00 Z%.4f\n"%self.z_move # Move to travel height
- gcode += "M03\n" # Spindle start
- gcode += self.pausecode + "\n"
-
- for point in points:
- gcode += t%point
- gcode += down + up
-
- gcode += t%(0,0)
- gcode += "M05\n" # Spindle stop
-
- self.gcode.append(gcode)
-
- def generate_from_geometry(self, geometry, append=True, tooldia=None):
- '''
- Generates G-Code for geometry (Shapely collection).
- '''
- if tooldia == None:
- tooldia = self.tooldia
- else:
- self.tooldia = tooldia
-
- self.input_geometry_bounds = geometry.bounds
-
- if append == False:
- self.gcode = ""
- t = "G0%d X%.4fY%.4f\n"
- self.gcode = self.unitcode[self.units] + "\n"
- self.gcode += self.absolutecode + "\n"
- self.gcode += self.feedminutecode + "\n"
- self.gcode += "F%.2f\n"%self.feedrate
- self.gcode += "G00 Z%.4f\n"%self.z_move # Move to travel height
- self.gcode += "M03\n" # Spindle start
- self.gcode += self.pausecode + "\n"
-
- for geo in geometry:
-
- if type(geo) == Polygon:
- path = list(geo.exterior.coords) # Polygon exterior
- self.gcode += t%(0, path[0][0], path[0][1]) # Move to first point
- self.gcode += "G01 Z%.4f\n"%self.z_cut # Start cutting
- for pt in path[1:]:
- self.gcode += t%(1, pt[0], pt[1]) # Linear motion to point
- self.gcode += "G00 Z%.4f\n"%self.z_move # Stop cutting
- for ints in geo.interiors: # Polygon interiors
- path = list(ints.coords)
- self.gcode += t%(0, path[0][0], path[0][1]) # Move to first point
- self.gcode += "G01 Z%.4f\n"%self.z_cut # Start cutting
- for pt in path[1:]:
- self.gcode += t%(1, pt[0], pt[1]) # Linear motion to point
- self.gcode += "G00 Z%.4f\n"%self.z_move # Stop cutting
- continue
-
- if type(geo) == LineString or type(geo) == LineRing:
- path = list(geo.coords)
- self.gcode += t%(0, path[0][0], path[0][1]) # Move to first point
- self.gcode += "G01 Z%.4f\n"%self.z_cut # Start cutting
- for pt in path[1:]:
- self.gcode += t%(1, pt[0], pt[1]) # Linear motion to point
- self.gcode += "G00 Z%.4f\n"%self.z_move # Stop cutting
- continue
-
- if type(geo) == Point:
- path = list(geo.coords)
- self.gcode += t%(0, path[0][0], path[0][1]) # Move to first point
- self.gcode += "G01 Z%.4f\n"%self.z_cut # Start cutting
- self.gcode += "G00 Z%.4f\n"%self.z_move # Stop cutting
- continue
-
- print "WARNING: G-code generation not implemented for %s"%(str(type(geo)))
-
- self.gcode += "G00 Z%.4f\n"%self.z_move # Stop cutting
- self.gcode += "G00 X0Y0\n"
- self.gcode += "M05\n" # Spindle stop
-
- def create_gcode_geometry(self):
- '''
- G-Code parser (from self.gcode). Generates dictionary with
- single-segment LineString's and "kind" indicating cut or travel,
- fast or feedrate speed.
- '''
- geometry = []
-
- # TODO: ???? bring this into the class??
- gobjs = gparse1b(self.gcode)
-
- # Last known instruction
- current = {'X': 0.0, 'Y': 0.0, 'Z': 0.0, 'G': 0}
-
- # Process every instruction
- for gobj in gobjs:
- if 'Z' in gobj:
- if ('X' in gobj or 'Y' in gobj) and gobj['Z'] != current['Z']:
- print "WARNING: Non-orthogonal motion: From", current
- print " To:", gobj
- current['Z'] = gobj['Z']
-
- if 'G' in gobj:
- current['G'] = gobj['G']
-
- if 'X' in gobj or 'Y' in gobj:
- x = 0
- y = 0
- kind = ["C","F"] # T=travel, C=cut, F=fast, S=slow
- if 'X' in gobj:
- x = gobj['X']
- else:
- x = current['X']
- if 'Y' in gobj:
- y = gobj['Y']
- else:
- y = current['Y']
- if current['Z'] > 0:
- kind[0] = 'T'
- if current['G'] == 1:
- kind[1] = 'S'
- geometry.append({'geom':LineString([(current['X'],current['Y']),
- (x,y)]), 'kind':kind})
-
- # Update current instruction
- for code in gobj:
- current[code] = gobj[code]
-
- self.G_geometry = geometry
- return geometry
-
- def plot(self, tooldia=None, dpi=75, margin=0.1,
- color={"T":["#F0E24D", "#B5AB3A"], "C":["#5E6CFF", "#4650BD"]},
- alpha={"T":0.3, "C":1.0}):
- '''
- Creates a Matplotlib figure with a plot of the
- G-code job.
- '''
- if tooldia == None:
- tooldia = self.tooldia
-
- fig = Figure(dpi=dpi)
- ax = fig.add_subplot(111)
- ax.set_aspect(1)
- xmin, ymin, xmax, ymax = self.input_geometry_bounds
- ax.set_xlim(xmin-margin, xmax+margin)
- ax.set_ylim(ymin-margin, ymax+margin)
-
- if tooldia == 0:
- for geo in self.G_geometry:
- linespec = '--'
- linecolor = color[geo['kind'][0]][1]
- if geo['kind'][0] == 'C':
- linespec = 'k-'
- x, y = geo['geom'].coords.xy
- ax.plot(x, y, linespec, color=linecolor)
- else:
- for geo in self.G_geometry:
- poly = geo['geom'].buffer(tooldia/2.0)
- patch = PolygonPatch(poly, facecolor=color[geo['kind'][0]][0],
- edgecolor=color[geo['kind'][0]][1],
- alpha=alpha[geo['kind'][0]], zorder=2)
- ax.add_patch(patch)
-
- return fig
-
- class Excellon(Geometry):
- def __init__(self):
- Geometry.__init__(self)
-
- self.tools = {}
-
- self.drills = []
-
- def parse_file(self, filename):
- efile = open(filename, 'r')
- estr = efile.readlines()
- efile.close()
- self.parse_lines(estr)
-
- def parse_lines(self, elines):
- '''
- Main Excellon parser.
- '''
- current_tool = ""
-
- for eline in elines:
-
- ## Tool definitions ##
- # TODO: Verify all this
- indexT = eline.find("T")
- indexC = eline.find("C")
- indexF = eline.find("F")
- # Type 1
- if indexT != -1 and indexC > indexT and indexF > indexF:
- tool = eline[1:indexC]
- spec = eline[indexC+1:indexF]
- self.tools[tool] = spec
- continue
- # Type 2
- # TODO: Is this inches?
- #indexsp = eline.find(" ")
- #indexin = eline.find("in")
- #if indexT != -1 and indexsp > indexT and indexin > indexsp:
- # tool = eline[1:indexsp]
- # spec = eline[indexsp+1:indexin]
- # self.tools[tool] = spec
- # continue
- # Type 3
- if indexT != -1 and indexC > indexT:
- tool = eline[1:indexC]
- spec = eline[indexC+1:-1]
- self.tools[tool] = spec
- continue
-
- ## Tool change
- if indexT == 0:
- current_tool = eline[1:-1]
- continue
-
- ## Drill
- indexX = eline.find("X")
- indexY = eline.find("Y")
- if indexX != -1 and indexY != -1:
- x = float(int(eline[indexX+1:indexY])/10000.0)
- y = float(int(eline[indexY+1:-1])/10000.0)
- self.drills.append({'point':Point((x,y)), 'tool':current_tool})
- continue
-
- print "WARNING: Line ignored:", eline
-
- def create_geometry(self):
- self.solid_geometry = []
- sizes = {}
- for tool in self.tools:
- sizes[tool] = float(self.tools[tool])
- for drill in self.drills:
- poly = Point(drill['point']).buffer(sizes[drill['tool']]/2.0)
- self.solid_geometry.append(poly)
- self.solid_geometry = cascaded_union(self.solid_geometry)
- class motion:
- '''
- Represents a machine motion, which can be cutting or just travelling.
- '''
- def __init__(self, start, end, depth, typ='line', offset=None, center=None,
- radius=None, tooldia=0.5):
- self.typ = typ
- self.start = start
- self.end = end
- self.depth = depth
- self.center = center
- self.radius = radius
- self.tooldia = tooldia
- self.offset = offset # (I, J)
-
-
- def gparse1(filename):
- '''
- Parses G-code file into list of dictionaries like
- Examples: {'G': 1.0, 'X': 0.085, 'Y': -0.125},
- {'G': 3.0, 'I': -0.01, 'J': 0.0, 'X': 0.0821, 'Y': -0.1179}
- '''
- f = open(filename)
- gcmds = []
- for line in f:
- line = line.strip()
-
- # Remove comments
- # NOTE: Limited to 1 bracket pair
- op = line.find("(")
- cl = line.find(")")
- if op > -1 and cl > op:
- #comment = line[op+1:cl]
- line = line[:op] + line[(cl+1):]
-
- # Parse GCode
- # 0 4 12
- # G01 X-0.007 Y-0.057
- # --> codes_idx = [0, 4, 12]
- codes = "NMGXYZIJFP"
- codes_idx = []
- i = 0
- for ch in line:
- if ch in codes:
- codes_idx.append(i)
- i += 1
- n_codes = len(codes_idx)
- if n_codes == 0:
- continue
-
- # Separate codes in line
- parts = []
- for p in range(n_codes-1):
- parts.append( line[ codes_idx[p]:codes_idx[p+1] ].strip() )
- parts.append( line[codes_idx[-1]:].strip() )
-
- # Separate codes from values
- cmds = {}
- for part in parts:
- cmds[part[0]] = float(part[1:])
- gcmds.append(cmds)
-
- f.close()
- return gcmds
- def gparse1b(gtext):
- gcmds = []
- lines = gtext.split("\n")
- for line in lines:
- line = line.strip()
-
- # Remove comments
- # NOTE: Limited to 1 bracket pair
- op = line.find("(")
- cl = line.find(")")
- if op > -1 and cl > op:
- #comment = line[op+1:cl]
- line = line[:op] + line[(cl+1):]
-
- # Parse GCode
- # 0 4 12
- # G01 X-0.007 Y-0.057
- # --> codes_idx = [0, 4, 12]
- codes = "NMGXYZIJFP"
- codes_idx = []
- i = 0
- for ch in line:
- if ch in codes:
- codes_idx.append(i)
- i += 1
- n_codes = len(codes_idx)
- if n_codes == 0:
- continue
-
- # Separate codes in line
- parts = []
- for p in range(n_codes-1):
- parts.append( line[ codes_idx[p]:codes_idx[p+1] ].strip() )
- parts.append( line[codes_idx[-1]:].strip() )
-
- # Separate codes from values
- cmds = {}
- for part in parts:
- cmds[part[0]] = float(part[1:])
- gcmds.append(cmds)
- return gcmds
-
-
- def gparse2(gcmds):
-
- x = []
- y = []
- z = []
- xypoints = []
- motions = []
- current_g = None
-
- for cmds in gcmds:
-
- # Destination point
- x_ = None
- y_ = None
- z_ = None
-
- if 'X' in cmds:
- x_ = cmds['X']
- x.append(x_)
- if 'Y' in cmds:
- y_ = cmds['Y']
- y.append(y_)
- if 'Z' in cmds:
- z_ = cmds['Z']
- z.append(z_)
-
- # Ingnore anything but XY movements from here on
- if x_ is None and y_ is None:
- #print "-> no x,y"
- continue
-
- if x_ is None:
- x_ = xypoints[-1][0]
-
- if y_ is None:
- y_ = xypoints[-1][1]
-
- if z_ is None:
- z_ = z[-1]
-
-
- mot = None
-
- if 'G' in cmds:
- current_g = cmds['G']
-
- if current_g == 0: # Fast linear
- if len(xypoints) > 0:
- #print "motion(", xypoints[-1], ", (", x_, ",", y_, "),", z_, ")"
- mot = motion(xypoints[-1], (x_, y_), z_)
-
- if current_g == 1: # Feed-rate linear
- if len(xypoints) > 0:
- #print "motion(", xypoints[-1], ", (", x_, ",", y_, "),", z_, ")"
- mot = motion(xypoints[-1], (x_, y_), z_)
-
- if current_g == 2: # Clockwise arc
- if len(xypoints) > 0:
- if 'I' in cmds and 'J' in cmds:
- mot = motion(xypoints[-1], (x_, y_), z_, offset=(cmds['I'],
- cmds['J']), typ='arccw')
-
- if current_g == 3: # Counter-clockwise arc
- if len(xypoints) > 0:
- if 'I' in cmds and 'J' in cmds:
- mot = motion(xypoints[-1], (x_, y_), z_, offset=(cmds['I'],
- cmds['J']), typ='arcacw')
-
- if mot is not None:
- motions.append(mot)
-
- xypoints.append((x_, y_))
-
- x = array(x)
- y = array(y)
- z = array(z)
- xmin = min(x)
- xmax = max(x)
- ymin = min(y)
- ymax = max(y)
- print "x:", min(x), max(x)
- print "y:", min(y), max(y)
- print "z:", min(z), max(z)
- print xypoints[-1]
-
- return xmin, xmax, ymin, ymax, motions
- ############### cam.py ####################
- def coord(gstr,digits,fraction):
- '''
- Parse Gerber coordinates
- '''
- global gerbx, gerby
- xindex = gstr.find("X")
- yindex = gstr.find("Y")
- index = gstr.find("D")
- if (xindex == -1):
- x = gerbx
- y = int(gstr[(yindex+1):index])*(10**(-fraction))
- elif (yindex == -1):
- y = gerby
- x = int(gstr[(xindex+1):index])*(10**(-fraction))
- else:
- x = int(gstr[(xindex+1):yindex])*(10**(-fraction))
- y = int(gstr[(yindex+1):index])*(10**(-fraction))
- gerbx = x
- gerby = y
- return [x,y]
- ################ end of cam.py #############
|