| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293 |
- import cairo
- #from string import *
- #from math import *
- #from random import *
- #from struct import *
- #import os
- #import sys
- from numpy import arctan2, Inf, array
- # 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])
- 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 = []
-
- 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):
- 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
-
- def create_geometry(self):
- if len(self.buffered_paths) == 0:
- self.buffer_paths()
- self.fix_regions()
- flash_polys = []
- 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)
- 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)
- flash_polys.append(rectangle)
- continue
- print "WARNING: Aperture type %s not implemented"%(aperture['type'])
- #TODO: Add support for type='O'
- self.solid_geometry = cascaded_union(
- self.buffered_paths +
- [poly['polygon'] for poly in self.regions] +
- flash_polys)
- 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 = ""
-
- def generate_from_geometry(self, geometry, append=True):
- 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 += "M05\n" # Spindle stop
-
- def create_gcode_geometry(self):
- geometry = []
-
- 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: No 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]
-
- return geometry
-
-
- 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)
- 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:
- '''
- 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
- 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):
- '''
- 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]
- 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 #############
|