def make(self,n = 2, size = 100, depth = 0): self.n = n self.size = size self.depth = depth blocks = [] block = Block(self.name) xi,yi = zip(*(self.hilbert(0.0,0.0,size,0.0,0.0,size,n))) block.append(CNC.zsafe()) block.append(CNC.grapid(xi[0],yi[0])) currDepth = 0. stepz = CNC.vars['stepz'] if stepz==0 : stepz=0.001 #avoid infinite while loop while True: currDepth -= stepz if currDepth < self.depth : currDepth = self.depth block.append(CNC.zenter(currDepth)) block.append(CNC.gcode(1, [("f",CNC.vars["cutfeed"])])) for x,y in zip(xi,yi): block.append(CNC.gline(x,y)) if currDepth <= self.depth : break block.append(CNC.zsafe()) blocks.append(block) return blocks
def make(self, Nlines, LineLen, StartEndLen, Step, CornerRes, Depth): blocks = [] block = Block(self.name) points = self.zigzag(Nlines, LineLen, StartEndLen, Step, CornerRes) block.append(CNC.zsafe()) block.append(CNC.grapid(points[0][0],points[0][1])) currDepth = 0. stepz = CNC.vars['stepz'] if stepz==0 : stepz=0.001 #avoid infinite while loop while True: currDepth -= stepz if currDepth < Depth : currDepth = Depth block.append(CNC.zenter(currDepth)) block.append(CNC.gcode(1, [("f",CNC.vars["cutfeed"])])) for (x,y) in points: block.append(CNC.gline(x,y)) if currDepth <= Depth : break block.append(CNC.zsafe()) blocks.append(block) return blocks
def make(self, n=2, size=100, depth=0): self.n = n self.size = size self.depth = depth blocks = [] block = Block(self.name) xi, yi = zip(*(self.hilbert(0.0, 0.0, size, 0.0, 0.0, size, n))) block.append(CNC.zsafe()) block.append(CNC.grapid(xi[0], yi[0])) currDepth = 0. stepz = CNC.vars['stepz'] if stepz == 0: stepz = 0.001 #avoid infinite while loop while True: currDepth -= stepz if currDepth < self.depth: currDepth = self.depth block.append(CNC.zenter(currDepth)) block.append(CNC.gcode(1, [("f", CNC.vars["cutfeed"])])) for x, y in zip(xi, yi): block.append(CNC.gline(x, y)) if currDepth <= self.depth: break block.append(CNC.zsafe()) blocks.append(block) return blocks
def create_block(self, holes, name): targetDepth = self.fromMm("TargetDepth") peck = self.fromMm("Peck") dwell = self["Dwell"] block = Block(name) holesCount = 0 if self.useCustom: block.append("M3 S0") else: block.append(CNC.zsafe()) for bid in holes: for xH, yH, zH in bid: holesCount += 1 if self.useCustom: block.append( CNC.grapid(x=xH, y=yH) + CNC.fmt(' F', self.rFeed)) else: #block.append(CNC.zsafe()) # Moved up block.append(CNC.grapid(xH, yH)) if peck != 0: z = 0 while z > targetDepth: z = max(z - peck, targetDepth) if self.useCustom: block.append( "( --- WARNING! Peck is not setup for laser mode --- )" ) break else: block.append(CNC.zenter(zH + z)) block.append(CNC.zsafe()) if self.useCustom: block.append("G1 S%s" % (self.spinMax)) block.append(CNC.gline(x=xH, y=yH)) else: block.append(CNC.zenter(zH + targetDepth)) # Dwell time only on last pass if dwell != 0: block.append(CNC.gcode(4, [("P", dwell)])) if self.useCustom: block.append("G1 S%s" % (self.spinMin)) else: block.append(CNC.zsafe()) # Gcode Zsafe on finish if self.useCustom: block.append("M5") else: block.append(CNC.zsafe()) return (block, holesCount)
def _rectangle(self, block, x0, y0, dx, dy, nx, ny, ex=0., ey=0.): block.append("( Location: %g,%g )" % (x0, y0)) block.append("( Dimensions: %g,%g )" % (dx, dy)) block.append("( Teeth: %d,%d )" % (nx, ny)) block.append("( Tool diameter: %g )" % (self.tool)) # Start with full length sx = dx / abs(nx) sy = dy / abs(ny) # Bottom pos = Vector(x0, y0, self.surface) pos -= self.r * Vector.Y # r*V block.append(CNC.gcode(0, zip("XY", pos[:2]))) z = self.surface #for z in frange(self.surface-self.stepz, self.surface-self.thick, -self.stepz): last = False while True: if self.cut: z -= self.stepz if z <= self.surface - self.thick: z = self.surface - self.thick last = True else: last = True pos[2] = z # Penetrate block.append(CNC.zenter(pos[2])) # Bottom pos = self.zigZagLine(block, pos, sx, self.thick, Vector.X, Vector.Y, nx, ex) block.append("") # Right pos = self.zigZagLine(block, pos, sy, self.thick, Vector.Y, -Vector.X, ny, ey) block.append("") # Top pos = self.zigZagLine(block, pos, sx, self.thick, -Vector.X, -Vector.Y, nx, ex) block.append("") # Right pos = self.zigZagLine(block, pos, sy, self.thick, -Vector.Y, Vector.X, ny, ey) block.append("") if last: break # Bring to safe height block.append(CNC.zsafe())
def writeGlyphContour(self,block,font,contours,fontSize,depth,xO, yO): width = font.header.x_max - font.header.x_min height = font.header.y_max - font.header.y_min scale = fontSize / font.header.units_per_em xO = xO * fontSize yO = yO * fontSize for cont in contours: block.append(CNC.zsafe()) block.append(CNC.grapid(xO + cont[0].x * scale , yO + cont[0].y * scale)) block.append(CNC.zenter(depth)) block.append(CNC.gcode(1, [("f",CNC.vars["cutfeed"])])) for p in cont: block.append(CNC.gline(xO + p.x * scale, yO + p.y * scale))
def _rectangle(self, block, x0, y0, dx, dy, nx, ny, ex=0., ey=0.): block.append("( Location: %g,%g )"%(x0,y0)) block.append("( Dimensions: %g,%g )"%(dx,dy)) block.append("( Teeth: %d,%d )"%(nx,ny)) block.append("( Tool diameter: %g )"%(self.tool)) # Start with full length sx = dx / abs(nx) sy = dy / abs(ny) # Bottom pos = Vector(x0, y0, self.surface) pos -= self.r*Vector.Y # r*V block.append(CNC.gcode(0, zip("XY",pos[:2]))) z = self.surface #for z in frange(self.surface-self.stepz, self.surface-self.thick, -self.stepz): last = False while True: if self.cut: z -= self.stepz if z <= self.surface - self.thick: z = self.surface - self.thick last = True else: last = True pos[2] = z # Penetrate block.append(CNC.zenter(pos[2])) # Bottom pos = self.zigZagLine(block, pos, sx, self.thick, Vector.X, Vector.Y, nx, ex) block.append("") # Right pos = self.zigZagLine(block, pos, sy, self.thick, Vector.Y, -Vector.X, ny, ey) block.append("") # Top pos = self.zigZagLine(block, pos, sx, self.thick, -Vector.X, -Vector.Y, nx, ex) block.append("") # Right pos = self.zigZagLine(block, pos, sy, self.thick, -Vector.Y, Vector.X, ny, ey) block.append("") if last: break # Bring to safe height block.append(CNC.zsafe())
def make(self, RExt=50.0, RInt=33.0, ROff=13.0, Depth=0): self.RExt = RExt self.RInt = RInt self.ROff = ROff if RExt > RInt: self.Spins = self.lcm(RExt, RInt) / max(RExt, RInt) else: self.Spins = self.lcm(RExt, RInt) / min(RExt, RInt) self.Depth = Depth self.PI = math.pi self.theta = 0.0 blocks = [] block = Block(self.name) block.append("(External Radius = %g)" % (self.RExt)) block.append("(Internal Radius = %g)" % (self.RInt)) block.append("(Offset Radius = %g)" % (self.ROff)) xi, yi = zip(*(self.calc_dots())) block.append(CNC.zsafe()) block.append(CNC.grapid(xi[0], yi[0])) currDepth = 0.0 stepz = CNC.vars["stepz"] if stepz == 0: stepz = 0.001 # avoid infinite while loop while True: currDepth -= stepz if currDepth < self.Depth: currDepth = self.Depth block.append(CNC.zenter(currDepth)) block.append(CNC.gcode(1, [("f", CNC.vars["cutfeed"])])) for x, y in zip(xi, yi): block.append(CNC.gline(x, y)) block.append(CNC.gline(xi[0], yi[0])) if currDepth <= self.Depth: break block.append(CNC.zsafe()) blocks.append(block) return blocks
def make(self, RExt=50., RInt=33., ROff=13., Depth=0): self.RExt = RExt self.RInt = RInt self.ROff = ROff if RExt > RInt: self.Spins = self.lcm(RExt, RInt) / max(RExt, RInt) else: self.Spins = self.lcm(RExt, RInt) / min(RExt, RInt) self.Depth = Depth self.PI = math.pi self.theta = 0.0 blocks = [] block = Block(self.name) block.append("(External Radius = %g)" % (self.RExt)) block.append("(Internal Radius = %g)" % (self.RInt)) block.append("(Offset Radius = %g)" % (self.ROff)) xi, yi = zip(*(self.calc_dots())) block.append(CNC.zsafe()) block.append(CNC.grapid(xi[0], yi[0])) currDepth = 0. stepz = CNC.vars['stepz'] if stepz == 0: stepz = 0.001 #avoid infinite while loop while True: currDepth -= stepz if currDepth < self.Depth: currDepth = self.Depth block.append(CNC.zenter(currDepth)) block.append(CNC.gcode(1, [("f", CNC.vars["cutfeed"])])) for x, y in zip(xi, yi): block.append(CNC.gline(x, y)) block.append(CNC.gline(xi[0], yi[0])) if currDepth <= self.Depth: break block.append(CNC.zsafe()) blocks.append(block) return blocks
def create_block(self, holes, name): targetDepth = self.fromMm("TargetDepth") peck = self.fromMm("Peck") dwell = self["Dwell"] block = Block(name) holesCount = 0 for bid in holes: for xH, yH, zH in bid: holesCount += 1 block.append(CNC.zsafe()) block.append(CNC.grapid(xH, yH)) if (peck != 0): z = 0 while z > targetDepth: z = max(z - peck, targetDepth) block.append(CNC.zenter(zH + z)) block.append(CNC.zsafe()) block.append(CNC.zenter(zH + targetDepth)) #dwell time only on last pass if dwell != 0: block.append(CNC.gcode(4, [("P", dwell)])) #Gcode Zsafe on finish block.append(CNC.zsafe()) return (block, holesCount)
def create_block(self, holes, name): targetDepth = self.fromMm("TargetDepth") peck = self.fromMm("Peck") dwell = self["Dwell"] block = Block(name) holesCount = 0 for bid in holes: for xH,yH,zH in bid: holesCount += 1 block.append(CNC.zsafe()) block.append(CNC.grapid(xH,yH)) if (peck != 0) : z = 0 while z > targetDepth: z = max(z-peck, targetDepth) block.append(CNC.zenter(zH + z)) block.append(CNC.zsafe()) block.append(CNC.zenter(zH + targetDepth)) #dwell time only on last pass if dwell != 0: block.append(CNC.gcode(4, [("P",dwell)])) #Gcode Zsafe on finish block.append(CNC.zsafe()) return (block,holesCount)
def execute(self, app): try: import midiparser as midiparser except: app.setStatus(_("Error: This plugin requires midiparser.py")) return n = self["name"] if not n or n=="default": n="Midi2CNC" fileName = self["File"] x=0.0 y=0.0 z=0.0 x_dir=1.0; y_dir=1.0; z_dir=1.0; # List of MIDI channels (instruments) to import. # Channel 10 is percussion, so better to omit it channels = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15] axes = self["AxisUsed"] active_axes = len(axes) transpose = (0,0,0) ppu = [ 200, 200, 200 ] ppu[0] = self["ppu_X"] ppu[1] = self["ppu_X"] ppu[2] = self["ppu_X"] safemin = [ 0, 0, 0 ] safemax = [ 100, 100, 50 ] safemax[0] = self["max_X"] safemax[1] = self["max_Y"] safemax[2] = self["max_Z"] try: midi = midiparser.File(fileName) except: app.setStatus(_("Error: Sorry can't parse the Midi file.")) return noteEventList=[] all_channels=set() for track in midi.tracks: #channels=set() for event in track.events: if event.type == midiparser.meta.SetTempo: tempo=event.detail.tempo # filter undesired instruments if ((event.type == midiparser.voice.NoteOn) and (event.channel in channels)): if event.channel not in channels: channels.add(event.channel) # NB: looks like some use "note on (vel 0)" as equivalent to note off, so check for vel=0 here and treat it as a note-off. if event.detail.velocity > 0: noteEventList.append([event.absolute, 1, event.detail.note_no, event.detail.velocity]) else: noteEventList.append([event.absolute, 0, event.detail.note_no, event.detail.velocity]) if (event.type == midiparser.voice.NoteOff) and (event.channel in channels): if event.channel not in channels: channels.add(event.channel) noteEventList.append([event.absolute, 0, event.detail.note_no, event.detail.velocity]) # Finished with this track if len(channels) > 0: msg=', ' . join(['%2d' % ch for ch in sorted(channels)]) #print 'Processed track %d, containing channels numbered: [%s ]' % (track.number, msg) all_channels = all_channels.union(channels) # List all channels encountered if len(all_channels) > 0: msg=', ' . join(['%2d' % ch for ch in sorted(all_channels)]) #print 'The file as a whole contains channels numbered: [%s ]' % msg # We now have entire file's notes with abs time from all channels # We don't care which channel/voice is which, but we do care about having all the notes in order # so sort event list by abstime to dechannelify noteEventList.sort() # print noteEventList # print len(noteEventList) last_time=-0 active_notes={} # make this a dict so we can add and remove notes by name # Start the output #Init blocks blocks = [] block = Block(self.name) block.append("(Midi2CNC)") block.append("(Midi:%s)" % fileName) block.append(CNC.zsafe()) block.append(CNC.grapid(0,0)) block.append(CNC.zenter(0)) for note in noteEventList: # note[timestamp, note off/note on, note_no, velocity] if last_time < note[0]: freq_xyz=[0,0,0] feed_xyz=[0,0,0] distance_xyz=[0,0,0] duration=0 # "i" ranges from 0 to "the number of active notes *or* the number of active axes, # whichever is LOWER". Note that the range operator stops # short of the maximum, so this means 0 to 2 at most for a 3-axis machine. # E.g. only look for the first few active notes to play despite what # is going on in the actual score. for i in range(0, min(len(active_notes.values()), active_axes)): # Which axis are should we be writing to? # j = self.axes_dict.get(axes)[i] # Debug # print"Axes %s: item %d is %d" % (axes_dict.get(args.axes), i, j) # Sound higher pitched notes first by sorting by pitch then indexing by axis # nownote=sorted(active_notes.values(), reverse=True)[i] # MIDI note 69 = A4(440Hz) # 2 to the power (69-69) / 12 * 440 = A4 440Hz # 2 to the power (64-69) / 12 * 440 = E4 329.627Hz # freq_xyz[j] = pow(2.0, (nownote-69 + transpose[j])/12.0)*440.0 # Here is where we need smart per-axis feed conversions # to enable use of X/Y *and* Z on a Makerbot # # feed_xyz[0] = X; feed_xyz[1] = Y; feed_xyz[2] = Z; # # Feed rate is expressed in mm / minutes so 60 times # scaling factor is required. feed_xyz[j] = ( freq_xyz[j] * 60.0 ) / ppu[j] # Get the duration in seconds from the MIDI values in divisions, at the given tempo duration = ( ( ( note[0] - last_time ) + 0.0 ) / ( midi.division + 0.0 ) * ( tempo / 1000000.0 ) ) # Get the actual relative distance travelled per axis in mm distance_xyz[j] = ( feed_xyz[j] * duration ) / 60.0 # Now that axes can be addressed in any order, need to make sure # that all of them are silent before declaring a rest is due. if distance_xyz[0] + distance_xyz[1] + distance_xyz[2] > 0: # At least one axis is playing, so process the note into # movements combined_feedrate = math.sqrt(feed_xyz[0]**2 + feed_xyz[1]**2 + feed_xyz[2]**2) # Turn around BEFORE crossing the limits of the # safe working envelope if self.reached_limit( x, distance_xyz[0], x_dir, safemin[0], safemax[0] ): x_dir = x_dir * -1 x = (x + (distance_xyz[0] * x_dir)) if self.reached_limit( y, distance_xyz[1], y_dir, safemin[1], safemax[1] ): y_dir = y_dir * -1 y = (y + (distance_xyz[1] * y_dir)) if self.reached_limit( z, distance_xyz[2], z_dir, safemin[2], safemax[2] ): z_dir = z_dir * -1 z = (z + (distance_xyz[2] * z_dir)) v = (x,y,z) block.append(CNC.glinev(1,v,combined_feedrate)) else: # Handle 'rests' in addition to notes. duration = (((note[0]-last_time)+0.0)/(midi.division+0.0)) * (tempo/1000000.0) block.append(CNC.gcode(4, [("P",duration)])) # finally, set this absolute time as the new starting time last_time = note[0] if note[1]==1: # Note on if active_notes.has_key(note[2]): pass else: # key and value are the same, but we don't really care. active_notes[note[2]]=note[2] elif note[1]==0: # Note off if(active_notes.has_key(note[2])): active_notes.pop(note[2]) blocks.append(block) active = app.activeBlock() if active==0: active=1 app.gcode.insBlocks(active, blocks, "Midi2CNC") app.refresh() app.setStatus(_("Generated Midi2CNC, ready to play?"))
def make(self,app, XStart=0.0, YStart=0.0, ZStart=30., AlignAxis="Y", \ RotAxis="A", StockLeng=20, ReduceDepth=-1, PassDepth=1, \ Stepover=1, ZApproach=35, SpiralType="Spiral", CutBoth="True", LiftPass="******"): #GCode Blocks blocks = [] # Load tool and material settings toolDiam = CNC.vars['diameter'] toolRadius = toolDiam / 2. #Calc tool diameter with Maximum Step Over allowed StepOverInUnitMax = toolDiam * CNC.vars['stepover'] / 100.0 #Check parameters if RotAxis == "": app.setStatus(_("Spiral abort: Rotary Axis is undefined")) return if SpiralType == "": app.setStatus(_("Spiral abort: Spiral Type is undefined")) return if ZApproach <= ZStart : app.setStatus(_("Spiral abort: Approach height must be greater than Z Start")) return if ReduceDepth > 0 : app.setStatus(_("Spiral abort: Depth Reduction must be negative")) return if Stepover > StepOverInUnitMax and SpiralType == "Spiral": #if Type is Lines then stepover is degrees not mm app.setStatus(_("Spiral abort: Step Over exceeds tool limits")) return elif Stepover > StepOverInUnitMax and SpiralType == "Lines": # This could cause a tool crash, but could also be used to make faceted shapes. dr=tkMessageBox.askyesno("Crash Risk","WARNING: Using a larger stepover value than tool's maximum with lines operation may result in a tool crash. Do you want to continue?") sys.stdout.write("%s"%(dr)) if dr == True or dr == "yes" : app.setStatus(_("Risk Accepted")) #Using positive logic, if python returns ANYTHING other than True/yes this will not make g-code. Incase Python uses No instead of False else: return if StockLeng <= 0 : app.setStatus(_("Spiral abort: Stock Length to cut must be positive")) return #Add Region disabled to show worked area block = Block(self.name + " Outline") block.enable = False block.append(CNC.grapid(CNC.vars["wx"],CNC.vars["wy"],ZApproach)) ## Cannot trust Safe-Z with 4th axis!! if AlignAxis == "X": outlineWidth = StockLeng else: outlineWidth = 0 if AlignAxis == "Y": outlineHeight = StockLeng else: outlineHeight = 0 xR,yR = self.RectPath(XStart,YStart,outlineWidth,outlineHeight) for x,y in zip(xR,yR): block.append(CNC.gline(x,y)) blocks.append(block) if StockLeng < toolDiam : app.setStatus(_("Spiral abort: Stock Length is too small for this End Mill.")) return #Prepare points for pocketing xP=[] yP=[] rP=[] zP=[] gP=[] #--------------------------------------------------------------------- #Line approach if SpiralType == "Lines": #Calc number of indexes IndexNum = math.ceil(360/Stepover) # Using the step over as Degrees #Calc number of pass VerticalCount = math.ceil(abs(ReduceDepth) / PassDepth) #Calc even depths of cut EvenCutDepths = ReduceDepth / VerticalCount currentR = 0 currentZ = ZStart - EvenCutDepths direction = 1 if AlignAxis == "X" : currentX = XStart + toolRadius currentY = YStart elif AlignAxis == "Y": currentX = XStart currentY = YStart + toolRadius else: app.setStatus(_("Spiral abort: Rotary Axis Not Assigned.")) return while (currentZ >= (ZStart + ReduceDepth)): #sys.stdout.write("~~~~~%s,%s,%s,%s,%s!"%(currentZ,ZStart,ReduceDepth,EvenCutDepths,VerticalCount)) while (currentR < 360): #sys.stdout.write("~~~~~%s,%s,%s,%s,%s!"%(currentR,Stepover,currentX,currentY,VerticalCount)) #Plunge in gP.append(1) rP.append(currentR) zP.append(currentZ) xP.append(currentX) yP.append(currentY) if direction == 1: if AlignAxis == "X" : currentX = StockLeng - toolRadius currentY = YStart elif AlignAxis == "Y": currentX = XStart currentY = StockLeng - toolRadius else: app.setStatus(_("Spiral abort: Rotary Axis Not Assigned.")) return if CutBoth == "True" : direction = -1 else : if AlignAxis == "X" : currentX = XStart + toolRadius currentY = YStart elif AlignAxis == "Y": currentX = XStart currentY = YStart + toolRadius else: app.setStatus(_("Spiral abort: Rotary Axis Not Assigned.")) return direction = 1 gP.append(1) zP.append(currentZ) xP.append(currentX) yP.append(currentY) rP.append(currentR) # Lift before rotating if required, useful to make non-round shape if CutBoth == "False" : # Return to start #Lift Before return gP.append(0) rP.append(currentR) zP.append(ZApproach) xP.append(currentX) yP.append(currentY) #Return to start if AlignAxis == "X" : currentX = XStart + toolRadius currentY = YStart elif AlignAxis == "Y": currentX = XStart currentY = YStart + toolRadius else: app.setStatus(_("Spiral abort: Rotary Axis Not Assigned.")) return gP.append(0) xP.append(currentX) yP.append(currentY) rP.append(currentR) zP.append(ZApproach) #Rotate currentR += Stepover gP.append(0) xP.append(currentX) yP.append(currentY) rP.append(currentR) zP.append(ZApproach) elif LiftPass == "True" and CutBoth == "True" : gP.append(0) rP.append(currentR) zP.append(ZApproach) xP.append(currentX) yP.append(currentY) currentR += Stepover gP.append(0) xP.append(currentX) yP.append(currentY) rP.append(currentR) zP.append(ZApproach) elif LiftPass == "False" and CutBoth == "True" : currentR += Stepover gP.append(0) xP.append(currentX) yP.append(currentY) rP.append(currentR) zP.append(ZApproach) currentR=0 gP.append(0) xP.append(currentX) yP.append(currentY) rP.append(currentR) zP.append(ZApproach) #Step Down currentZ += EvenCutDepths #--------------------------------------------------------------------- #Spiral approach if SpiralType == "Spiral": #Calc number of pass StepsPerRot = math.ceil(StockLeng/Stepover) TotalRot = 360 * StepsPerRot #Calc steps in depth VerticalCount = math.ceil(abs(ReduceDepth) / PassDepth) #Calc even depths of cut EvenCutDepths = ReduceDepth / VerticalCount direction = 1 currentZ = ZStart - EvenCutDepths if AlignAxis == "X" : currentX = XStart + toolRadius currentY = YStart elif AlignAxis == "Y": currentX = XStart currentY = YStart + toolRadius else: app.setStatus(_("Spiral abort: Rotary Axis Not Assigned.")) return currentR = 0 while (currentZ >= (ZStart + ReduceDepth)): # Plunge to depth currentR += 90 # Ramp the Plunge gP.append(1) rP.append(currentR) zP.append(currentZ) xP.append(currentX) yP.append(currentY) # One Full Rotation for a clean shoulder currentR += 360 gP.append(1) rP.append(currentR) zP.append(currentZ) xP.append(currentX) yP.append(currentY) if AlignAxis == "X" : if direction == 1: currentX = StockLeng - toolRadius else: currentX = XStart + toolRadius currentY = YStart elif AlignAxis == "Y": currentX = XStart if direction == 1: currentY = StockLeng - toolRadius else: currentY = YStart + toolRadius else: app.setStatus(_("Spiral abort: Rotary Axis Not Assigned.")) return currentR += TotalRot gP.append(1) rP.append(currentR) zP.append(currentZ) xP.append(currentX) yP.append(currentY) # One Full Rotation for a clean shoulder currentR += 360 gP.append(1) rP.append(currentR) zP.append(currentZ) xP.append(currentX) yP.append(currentY) if CutBoth == "True" : direction *= - 1 else: #Retract gP.append(0) rP.append(currentR) zP.append(ZApproach) xP.append(currentX) yP.append(currentY) # Return and Rewind gP.append(0) rP.append(currentR) zP.append(ZApproach) if AlignAxis == "X" : currentX = XStart + toolRadius currentY = YStart elif AlignAxis == "Y": currentX = XStart currentY = YStart + toolRadius else: app.setStatus(_("Spiral abort: Rotary Axis Not Assigned.")) return xP.append(currentX) yP.append(currentY) currentZ += EvenCutDepths #Start G-Code Processes #Blocks for pocketing block = Block(self.name) block.append("(Reduce Rotary by Y=%g)"%(ReduceDepth)) block.append("(Approach: %s )" % (SpiralType)) #Move safe to first point block.append(CNC.grapid(CNC.vars["mx"],CNC.vars["my"],ZApproach)) ## Cannot trust Safe-Z with 4th axis!! if AlignAxis == "X" : block.append(CNC.grapid(XStart + toolRadius,YStart)) elif AlignAxis == "Y": block.append(CNC.grapid(XStart,YStart + toolRadius)) else: app.setStatus(_("Spiral abort: Rotary Axis Not Assigned.")) return block.append(CNC.zenter(ZApproach)) block.append(CNC.gcode(1, [("f",CNC.vars["cutfeed"])])) for g,x,y,z,r in zip(gP, xP,yP,zP, rP): if RotAxis == "A" : if g==0: block.append(CNC.grapidABC(x,y,z,r,CNC.vars["wb"],CNC.vars["wc"])) #sys.stdout.write("%s,%s,%s,%s,%s"%(g,x,y,z,r)) else: block.append(CNC.glineABC(x,y,z,r,CNC.vars["wb"],CNC.vars["wc"])) #sys.stdout.write("%s,%s,%s,%s,%s"%(g,x,y,z,r)) elif RotAxis == "B" : if g==0: block.append(CNC.grapidABC(x,y,z,CNC.vars["wa"],r,CNC.vars["wc"])) else: block.append(CNC.glineABC(x,y,z,CNC.vars["wa"],r,CNC.vars["wc"])) elif RotAxis == "C" : if g==0: block.append(CNC.grapidABC(x,y,z,CNC.vars["wa"],CNC.vars["wb"],r)) else: block.append(CNC.glineABC(x,y,z,CNC.vars["wa"],CNC.vars["wb"],r)) block.append(CNC.grapid(CNC.vars["wx"],CNC.vars["wy"],ZApproach)) ## Cannot trust Safe-Z with 4th axis!! if AlignAxis == "X" : block.append(CNC.grapid(XStart + toolRadius,YStart)) elif AlignAxis == "Y": block.append(CNC.grapid(XStart,YStart + toolRadius)) else: app.setStatus(_("Spiral abort: Rotary Axis Not Assigned.")) return block.append(CNC.zexit(ZApproach)) blocks.append(block) tkMessageBox.showinfo("Crash Risk","WARNING: Check CAM file Header for Z move. If it exists, remove it to prevent tool crash.") return blocks
def execute(self, app): #Get inputs holesDistance = self.fromMm("HolesDistance") targetDepth = self.fromMm("TargetDepth") peck = self.fromMm("Peck") dwell = self["Dwell"] zSafe = CNC.vars["safe"] #Check inputs if holesDistance <= 0: app.setStatus(_("Driller abort: Distance must be > 0")) return if peck < 0: app.setStatus(_("Driller abort: Peck must be >= 0")) return if dwell < 0: app.setStatus( _("Driller abort: Dwell time >= 0, here time runs only forward!" )) return # Get selected blocks from editor selBlocks = app.editor.getSelectedBlocks() if not selBlocks: app.editor.selectAll() selBlocks = app.editor.getSelectedBlocks() if not selBlocks: app.setStatus(_("Driller abort: Please select some path")) return #Get all segments from gcode allSegments = self.extractAllSegments(app, selBlocks) #Create holes locations allHoles = [] for bidSegment in allSegments: if len(bidSegment) == 0: continue #Summ all path length fullPathLength = 0.0 for s in bidSegment: fullPathLength += s[3] #Calc rest holes = fullPathLength // holesDistance rest = fullPathLength - (holesDistance * (holes)) #Travel along the path elapsedLength = rest / 2.0 #equaly distribute rest, as option??? bidHoles = [] while elapsedLength <= fullPathLength: #Search best segment to apply line interpolation bestSegment = bidSegment[0] segmentsSum = 0.0 perc = 0.0 for s in bidSegment: bestSegment = s segmentLength = bestSegment[3] perc = (elapsedLength - segmentsSum) / segmentLength segmentsSum += segmentLength if segmentsSum > elapsedLength: break #Fist point x1 = bestSegment[0][0] y1 = bestSegment[0][1] z1 = bestSegment[0][2] #Last point x2 = bestSegment[1][0] y2 = bestSegment[1][1] z2 = bestSegment[1][2] #Check if segment is not excluded if not bestSegment[2]: newHolePoint = (x1 + perc * (x2 - x1), y1 + perc * (y2 - y1), z1 + perc * (z2 - z1)) bidHoles.append(newHolePoint) #Go to next hole elapsedLength += holesDistance #Add bidHoles to allHoles allHoles.append(bidHoles) #Write gcommands from allSegments to the drill block n = self["name"] if not n or n == "default": n = "Driller" blocks = [] block = Block(self.name) holesCount = 0 for bid in allHoles: for xH, yH, zH in bid: holesCount += 1 block.append(CNC.grapid(None, None, zH + zSafe)) block.append(CNC.grapid(xH, yH)) if (peck != 0): z = 0 while z > targetDepth: z = max(z - peck, targetDepth) block.append(CNC.zenter(zH + z)) block.append(CNC.grapid(None, None, zH + zSafe)) block.append(CNC.zenter(zH + targetDepth)) #dwell time only on last pass if dwell != 0: block.append(CNC.gcode(4, [("P", dwell)])) #Gcode Zsafe on finish block.append(CNC.zsafe()) blocks.append(block) #Insert created block active = app.activeBlock() if active == 0: active = 1 app.gcode.insBlocks(active, blocks, "Driller") app.refresh() app.setStatus(_("Generated Driller: %d holes") % holesCount)
def execute(self, app): name = self['name'] if not name or name == 'default': name = 'Function' # Initialize blocks that will contain our gCode blocks = [] block = Block(name) #Variable definitions formula = self['form'] res = self['res'] # X resolution ran = [self['ranX'], self['ranY']] # Range of X,Y, from -10, to 10 range is 20 cent = [self['centX'], self['centY']] # Coordinates X,Y of the center from bottom left of the coordinate system dim = [self['dimX'], self['dimY']] # Real dimensions in gcode units spacX = self['spacX'] # Spacing of X axis lines spacY = self['spacY'] # Spacing of Y axis lines lin = self['lin'] # Small value - length of a line in gcode units draw = self['draw'] # Draw the coordinate system block.append("(Generated with a script by kswiorek)\n") block.append("(Equation: " + formula +")\n") block.append("(Resolution: " + str(res) +")\n") block.append("(Range: " + str(ran) +")\n") block.append("(Center: " + str(cent) +")\n") block.append("(Dimensions: " + str(dim) +")\n") block.append("(SpacingXY: " + str(spacX) +", " + str(spacY) +")\n") def mapc(var, axis): #Map coordinate systems return (var * (dim[axis]/ran[axis])) #Define coordinate system mins and maxes minX = -cent[0] maxX = ran[0]-cent[0] minY = -cent[1] maxY = ran[1]-cent[1] #Define domain and codomain X = [] Y = [] e_old = "" #Store old exception to comapre #Calculate values for arguments with a resolution for i in range(0, int(ran[0]/res+1)): #Complaints about values beeing floats x = i*res + minX #Iterate x X.append(x) try: Y.append(eval(formula)) except Exception as exc: #Append None, not to loose sync with X Y.append(None) e = str(exc) if e != e_old: #If there is a different exception - display it print("Warning: " + str(e)) app.setStatus(_("Warning: " + str(e))) e_old = e raised = True # Z axis is raised at start #Clip values out of bounds, replace with None, not to loose sync with X for i, item in enumerate(Y): y = Y[i] if not y is None and (y < minY or y > maxY): Y[i] = None #Y without "None", min() and max() can't compare them Ynn = [] #Y no Nones for i, item in enumerate(Y): if not Y[i] is None: Ynn.append(Y[i]) block.append(CNC.gcode(1, [("f",CNC.vars["cutfeed"])])) #Set feedrate if draw: #If the user selected to draw the coordinate system #X axis block.append(CNC.grapid(z=3)) block.append(CNC.grapid(0, mapc(cent[1], 1))) #1st point of X axis line block.append(CNC.grapid(z=0)) block.append(CNC.gline(dim[0] + lin*1.2, mapc(cent[1], 1))) #End of X axis line + a bit more for the arrow block.append(CNC.gline(dim[0] - lin/2, mapc(cent[1], 1) - lin / 2)) #bottom part of the arrow block.append(CNC.grapid(z=3)) block.append(CNC.grapid(dim[0] + lin*1.2, mapc(cent[1], 1), 0)) #End of X axis line block.append(CNC.grapid(z=0)) block.append(CNC.gline(dim[0] - lin/2, mapc(cent[1], 1) + lin / 2)) #top part of the arrow block.append(CNC.grapid(z=3)) #Y axis, just inverted x with y block.append(CNC.grapid(z=3)) block.append(CNC.grapid(mapc(cent[0], 0), 0)) #1st point of Y axis line block.append(CNC.grapid(z=0)) block.append(CNC.gline(mapc(cent[0], 0), dim[1] + lin*1.2)) #End of Y axis line + a bit more for the arrow block.append(CNC.gline(mapc(cent[0], 0) - lin / 2, dim[1] - lin/2)) #left part of the arrow block.append(CNC.grapid(z=3)) block.append(CNC.grapid(mapc(cent[0], 0), dim[1] + lin*1.2)) #End of Y axis line block.append(CNC.grapid(z=0)) block.append(CNC.gline(mapc(cent[0], 0) + lin / 2, dim[1] - lin/2)) #right part of the arrow block.append(CNC.grapid(z=3)) #X axis number lines i = 0 while i < ran[0] - cent[0]: #While i is on the left of the arrow i +=spacX #Add line spacing #Draw lines right of the center block.append(CNC.grapid(mapc(i+cent[0],0), mapc(cent[1], 1) + lin/2)) block.append(CNC.grapid(z=0)) block.append(CNC.gline(mapc(i+cent[0],0), mapc(cent[1], 1) - lin/2)) block.append(CNC.grapid(z=3)) i = 0 while i > -cent[0]: #While i is lower than center coordinate, inverted for easier math i -=spacX #Add line spacing #Draw lines left of the center block.append(CNC.grapid(mapc(i+cent[0],0), mapc(cent[1], 1) + lin/2)) block.append(CNC.grapid(z=0)) block.append(CNC.gline(mapc(i+cent[0],0), mapc(cent[1], 1) - lin/2)) block.append(CNC.grapid(z=3)) #Y axis number lines i = 0 while i < ran[1] - cent[1]: #While i is between the center and the arrow i +=spacX #Add line spacing #Draw lines top of the center (everything just inverted) block.append(CNC.grapid(mapc(cent[0], 0) + lin/2, mapc(i+cent[1],1))) block.append(CNC.grapid(z=0)) block.append(CNC.gline(mapc(cent[0], 0) - lin/2, mapc(i+cent[1],1))) block.append(CNC.grapid(z=3)) i = 0 while i > -1*cent[1]: i -=spacX #Add line spacing #Draw lines bottom of the center block.append(CNC.grapid(mapc(cent[0], 0) + lin/2, mapc(i+cent[1],1))) block.append(CNC.grapid(z=0)) block.append(CNC.gline(mapc(cent[0], 0) - lin/2, mapc(i+cent[1],1))) block.append(CNC.grapid(z=3)) raised = True #Z was raised #Draw graph for i, item in enumerate(Y): if not Y[i] is None: x = mapc(X[i]+cent[0], 0) #Take an argument y = mapc(Y[i]+cent[1], 1) #Take a value else: y = Y[i] #only for tne None checks next if y is None and not raised: #If a None "period" just started raise Z raised = True block.append(CNC.grapid(z=3)) elif not y is None and raised: #If Z was raised and the None "period" ended move to new coordinates block.append(CNC.grapid(round(x, 2),round(y, 2))) block.append(CNC.grapid(z=0)) #Lower Z raised = False elif not y is None and not raised: #Nothing to do with Nones? Just draw block.append(CNC.gline(round(x, 2),round(y, 2))) block.append(CNC.grapid(z=3)) #Raise on the end blocks.append(block) active = app.activeBlock() app.gcode.insBlocks(active, blocks, 'Function inserted') #insert blocks over active block in the editor app.refresh() #refresh editor app.setStatus(_('Generated function graph')) #feed back result print()
def execute(self, app): try: import midiparser as midiparser except: app.setStatus(_("Error: This plugin requires midiparser.py")) return n = self["name"] if not n or n == "default": n = "Midi2CNC" fileName = self["File"] x = 0.0 y = 0.0 z = 0.0 x_dir = 1.0 y_dir = 1.0 z_dir = 1.0 # List of MIDI channels (instruments) to import. # Channel 10 is percussion, so better to omit it channels = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15] axes = self["AxisUsed"] active_axes = len(axes) transpose = (0, 0, 0) ppu = [200, 200, 200] ppu[0] = self["ppu_X"] ppu[1] = self["ppu_X"] ppu[2] = self["ppu_X"] safemin = [0, 0, 0] safemax = [100, 100, 50] safemax[0] = self["max_X"] safemax[1] = self["max_Y"] safemax[2] = self["max_Z"] try: midi = midiparser.File(fileName) except: app.setStatus(_("Error: Sorry can't parse the Midi file.")) return noteEventList = [] all_channels = set() for track in midi.tracks: #channels=set() for event in track.events: if event.type == midiparser.meta.SetTempo: tempo = event.detail.tempo # filter undesired instruments if ((event.type == midiparser.voice.NoteOn) and (event.channel in channels)): if event.channel not in channels: channels.add(event.channel) # NB: looks like some use "note on (vel 0)" as equivalent to note off, so check for vel=0 here and treat it as a note-off. if event.detail.velocity > 0: noteEventList.append([ event.absolute, 1, event.detail.note_no, event.detail.velocity ]) else: noteEventList.append([ event.absolute, 0, event.detail.note_no, event.detail.velocity ]) if (event.type == midiparser.voice.NoteOff) and (event.channel in channels): if event.channel not in channels: channels.add(event.channel) noteEventList.append([ event.absolute, 0, event.detail.note_no, event.detail.velocity ]) # Finished with this track if len(channels) > 0: msg = ', '.join(['%2d' % ch for ch in sorted(channels)]) #print 'Processed track %d, containing channels numbered: [%s ]' % (track.number, msg) all_channels = all_channels.union(channels) # List all channels encountered if len(all_channels) > 0: msg = ', '.join(['%2d' % ch for ch in sorted(all_channels)]) #print 'The file as a whole contains channels numbered: [%s ]' % msg # We now have entire file's notes with abs time from all channels # We don't care which channel/voice is which, but we do care about having all the notes in order # so sort event list by abstime to dechannelify noteEventList.sort() # print noteEventList # print len(noteEventList) last_time = -0 active_notes = { } # make this a dict so we can add and remove notes by name # Start the output #Init blocks blocks = [] block = Block(self.name) block.append("(Midi2CNC)") block.append("(Midi:%s)" % fileName) block.append(CNC.zsafe()) block.append(CNC.grapid(0, 0)) block.append(CNC.zenter(0)) for note in noteEventList: # note[timestamp, note off/note on, note_no, velocity] if last_time < note[0]: freq_xyz = [0, 0, 0] feed_xyz = [0, 0, 0] distance_xyz = [0, 0, 0] duration = 0 # "i" ranges from 0 to "the number of active notes *or* the number of active axes, # whichever is LOWER". Note that the range operator stops # short of the maximum, so this means 0 to 2 at most for a 3-axis machine. # E.g. only look for the first few active notes to play despite what # is going on in the actual score. for i in range(0, min(len(active_notes.values()), active_axes)): # Which axis are should we be writing to? # j = self.axes_dict.get(axes)[i] # Debug # print"Axes %s: item %d is %d" % (axes_dict.get(args.axes), i, j) # Sound higher pitched notes first by sorting by pitch then indexing by axis # nownote = sorted(active_notes.values(), reverse=True)[i] # MIDI note 69 = A4(440Hz) # 2 to the power (69-69) / 12 * 440 = A4 440Hz # 2 to the power (64-69) / 12 * 440 = E4 329.627Hz # freq_xyz[j] = pow( 2.0, (nownote - 69 + transpose[j]) / 12.0) * 440.0 # Here is where we need smart per-axis feed conversions # to enable use of X/Y *and* Z on a Makerbot # # feed_xyz[0] = X; feed_xyz[1] = Y; feed_xyz[2] = Z; # # Feed rate is expressed in mm / minutes so 60 times # scaling factor is required. feed_xyz[j] = (freq_xyz[j] * 60.0) / ppu[j] # Get the duration in seconds from the MIDI values in divisions, at the given tempo duration = (((note[0] - last_time) + 0.0) / (midi.division + 0.0) * (tempo / 1000000.0)) # Get the actual relative distance travelled per axis in mm distance_xyz[j] = (feed_xyz[j] * duration) / 60.0 # Now that axes can be addressed in any order, need to make sure # that all of them are silent before declaring a rest is due. if distance_xyz[0] + distance_xyz[1] + distance_xyz[2] > 0: # At least one axis is playing, so process the note into # movements combined_feedrate = math.sqrt(feed_xyz[0]**2 + feed_xyz[1]**2 + feed_xyz[2]**2) # Turn around BEFORE crossing the limits of the # safe working envelope if self.reached_limit(x, distance_xyz[0], x_dir, safemin[0], safemax[0]): x_dir = x_dir * -1 x = (x + (distance_xyz[0] * x_dir)) if self.reached_limit(y, distance_xyz[1], y_dir, safemin[1], safemax[1]): y_dir = y_dir * -1 y = (y + (distance_xyz[1] * y_dir)) if self.reached_limit(z, distance_xyz[2], z_dir, safemin[2], safemax[2]): z_dir = z_dir * -1 z = (z + (distance_xyz[2] * z_dir)) v = (x, y, z) block.append(CNC.glinev(1, v, combined_feedrate)) else: # Handle 'rests' in addition to notes. duration = (((note[0] - last_time) + 0.0) / (midi.division + 0.0)) * (tempo / 1000000.0) block.append(CNC.gcode(4, [("P", duration)])) # finally, set this absolute time as the new starting time last_time = note[0] if note[1] == 1: # Note on if active_notes.has_key(note[2]): pass else: # key and value are the same, but we don't really care. active_notes[note[2]] = note[2] elif note[1] == 0: # Note off if (active_notes.has_key(note[2])): active_notes.pop(note[2]) blocks.append(block) active = app.activeBlock() if active == 0: active = 1 app.gcode.insBlocks(active, blocks, "Midi2CNC") app.refresh() app.setStatus(_("Generated Midi2CNC, ready to play?"))
def execute(self, app): name = self['name'] if not name or name == 'default': name = 'Function' # Initialize blocks that will contain our gCode blocks = [] block = Block(name) #Variable definitions formula = self['form'] res = self['res'] # X resolution ran = [self['ranX'], self['ranY']] # Range of X,Y, from -10, to 10 range is 20 cent = [ self['centX'], self['centY'] ] # Coordinates X,Y of the center from bottom left of the coordinate system dim = [self['dimX'], self['dimY']] # Real dimensions in gcode units spacX = self['spacX'] # Spacing of X axis lines spacY = self['spacY'] # Spacing of Y axis lines lin = self['lin'] # Small value - length of a line in gcode units draw = self['draw'] # Draw the coordinate system block.append("(Generated with a script by kswiorek)\n") block.append("(Equation: " + formula + ")\n") block.append("(Resolution: " + str(res) + ")\n") block.append("(Range: " + str(ran) + ")\n") block.append("(Center: " + str(cent) + ")\n") block.append("(Dimensions: " + str(dim) + ")\n") block.append("(SpacingXY: " + str(spacX) + ", " + str(spacY) + ")\n") def mapc(var, axis): #Map coordinate systems return (var * (dim[axis] / ran[axis])) #Define coordinate system mins and maxes minX = -cent[0] maxX = ran[0] - cent[0] minY = -cent[1] maxY = ran[1] - cent[1] #Define domain and codomain X = [] Y = [] e_old = "" #Store old exception to comapre #Calculate values for arguments with a resolution for i in range(0, int(ran[0] / res + 1)): #Complaints about values beeing floats x = i * res + minX #Iterate x X.append(x) try: Y.append(eval(formula)) except Exception as exc: #Append None, not to loose sync with X Y.append(None) e = str(exc) if e != e_old: #If there is a different exception - display it print("Warning: " + str(e)) app.setStatus(_("Warning: " + str(e))) e_old = e raised = True # Z axis is raised at start #Clip values out of bounds, replace with None, not to loose sync with X for i, item in enumerate(Y): y = Y[i] if not y is None and (y < minY or y > maxY): Y[i] = None #Y without "None", min() and max() can't compare them Ynn = [] #Y no Nones for i, item in enumerate(Y): if not Y[i] is None: Ynn.append(Y[i]) block.append(CNC.gcode(1, [("f", CNC.vars["cutfeed"])])) #Set feedrate if draw: #If the user selected to draw the coordinate system #X axis block.append(CNC.grapid(z=3)) block.append(CNC.grapid(0, mapc(cent[1], 1))) #1st point of X axis line block.append(CNC.grapid(z=0)) block.append(CNC.gline(dim[0] + lin * 1.2, mapc( cent[1], 1))) #End of X axis line + a bit more for the arrow block.append( CNC.gline(dim[0] - lin / 2, mapc(cent[1], 1) - lin / 2)) #bottom part of the arrow block.append(CNC.grapid(z=3)) block.append(CNC.grapid(dim[0] + lin * 1.2, mapc(cent[1], 1), 0)) #End of X axis line block.append(CNC.grapid(z=0)) block.append( CNC.gline(dim[0] - lin / 2, mapc(cent[1], 1) + lin / 2)) #top part of the arrow block.append(CNC.grapid(z=3)) #Y axis, just inverted x with y block.append(CNC.grapid(z=3)) block.append(CNC.grapid(mapc(cent[0], 0), 0)) #1st point of Y axis line block.append(CNC.grapid(z=0)) block.append(CNC.gline( mapc(cent[0], 0), dim[1] + lin * 1.2)) #End of Y axis line + a bit more for the arrow block.append( CNC.gline(mapc(cent[0], 0) - lin / 2, dim[1] - lin / 2)) #left part of the arrow block.append(CNC.grapid(z=3)) block.append(CNC.grapid(mapc(cent[0], 0), dim[1] + lin * 1.2)) #End of Y axis line block.append(CNC.grapid(z=0)) block.append( CNC.gline(mapc(cent[0], 0) + lin / 2, dim[1] - lin / 2)) #right part of the arrow block.append(CNC.grapid(z=3)) #X axis number lines i = 0 while i < ran[0] - cent[0]: #While i is on the left of the arrow i += spacX #Add line spacing #Draw lines right of the center block.append( CNC.grapid(mapc(i + cent[0], 0), mapc(cent[1], 1) + lin / 2)) block.append(CNC.grapid(z=0)) block.append( CNC.gline(mapc(i + cent[0], 0), mapc(cent[1], 1) - lin / 2)) block.append(CNC.grapid(z=3)) i = 0 while i > -cent[ 0]: #While i is lower than center coordinate, inverted for easier math i -= spacX #Add line spacing #Draw lines left of the center block.append( CNC.grapid(mapc(i + cent[0], 0), mapc(cent[1], 1) + lin / 2)) block.append(CNC.grapid(z=0)) block.append( CNC.gline(mapc(i + cent[0], 0), mapc(cent[1], 1) - lin / 2)) block.append(CNC.grapid(z=3)) #Y axis number lines i = 0 while i < ran[1] - cent[ 1]: #While i is between the center and the arrow i += spacX #Add line spacing #Draw lines top of the center (everything just inverted) block.append( CNC.grapid( mapc(cent[0], 0) + lin / 2, mapc(i + cent[1], 1))) block.append(CNC.grapid(z=0)) block.append( CNC.gline( mapc(cent[0], 0) - lin / 2, mapc(i + cent[1], 1))) block.append(CNC.grapid(z=3)) i = 0 while i > -1 * cent[1]: i -= spacX #Add line spacing #Draw lines bottom of the center block.append( CNC.grapid( mapc(cent[0], 0) + lin / 2, mapc(i + cent[1], 1))) block.append(CNC.grapid(z=0)) block.append( CNC.gline( mapc(cent[0], 0) - lin / 2, mapc(i + cent[1], 1))) block.append(CNC.grapid(z=3)) raised = True #Z was raised #Draw graph for i, item in enumerate(Y): if not Y[i] is None: x = mapc(X[i] + cent[0], 0) #Take an argument y = mapc(Y[i] + cent[1], 1) #Take a value else: y = Y[i] #only for tne None checks next if y is None and not raised: #If a None "period" just started raise Z raised = True block.append(CNC.grapid(z=3)) elif not y is None and raised: #If Z was raised and the None "period" ended move to new coordinates block.append(CNC.grapid(round(x, 2), round(y, 2))) block.append(CNC.grapid(z=0)) #Lower Z raised = False elif not y is None and not raised: #Nothing to do with Nones? Just draw block.append(CNC.gline(round(x, 2), round(y, 2))) block.append(CNC.grapid(z=3)) #Raise on the end blocks.append(block) active = app.activeBlock() app.gcode.insBlocks(active, blocks, 'Function inserted' ) #insert blocks over active block in the editor app.refresh() #refresh editor app.setStatus(_('Generated function graph')) #feed back result print()
def make(self,app, XStart=0.0, YStart=0.0, FlatWidth=10., FlatHeight=10., \ FlatDepth=0, BorderPass=False, CutDirection="Climb", PocketType="Raster"): #GCode Blocks blocks = [] #Check parameters if CutDirection is "": app.setStatus(_("Flatten abort: Cut Direction is undefined")) return if PocketType is "": app.setStatus(_("Flatten abort: Pocket Type is undefined")) return if FlatWidth <= 0 or FlatHeight <= 0 : app.setStatus(_("Flatten abort: Flatten Area dimensions must be > 0")) return if FlatDepth > 0 : app.setStatus(_("Flatten abort: Hey this is only for subtractive machine! Check depth!")) return #Add Region disabled to show worked area block = Block(self.name + " Outline") block.enable = False block.append(CNC.zsafe()) xR,yR = self.RectPath(XStart,YStart,FlatWidth,FlatHeight) for x,y in zip(xR,yR): block.append(CNC.gline(x,y)) blocks.append(block) # Load tool and material settings toolDiam = CNC.vars['diameter'] toolRadius = toolDiam / 2. #Calc tool diameter with Maximum Step Over allowed StepOverInUnitMax = toolDiam * CNC.vars['stepover'] / 100.0 #Offset for Border Cut BorderXStart = XStart + toolRadius BorderYStart = YStart + toolRadius BorderWidth = FlatWidth - toolDiam BorderHeight = FlatHeight - toolDiam BorderXEnd = XStart + FlatWidth - toolRadius BorderYEnd = YStart + FlatHeight - toolRadius PocketXStart = BorderXStart PocketYStart = BorderYStart PocketXEnd = BorderXEnd PocketYEnd = BorderYEnd #Calc space to work with/without border cut WToWork = FlatWidth - toolDiam HToWork = FlatHeight - toolDiam if(WToWork < toolRadius or HToWork < toolRadius): app.setStatus(_("Flatten abort: Flatten area is too small for this End Mill.")) return #Prepare points for pocketing xP=[] yP=[] #and border xB=[] yB=[] #--------------------------------------------------------------------- #Raster approach if PocketType == "Raster": #Correct sizes if border is used if(BorderPass): PocketXStart += StepOverInUnitMax PocketYStart += StepOverInUnitMax PocketXEnd -= StepOverInUnitMax PocketYEnd -= StepOverInUnitMax WToWork -= (StepOverInUnitMax) HToWork -= (StepOverInUnitMax) #Calc number of pass VerticalCount = (int)(HToWork / StepOverInUnitMax) #Calc step minor of Max step StepOverInUnit = HToWork / (VerticalCount +1) flip = False ActualY = PocketYStart #Zig zag if StepOverInUnit==0 : StepOverInUnit=0.001 #avoid infinite while loop while (True): #Zig xP.append(self.ZigZag(flip,PocketXStart,PocketXEnd)) yP.append(ActualY) flip = not flip #Zag xP.append(self.ZigZag(flip,PocketXStart,PocketXEnd)) yP.append(ActualY) if(ActualY >= PocketYEnd - StepOverInUnitMax + StepOverInUnit): break #Up ActualY += StepOverInUnit xP.append(self.ZigZag(flip,PocketXStart,PocketXEnd)) yP.append(ActualY) #Points for border cut depends on Zig/Zag end if(BorderPass): if flip: xB,yB = self.RectPath(BorderXStart,BorderYEnd,BorderWidth,-BorderHeight) else: xB,yB = self.RectPath(BorderXEnd,BorderYEnd,-BorderWidth,-BorderHeight) #Reverse in case of Climb if CutDirection == "Climb": xB = xB[::-1] yB = yB[::-1] #--------------------------------------------------------------------- #Offset approach if PocketType == "Offset": #Calc number of pass VerticalCount = (int)(HToWork / StepOverInUnitMax) HorrizontalCount = (int)(WToWork / StepOverInUnitMax) #Make them odd if VerticalCount%2 == 0 : VerticalCount += 1 if HorrizontalCount%2 == 0 : HorrizontalCount += 1 #Calc step minor of Max step StepOverInUnitH = HToWork / (VerticalCount) StepOverInUnitW = WToWork / (HorrizontalCount) #Start from border to center xS = PocketXStart yS = PocketYStart wS = WToWork hS = HToWork xC = 0 yC = 0 while (xC<=HorrizontalCount/2 and yC<=VerticalCount/2): #Pocket offset points xO,yO = self.RectPath(xS, yS, wS, hS) if CutDirection == "Conventional": xO = xO[::-1] yO = yO[::-1] xP = xP + xO yP = yP + yO xS+=StepOverInUnitH yS+=StepOverInUnitW hS-=2.0*StepOverInUnitH wS-=2.0*StepOverInUnitW xC += 1 yC += 1 #Reverse point to start from inside (less stres on the tool) xP = xP[::-1] yP = yP[::-1] #Blocks for pocketing block = Block(self.name) block.append("(Flatten from X=%g Y=%g)"%(XStart,YStart)) block.append("(W=%g x H=%g x D=%g)"%(FlatWidth,FlatHeight,FlatDepth)) block.append("(Approach: %s %s)" % (PocketType,CutDirection)) if BorderPass : block.append("(with border)") #Move safe to first point block.append(CNC.zsafe()) block.append(CNC.grapid(xP[0],yP[0])) #Init Depth currDepth = 0. stepz = CNC.vars['stepz'] if stepz==0 : stepz=0.001 #avoid infinite while loop #Create GCode from points while True: currDepth -= stepz if currDepth < FlatDepth : currDepth = FlatDepth block.append(CNC.zenter(currDepth)) block.append(CNC.gcode(1, [("f",CNC.vars["cutfeed"])])) #Pocketing for x,y in zip(xP,yP): block.append(CNC.gline(x,y)) #Border cut if request for x,y in zip(xB,yB): block.append(CNC.gline(x,y)) #Verify exit condition if currDepth <= FlatDepth : break #Move to the begin in a safe way block.append(CNC.zsafe()) block.append(CNC.grapid(xP[0],yP[0])) #Zsafe block.append(CNC.zsafe()) blocks.append(block) return blocks
def setCutFeedrate(): block.append(CNC.gcode(1, [("f",CNC.vars["cutfeed"])]))
def execute(self, app): name = self["name"] if not name or name=="default": name="Default Name" sel_Blocks = self["Sel_Blocks"] #Get inputs x = self["X"] y = self["Y"] z = self["Z"] if z == "": z=CNC.vars["surface"] cutDiam = self["CutDiam"] cutRadius = cutDiam/2.0 if self["endmill"]: self.master["endmill"].makeCurrent(self["endmill"]) toolDiam = CNC.vars["diameter"] #Radio = self["RadioHelix"] pitch = self["Pitch"] Depth = self["Depth"] Mult_F_Z = self["Mult_Feed_Z"] helicalCut = self["HelicalCut"] clearanceEntry = self["ClearanceEntry"] clearanceExit = self["ClearanceExit"] clearance = clearanceEntry entry = self["Entry"] returnToSafeZ = self["ReturnToSafeZ"] toolDiam = CNC.vars['diameter'] toolRadius = toolDiam/2.0 Radio = cutRadius - toolRadius if(Radio < 0): Radio = 0 toolDiam = CNC.vars['diameter'] toolRadius = toolDiam/2.0 Radio = cutRadius - toolRadius if clearanceEntry =="": clearanceEntry =0 if clearanceExit =="": clearanceExit =0 if helicalCut == "Helical Cut": turn = 2 p="HelicalCut " elif helicalCut == "Internal Right Thread": turn = 2 p= "IntRightThread " elif helicalCut == "Internal Left Thread": turn = 3 p= "IntLeftThread " elif helicalCut == "External Right Thread": Radio = cutRadius + toolRadius turn = 2 p= "ExtRightThread " elif helicalCut == "External Left Thread": Radio = cutRadius + toolRadius turn = 3 p= "ExtLeftThread " # ------------------------------------------------------------------------------------------------------------------ #Check inputs if sel_Blocks == 0: if x == "" or y == "" : app.setStatus(_("If block selected false, please make a value of x")) return elif helicalCut == "": app.setStatus(_("Helical Abort: Please select helical type")) return elif cutDiam < toolDiam or cutDiam == "": app.setStatus(_("Helical Abort: Helix diameter must be greater than the end mill")) return elif cutDiam <= 0: app.setStatus(_("Helical Abort: Helix diameter must be positive")) return elif pitch <= 0 or pitch =="": app.setStatus(_("Helical Abort: Drop must be greater than 0")) return elif Mult_F_Z <= 0 or Mult_F_Z == "": app.setStatus(_("Helical Abort: Z Feed Multiplier must be greater than 0")) return elif entry == "": app.setStatus(_("Helical Abort: Please selecte Entry and Exit type")) return elif clearanceEntry < 0 or clearanceEntry == "": app.setStatus(_("Helical Abort: Entry Edge Clearence may be positive")) return elif clearanceExit < 0 or clearanceExit == "": app.setStatus(_("Helical Abort: Exit Edge Clearence may be positive")) return # ------------------------------------------------------------------------------------------------------------------ #Initialize blocks that will contain our gCode blocks = [] #block = Block(name) block = Block( p + str(cutDiam) + " Pitch " + str(pitch) + " Bit " + str(toolDiam) + " depth " + str(Depth)) cutFeed = CNC.vars["cutfeedz"] #<<< Get cut feed Z for the current material cutFeedMax = CNC.vars["cutfeed"] #<<< Get cut feed XY for the current material # ------------------------------------------------------------------------------------------------------------------ # Get selected blocks from editor selBlocks = app.editor.getSelectedBlocks() if not selBlocks: app.editor.selectAll() selBlocks = app.editor.getSelectedBlocks() if not selBlocks: if sel_Blocks == 1: app.setStatus(_("Helical abort: Please select some path")) return # ------------------------------------------------------------------------------------------------------------------ # Get selected blocks from editor if sel_Blocks == 1: selBlocks = app.editor.getSelectedBlocks() if not selBlocks: app.editor.selectAll() selBlocks = app.editor.getSelectedBlocks() #Get all segments from gcode allSegments = self.extractAllSegments(app,selBlocks) #Create holes locations allHoles=[] for bidSegment in allSegments: if len(bidSegment)==0: continue bidHoles = [] for idx, anchor in enumerate(bidSegment): if idx ==2: newHolePoint = (anchor[0][0],anchor[0][1],anchor[0][2]) bidHoles.append(newHolePoint) #Add bidHoles to allHoles allHoles.append(bidHoles) # ------------------------------------------------------------------------------------------------------------------ holesCount = 0 for bid in allHoles: for xH,yH,zH in bid: x = xH y = yH # ------------------------------------------------------------------------------------------------------------------ # Init: Adjust feed and rapid move to Z safe if Mult_F_Z is"": Mult_F_Z = 1 if Mult_F_Z == 0: Mult_F_Z = 1 if Mult_F_Z * cutFeed > cutFeedMax: cutFeed = cutFeedMax else: cutFeed = cutFeed*Mult_F_Z block.append(CNC.zsafe()) #<<< Move rapid Z axis to the safe height in Stock Material # Move rapid to X and Y coordinate if helicalCut == "Helical Cut" or helicalCut == "Internal Right Thread" or helicalCut == "Internal Left Thread": if entry == "Center": block.append(CNC.grapid(x,y)) else: block.append(CNC.grapid(x-Radio+clearance ,y)) if helicalCut == "External Right Thread" or helicalCut == "External Left Thread": if entry == "Center": clearance = 0.0 block.append(CNC.grapid(x-Radio-clearance ,y)) #cutFeed = int(cutFeed) block.append(CNC.fmt("f",cutFeed)) #<<< Set cut feed # block.append(CNC.gline(x,y) # while (z < 1): block.append(CNC.zenter(z)) block.append(CNC.gline(x-Radio,y)) # cutFeed = int((CNC.vars["cutfeed"] + CNC.vars["cutfeedz"])/2) #<<< Get cut feed for the current material #cutFeed = int(cutFeed) block.append(CNC.fmt("F",cutFeed)) #<<< Set cut feed #----------------------------------------------------------------------------------------------------- # Uncomment for first flat pass if helicalCut == "Helical Cut": block.append(CNC.gcode(turn, [("X",x-Radio),("Y",y),("Z", z),("I",Radio), ("J",0)])) #----------------------------------------------------------------------------------------------------- if (z < Depth): pitch = -pitch while ((z-pitch) < Depth) : z = z-pitch block.append(CNC.gcode(turn, [("X",x-Radio),("Y",y),("Z", z),("I",Radio), ("J",0)])) else: while ((z-pitch) >= Depth) : z = z-pitch block.append(CNC.gcode(turn, [("X",x-Radio),("Y",y),("Z", z),("I",Radio), ("J",0)])) #Target Level if entry == "Center": clearanceExit = 0.0 clearance = clearanceExit alpha= round(Depth / pitch, 4 ) - round(Depth / pitch, 0) alpha = alpha * 2*pi Radiox = Radio * cos(alpha) Radioy = Radio * sin(alpha) xsi = Radiox - clearance* cos(alpha) ysi =Radioy - clearance* sin(alpha) xse = Radiox + clearance* cos(alpha) yse =Radioy + clearance* sin(alpha) z = Depth if helicalCut == "Helical Cut": block.append(CNC.gcode(turn, [("X",x-Radio),("Y",y),("Z", z),("I",Radio), ("J",0)])) #Last flat pass block.append(CNC.gcode(turn, [("X",x-Radio),("Y",y),("Z", z),("I",Radio), ("J",0)])) elif helicalCut == "Internal Right Thread" or helicalCut == "External Right Thread": block.append(CNC.gcode(turn, [("X",x-Radiox),("Y",y-Radioy),("Z", z),("I",Radio), ("J",0)])) elif helicalCut == "Internal Left Thread" or helicalCut == "External Left Thread": block.append(CNC.gcode(turn, [("X",x-Radiox),("Y",y+Radioy),("Z", z),("I",Radio), ("J",0)])) # Exit clearance if helicalCut == "Internal Right Thread": block.append(CNC.gline(x-xsi,y-ysi)) elif helicalCut == "Internal Left Thread": block.append(CNC.gline(x-xsi,y+ysi)) if helicalCut == "External Right Thread": block.append(CNC.gline(x-xse,y-yse)) elif helicalCut == "External Left Thread": block.append(CNC.gline(x-xse,y+yse)) # Return to Z Safe if returnToSafeZ == 1: if helicalCut == "Helical Cut" or helicalCut == "Internal Right Thread" or helicalCut == "Internal Left Thread": if entry == "Center": block.append(CNC.gline(x,y)) block.append(CNC.zsafe()) blocks.append(block) active = app.activeBlock() app.gcode.insBlocks(active, blocks, "Helical_Descent inserted") #<<< insert blocks over active block in the editor app.refresh() #<<< refresh editor app.setStatus(_("Generated: Helical_Descent Result")) #<<< feed back result
def execute(self, app): try: from PIL import Image except: app.setStatus(_("Sketch abort: This plugin requires PIL/Pillow to read image data")) return n = self["name"] if not n or n=="default": n="Sketch" #Calc desired size grundgy =self["Grundgy"] maxSize = self["MaxSize"] squiggleTotal = self["SquiggleTotal"] squiggleLength = self["SquiggleLength"] depth = self["Depth"] drawBorder = self["DrawBorder"] channel = self["Channel"] radius = 1 if grundgy == "Low": radius = 2 elif grundgy == "Medium": radius = 3 elif grundgy == "High": radius = 6 elif grundgy == "Very High": radius = 9 #Check parameters if maxSize < 1: app.setStatus(_("Sketch abort: Too small to draw anything!")) return if squiggleTotal < 1: app.setStatus(_("Sketch abort: Please let me draw at least 1 squiggle")) return if squiggleLength <= 0: app.setStatus(_("Sketch abort: Squiggle Length must be > 0")) return fileName = self["File"] try: img = Image.open(fileName) except: app.setStatus(_("Sketch abort: Can't read image file")) return #Create a scaled image to work faster with big image and better with small ones iWidth,iHeight = img.size resampleRatio = 800.0 / iHeight img = img.resize((int(iWidth *resampleRatio) ,int(iHeight * resampleRatio)), Image.ANTIALIAS) if channel == 'Blue': img = img.convert('RGB') img = img.split()[0] elif channel == 'Green': img = img.convert('RGB') img = img.split()[1] elif channel == 'Red': img = img.convert('RGB') img = img.split()[2] else: img = img.convert ('L') #to calculate luminance img = img.transpose(Image.FLIP_TOP_BOTTOM) #ouput correct image pix = img.load() #Get image size self.imgWidth, self.imgHeight = img.size self.ratio = 1 if (iWidth > iHeight): self.ratio = maxSize / float(self.imgWidth) else: self.ratio = maxSize / float(self.imgHeight) #Init blocks blocks = [] #Border block block = Block("%s-border"%(self.name)) block.enable = drawBorder block.append(CNC.zsafe()) block.append(CNC.grapid(0,0)) block.append(CNC.zenter(depth)) block.append(CNC.gcode(1, [("f",CNC.vars["cutfeed"])])) block.append(CNC.gline(self.imgWidth * self.ratio, 0)) block.append(CNC.gline(self.imgWidth * self.ratio, self.imgHeight*self.ratio)) block.append(CNC.gline(0, self.imgHeight*self.ratio)) block.append(CNC.gline(0,0)) blocks.append(block) #Draw block block = Block(self.name) block.append("(Sketch size W=%d x H=%d x distance=%d)" % (self.imgWidth * self.ratio , self.imgHeight * self.ratio , depth)) block.append("(Channel = %s)" %(channel)) #choose a nice starting point x = self.imgWidth / 4. y = self.imgHeight / 4. #First round search in all image self.mostest = 256 x,y = self.findFirst(pix, True) #startAll = time.time() for c in range(squiggleTotal): #print c,x,y #start = time.time() x,y = self.findFirst(pix, False) #print 'Find mostest: %f' % (time.time() - start) #move there block.append(CNC.zsafe()) block.append(CNC.grapid(x*self.ratio, y*self.ratio)) #tool down block.append(CNC.zenter(depth)) #restore cut/draw feed block.append(CNC.gcode(1, [("f",CNC.vars["cutfeed"])])) #start = time.time() s = 0 while (s < squiggleLength): x,y,distance = self.findInRange(x, y, pix, radius) s+= max(1,distance*self.ratio) #add traveled distance #move there block.append(CNC.gline(x*self.ratio,y*self.ratio)) self.fadePixel(x, y, pix) #adjustbrightness int the bright map #tool up block.append(CNC.zsafe()) #print 'Squiggle: %f' % (time.time() - start) #Gcode Zsafe block.append(CNC.zsafe()) blocks.append(block) active = app.activeBlock() app.gcode.insBlocks(active, blocks, "Sketch") app.refresh() app.setStatus(_("Generated Sketch size W=%d x H=%d x distance=%d, Total length:%d") % (self.imgWidth*self.ratio , self.imgHeight*self.ratio , depth, squiggleTotal*squiggleLength))
def execute(self, app): #Get inputs holesDistance = self.fromMm("HolesDistance") targetDepth = self.fromMm("TargetDepth") peck = self.fromMm("Peck") dwell = self["Dwell"] zSafe = CNC.vars["safe"] #Check inputs if holesDistance <=0: app.setStatus(_("Driller abort: Distance must be > 0")) return if peck <0: app.setStatus(_("Driller abort: Peck must be >= 0")) return if dwell <0: app.setStatus(_("Driller abort: Dwell time >= 0, here time runs only forward!")) return # Get selected blocks from editor selBlocks = app.editor.getSelectedBlocks() if not selBlocks: app.editor.selectAll() selBlocks = app.editor.getSelectedBlocks() if not selBlocks: app.setStatus(_("Driller abort: Please select some path")) return #Get all segments from gcode allSegments = self.extractAllSegments(app,selBlocks) #Create holes locations allHoles=[] for bidSegment in allSegments: if len(bidSegment)==0: continue #Summ all path length fullPathLength = 0.0 for s in bidSegment: fullPathLength += s[3] #Calc rest holes = fullPathLength // holesDistance rest = fullPathLength - (holesDistance * (holes)) #Travel along the path elapsedLength = rest / 2.0 #equaly distribute rest, as option??? bidHoles = [] while elapsedLength <= fullPathLength: #Search best segment to apply line interpolation bestSegment = bidSegment[0] segmentsSum = 0.0 perc = 0.0 for s in bidSegment: bestSegment = s segmentLength = bestSegment[3] perc = (elapsedLength-segmentsSum) / segmentLength segmentsSum += segmentLength if segmentsSum > elapsedLength : break #Fist point x1 = bestSegment[0][0] y1 = bestSegment[0][1] z1 = bestSegment[0][2] #Last point x2 = bestSegment[1][0] y2 = bestSegment[1][1] z2 = bestSegment[1][2] #Check if segment is not excluded if not bestSegment[2]: newHolePoint = (x1 + perc*(x2-x1) , y1 + perc*(y2-y1), z1 + perc*(z2-z1)) bidHoles.append(newHolePoint) #Go to next hole elapsedLength += holesDistance #Add bidHoles to allHoles allHoles.append(bidHoles) #Write gcommands from allSegments to the drill block n = self["name"] if not n or n=="default": n="Driller" blocks = [] block = Block(self.name) holesCount = 0 for bid in allHoles: for xH,yH,zH in bid: holesCount += 1 block.append(CNC.grapid(None,None,zH + zSafe)) block.append(CNC.grapid(xH,yH)) if (peck != 0) : z = 0 while z > targetDepth: z = max(z-peck, targetDepth) block.append(CNC.zenter(zH + z)) block.append(CNC.grapid(None,None,zH + zSafe)) block.append(CNC.zenter(zH + targetDepth)) #dwell time only on last pass if dwell != 0: block.append(CNC.gcode(4, [("P",dwell)])) #Gcode Zsafe on finish block.append(CNC.zsafe()) blocks.append(block) #Insert created block active = app.activeBlock() if active==0: active=1 app.gcode.insBlocks(active, blocks, "Driller") app.refresh() app.setStatus(_("Generated Driller: %d holes")%holesCount)
def execute(self, app): try: from PIL import Image except: app.setStatus( _("Sketch abort: This plugin requires PIL/Pillow to read image data" )) return n = self["name"] if not n or n == "default": n = "Sketch" #Calc desired size grundgy = self["Grundgy"] maxSize = self["MaxSize"] squiggleTotal = self["SquiggleTotal"] squiggleLength = self["SquiggleLength"] depth = self["Depth"] drawBorder = self["DrawBorder"] channel = self["Channel"] casual = self["Casual"] fading = self["Fading"] max_light = self["Max_light"] repetition = self["Repetition"] radius = 1 if grundgy == "Low": radius = 2 elif grundgy == "Medium": radius = 3 elif grundgy == "High": radius = 6 elif grundgy == "Very High": radius = 9 #Check parameters if maxSize < 1: app.setStatus(_("Sketch abort: Too small to draw anything!")) return if max_light > 256: app.setStatus( _("The maximum illumination shouldn't be more than 250!")) return if squiggleTotal < 1: app.setStatus( _("Sketch abort: Please let me draw at least 1 squiggle")) return if squiggleLength <= 0: app.setStatus(_("Sketch abort: Squiggle Length must be > 0")) return fileName = self["File"] try: img = Image.open(fileName) except: app.setStatus(_("Sketch abort: Can't read image file")) return #Create a scaled image to work faster with big image and better with small ones iWidth, iHeight = img.size resampleRatio = 800.0 / iHeight img = img.resize( (int(iWidth * resampleRatio), int(iHeight * resampleRatio)), Image.ANTIALIAS) if channel == 'Blue': img = img.convert('RGB') img = img.split()[0] elif channel == 'Green': img = img.convert('RGB') img = img.split()[1] elif channel == 'Red': img = img.convert('RGB') img = img.split()[2] else: img = img.convert('L') #to calculate luminance img = img.transpose(Image.FLIP_TOP_BOTTOM) #ouput correct image pix = img.load() #Get image size self.imgWidth, self.imgHeight = img.size self.ratio = 1 if (iWidth > iHeight): self.ratio = maxSize / float(self.imgWidth) else: self.ratio = maxSize / float(self.imgHeight) #Init blocks blocks = [] #Info block block = Block("Info") block.append( "(Sketch size W=%d x H=%d x distance=%d)" % (self.imgWidth * self.ratio, self.imgHeight * self.ratio, depth)) block.append("(Channel = %s)" % (channel)) blocks.append(block) #Border block block = Block("%s-border" % (self.name)) block.enable = drawBorder block.append(CNC.zsafe()) block.append(CNC.grapid(0, 0)) block.append(CNC.zenter(depth)) block.append(CNC.gcode(1, [("f", CNC.vars["cutfeed"])])) block.append(CNC.gline(self.imgWidth * self.ratio, 0)) block.append( CNC.gline(self.imgWidth * self.ratio, self.imgHeight * self.ratio)) block.append(CNC.gline(0, self.imgHeight * self.ratio)) block.append(CNC.gline(0, 0)) blocks.append(block) #choose a nice starting point x = self.imgWidth / 4. y = self.imgHeight / 4. #First round search in all image self.mostest = 256 x, y = self.findFirst(pix, True, casual) #startAll = time.time() total_line = 0 total_length = 0 for c in range(squiggleTotal): x, y = self.findFirst(pix, False, casual) if pix[x, y] > max_light: continue block = Block(self.name) #print c,x,y #start = time.time() total_line += 1 total_length += 1 #move there block.append(CNC.zsafe()) block.append(CNC.grapid(x * self.ratio, y * self.ratio)) #tool down block.append(CNC.zenter(depth)) #restore cut/draw feed block.append(CNC.gcode(1, [("f", CNC.vars["cutfeed"])])) #start = time.time() s = 0 while (s < squiggleLength): x, y, distance = self.findInRange(x, y, pix, radius) if pix[x, y] > max_light: break s += max(1, distance * self.ratio) #add traveled distance total_length += 1 #move there block.append(CNC.gline(x * self.ratio, y * self.ratio)) self.fadePixel( x, y, pix, fading, repetition) #adjustbrightness int the bright map #tool up #print 'Squiggle: %f' % (time.time() - start) #Gcode Zsafe block.append(CNC.zsafe()) blocks.append(block) active = app.activeBlock() app.gcode.insBlocks(active, blocks, "Sketch") app.refresh() app.setStatus( _("Generated Sketch size W=%d x H=%d x distance=%d, Total line:%i, Total length:%d" ) % (self.imgWidth * self.ratio, self.imgHeight * self.ratio, depth, total_line, total_length))
def execute(self, app): name = self["name"] if not name or name == "default": name = "Default Name" sel_Blocks = self["Sel_Blocks"] #Get inputs x = self["X"] y = self["Y"] z = self["Z"] if z == "": z = CNC.vars["surface"] cutDiam = self["CutDiam"] cutRadius = cutDiam / 2.0 if self["endmill"]: self.master["endmill"].makeCurrent(self["endmill"]) toolDiam = CNC.vars["diameter"] #Radio = self["RadioHelix"] pitch = self["Pitch"] Depth = self["Depth"] Mult_F_Z = self["Mult_Feed_Z"] helicalCut = self["HelicalCut"] clearanceEntry = self["ClearanceEntry"] clearanceExit = self["ClearanceExit"] clearance = clearanceEntry entry = self["Entry"] returnToSafeZ = self["ReturnToSafeZ"] toolDiam = CNC.vars['diameter'] toolRadius = toolDiam / 2.0 Radio = cutRadius - toolRadius if (Radio < 0): Radio = 0 toolDiam = CNC.vars['diameter'] toolRadius = toolDiam / 2.0 Radio = cutRadius - toolRadius if clearanceEntry == "": clearanceEntry = 0 if clearanceExit == "": clearanceExit = 0 if helicalCut == "Helical Cut": turn = 2 p = "HelicalCut " elif helicalCut == "Internal Right Thread": turn = 2 p = "IntRightThread " elif helicalCut == "Internal Left Thread": turn = 3 p = "IntLeftThread " elif helicalCut == "External Right Thread": Radio = cutRadius + toolRadius turn = 2 p = "ExtRightThread " elif helicalCut == "External Left Thread": Radio = cutRadius + toolRadius turn = 3 p = "ExtLeftThread " # ------------------------------------------------------------------------------------------------------------------ #Check inputs if sel_Blocks == 0: if x == "" or y == "": app.setStatus( _("If block selected false, please make a value of x")) return elif helicalCut == "": app.setStatus(_("Helical Abort: Please select helical type")) return elif cutDiam < toolDiam or cutDiam == "": app.setStatus( _("Helical Abort: Helix diameter must be greater than the end mill" )) return elif cutDiam <= 0: app.setStatus(_("Helical Abort: Helix diameter must be positive")) return elif pitch <= 0 or pitch == "": app.setStatus(_("Helical Abort: Drop must be greater than 0")) return elif Mult_F_Z <= 0 or Mult_F_Z == "": app.setStatus( _("Helical Abort: Z Feed Multiplier must be greater than 0")) return elif entry == "": app.setStatus( _("Helical Abort: Please selecte Entry and Exit type")) return elif clearanceEntry < 0 or clearanceEntry == "": app.setStatus( _("Helical Abort: Entry Edge Clearence may be positive")) return elif clearanceExit < 0 or clearanceExit == "": app.setStatus( _("Helical Abort: Exit Edge Clearence may be positive")) return # ------------------------------------------------------------------------------------------------------------------ #Initialize blocks that will contain our gCode blocks = [] #block = Block(name) block = Block(p + str(cutDiam) + " Pitch " + str(pitch) + " Bit " + str(toolDiam) + " depth " + str(Depth)) cutFeed = CNC.vars[ "cutfeedz"] #<<< Get cut feed Z for the current material cutFeedMax = CNC.vars[ "cutfeed"] #<<< Get cut feed XY for the current material # ------------------------------------------------------------------------------------------------------------------ # Get selected blocks from editor selBlocks = app.editor.getSelectedBlocks() if not selBlocks: app.editor.selectAll() selBlocks = app.editor.getSelectedBlocks() if not selBlocks: if sel_Blocks == 1: app.setStatus(_("Helical abort: Please select some path")) return # ------------------------------------------------------------------------------------------------------------------ # Get selected blocks from editor if sel_Blocks == 1: selBlocks = app.editor.getSelectedBlocks() if not selBlocks: app.editor.selectAll() selBlocks = app.editor.getSelectedBlocks() #Get all segments from gcode allSegments = self.extractAllSegments(app, selBlocks) #Create holes locations allHoles = [] for bidSegment in allSegments: if len(bidSegment) == 0: continue bidHoles = [] for idx, anchor in enumerate(bidSegment): if idx == 2: newHolePoint = (anchor[0][0], anchor[0][1], anchor[0][2]) bidHoles.append(newHolePoint) #Add bidHoles to allHoles allHoles.append(bidHoles) # ------------------------------------------------------------------------------------------------------------------ holesCount = 0 for bid in allHoles: for xH, yH, zH in bid: x = xH y = yH # ------------------------------------------------------------------------------------------------------------------ # Init: Adjust feed and rapid move to Z safe if Mult_F_Z is "": Mult_F_Z = 1 if Mult_F_Z == 0: Mult_F_Z = 1 if Mult_F_Z * cutFeed > cutFeedMax: cutFeed = cutFeedMax else: cutFeed = cutFeed * Mult_F_Z block.append(CNC.zsafe( )) #<<< Move rapid Z axis to the safe height in Stock Material # Move rapid to X and Y coordinate if helicalCut == "Helical Cut" or helicalCut == "Internal Right Thread" or helicalCut == "Internal Left Thread": if entry == "Center": block.append(CNC.grapid(x, y)) else: block.append(CNC.grapid(x - Radio + clearance, y)) if helicalCut == "External Right Thread" or helicalCut == "External Left Thread": if entry == "Center": clearance = 0.0 block.append(CNC.grapid(x - Radio - clearance, y)) #cutFeed = int(cutFeed) block.append(CNC.fmt("f", cutFeed)) #<<< Set cut feed # block.append(CNC.gline(x,y) # while (z < 1): block.append(CNC.zenter(z)) block.append(CNC.gline(x - Radio, y)) # cutFeed = int((CNC.vars["cutfeed"] + CNC.vars["cutfeedz"])/2) #<<< Get cut feed for the current material #cutFeed = int(cutFeed) block.append(CNC.fmt("F", cutFeed)) #<<< Set cut feed #----------------------------------------------------------------------------------------------------- # Uncomment for first flat pass if helicalCut == "Helical Cut": block.append( CNC.gcode(turn, [("X", x - Radio), ("Y", y), ("Z", z), ("I", Radio), ("J", 0)])) #----------------------------------------------------------------------------------------------------- if (z < Depth): pitch = -pitch while ((z - pitch) < Depth): z = z - pitch block.append( CNC.gcode(turn, [("X", x - Radio), ("Y", y), ("Z", z), ("I", Radio), ("J", 0)])) else: while ((z - pitch) >= Depth): z = z - pitch block.append( CNC.gcode(turn, [("X", x - Radio), ("Y", y), ("Z", z), ("I", Radio), ("J", 0)])) #Target Level if entry == "Center": clearanceExit = 0.0 clearance = clearanceExit alpha = round(Depth / pitch, 4) - round(Depth / pitch, 0) alpha = alpha * 2 * pi Radiox = Radio * cos(alpha) Radioy = Radio * sin(alpha) xsi = Radiox - clearance * cos(alpha) ysi = Radioy - clearance * sin(alpha) xse = Radiox + clearance * cos(alpha) yse = Radioy + clearance * sin(alpha) z = Depth if helicalCut == "Helical Cut": block.append( CNC.gcode(turn, [("X", x - Radio), ("Y", y), ("Z", z), ("I", Radio), ("J", 0)])) #Last flat pass block.append( CNC.gcode(turn, [("X", x - Radio), ("Y", y), ("Z", z), ("I", Radio), ("J", 0)])) elif helicalCut == "Internal Right Thread" or helicalCut == "External Right Thread": block.append( CNC.gcode(turn, [("X", x - Radiox), ("Y", y - Radioy), ("Z", z), ("I", Radio), ("J", 0)])) elif helicalCut == "Internal Left Thread" or helicalCut == "External Left Thread": block.append( CNC.gcode(turn, [("X", x - Radiox), ("Y", y + Radioy), ("Z", z), ("I", Radio), ("J", 0)])) # Exit clearance if helicalCut == "Internal Right Thread": block.append(CNC.gline(x - xsi, y - ysi)) elif helicalCut == "Internal Left Thread": block.append(CNC.gline(x - xsi, y + ysi)) if helicalCut == "External Right Thread": block.append(CNC.gline(x - xse, y - yse)) elif helicalCut == "External Left Thread": block.append(CNC.gline(x - xse, y + yse)) # Return to Z Safe if returnToSafeZ == 1: if helicalCut == "Helical Cut" or helicalCut == "Internal Right Thread" or helicalCut == "Internal Left Thread": if entry == "Center": block.append(CNC.gline(x, y)) block.append(CNC.zsafe()) blocks.append(block) active = app.activeBlock() app.gcode.insBlocks( active, blocks, "Helical_Descent inserted" ) #<<< insert blocks over active block in the editor app.refresh() #<<< refresh editor app.setStatus( _("Generated: Helical_Descent Result")) #<<< feed back result
def execute(self, app): try: from PIL import Image except: app.setStatus(_("Pyrograph abort: This plugin requires PIL/Pillow")) return n = self["name"] if not n or n=="default": n="Pyrograph" #Calc desired size toolSize = self.fromMm("ToolSize") maxSize = self.fromMm("MaxSize") feedMin = self["FeedMin"] feedMax = self["FeedMax"] depth = self.fromMm("Depth") direction = self["Direction"] drawBorder = self["DrawBorder"] #Check parameters if direction is "": app.setStatus(_("Pyrograph abort: please define a scan Direction")) return if toolSize <=0: app.setStatus(_("Pyrograph abort: Tool Size must be > 0")) return if feedMin <=0 or feedMax <=0 : app.setStatus(_("Pyrograph abort: Please check feed rate parameters")) return #divisions divisions = maxSize / toolSize fileName = self["File"] try: img = Image.open(fileName) img = img.convert ('RGB') #be sure to have color to calculate luminance except: app.setStatus(_("Pyrograph abort: Can't read image file")) return iWidth,iHeight = img.size newWidth = iWidth newHeight = iHeight ratio = 1 if (iWidth > iHeight): ratio = float(iWidth) / float(iHeight) newWidth = int(divisions) newHeight = int(divisions / ratio) else: ratio = float(iHeight) / float(iWidth) newWidth = int(divisions / ratio) newHeight = int(divisions) #Create a thumbnail image to work faster img.thumbnail((newWidth,newHeight), Image.ANTIALIAS) newWidth,newHeight = img.size #img.save("thumb.png") pixels = list(img.getdata()) #Extract luminance gMap = [] for x in range(0,newWidth): gRow = [] for y in range(0,newHeight): R,G,B = pixels[(y * newWidth) + x ] L = (0.299*R + 0.587*G + 0.114*B) #Luminance (Rec. 601 standard) gRow.append(L) gMap.append(gRow) #Init blocks blocks = [] block = Block(self.name) block.append("(Pyrograph W=%g x H=%g x D=%g)" % (newWidth * toolSize , newHeight * toolSize , depth)) #Create points for vertical scan xH = [] yH = [] fH = [] if (direction=="Vertical" or direction=="Both"): r = range(0,newHeight) for x in range(0,newWidth): r = r[::-1] fPrec = -1 for y in r: f = int(feedMin + ((feedMax - feedMin) * gMap[x][y] / 255.0)) if(f != fPrec or y==0 or y==newHeight-1): xH.append(x * toolSize) yH.append((newHeight-y) * toolSize) fH.append(f) fPrec = f #Create points for horizontal scan xV = [] yV = [] fV = [] if (direction=="Horizontal" or direction=="Both"): r = range(0,newWidth) for y in reversed(range(0,newHeight)): fPrec = -1 for x in r: f = int(feedMin + ((feedMax - feedMin) * gMap[x][y] / 255.0)) if(f != fPrec or x==0 or x==newWidth-1): xV.append(x * toolSize) yV.append((newHeight-y) * toolSize) fV.append(f) fPrec = f r = r[::-1] #Gcode Horizontal if (len(xH)>1 and len(yH)>1): block.append(CNC.zsafe()) block.append(CNC.grapid(xH[0],yH[0])) block.append(CNC.zenter(depth)) for x,y,f in zip(xH,yH,fH): v = (x,y,depth) block.append(CNC.glinev(1,v,f)) #Gcode Vertical if (len(xV)>1 and len(yV)>1): block.append(CNC.zsafe()) block.append(CNC.grapid(xV[0],yV[0])) block.append(CNC.zenter(depth)) for x,y,f in zip(xV,yV,fV): v = (x,y,depth) block.append(CNC.glinev(1,v,f)) #Draw Border if required if drawBorder: block.append(CNC.zsafe()) block.append(CNC.grapid(0,0)) block.append(CNC.zenter(depth)) block.append(CNC.gcode(1, [("f",feedMin)])) block.append(CNC.gline(newWidth * toolSize - toolSize,0)) block.append(CNC.gline(newWidth * toolSize - toolSize ,newHeight* toolSize)) block.append(CNC.gline(0,newHeight* toolSize )) block.append(CNC.gline(0,0)) #Gcode Zsafe block.append(CNC.zsafe()) blocks.append(block) active = app.activeBlock() if active==0: active=1 app.gcode.insBlocks(active, blocks, "Pyrograph") app.refresh() app.setStatus(_("Generated Pyrograph W=%g x H=%g x D=%g") % (newWidth * toolSize , newHeight * toolSize , depth))
def execute(self, app): try: from PIL import Image except: app.setStatus( _("Pyrograph abort: This plugin requires PIL/Pillow")) return n = self["name"] if not n or n == "default": n = "Pyrograph" #Calc desired size toolSize = self.fromMm("ToolSize") maxSize = self.fromMm("MaxSize") feedMin = self["FeedMin"] feedMax = self["FeedMax"] depth = self.fromMm("Depth") direction = self["Direction"] drawBorder = self["DrawBorder"] #Check parameters if direction is "": app.setStatus(_("Pyrograph abort: please define a scan Direction")) return if toolSize <= 0: app.setStatus(_("Pyrograph abort: Tool Size must be > 0")) return if feedMin <= 0 or feedMax <= 0: app.setStatus( _("Pyrograph abort: Please check feed rate parameters")) return #divisions divisions = maxSize / toolSize fileName = self["File"] try: img = Image.open(fileName) img = img.convert( 'RGB') #be sure to have color to calculate luminance except: app.setStatus(_("Pyrograph abort: Can't read image file")) return iWidth, iHeight = img.size newWidth = iWidth newHeight = iHeight ratio = 1 if (iWidth > iHeight): ratio = float(iWidth) / float(iHeight) newWidth = int(divisions) newHeight = int(divisions / ratio) else: ratio = float(iHeight) / float(iWidth) newWidth = int(divisions / ratio) newHeight = int(divisions) #Create a thumbnail image to work faster img.thumbnail((newWidth, newHeight), Image.ANTIALIAS) newWidth, newHeight = img.size #img.save("thumb.png") pixels = list(img.getdata()) #Extract luminance gMap = [] for x in range(0, newWidth): gRow = [] for y in range(0, newHeight): R, G, B = pixels[(y * newWidth) + x] L = (0.299 * R + 0.587 * G + 0.114 * B ) #Luminance (Rec. 601 standard) gRow.append(L) gMap.append(gRow) #Init blocks blocks = [] block = Block(self.name) block.append("(Pyrograph W=%g x H=%g x D=%g)" % (newWidth * toolSize, newHeight * toolSize, depth)) #Create points for vertical scan xH = [] yH = [] fH = [] if (direction == "Vertical" or direction == "Both"): r = range(0, newHeight) for x in range(0, newWidth): r = r[::-1] fPrec = -1 for y in r: f = int(feedMin + ((feedMax - feedMin) * gMap[x][y] / 255.0)) if (f != fPrec or y == 0 or y == newHeight - 1): xH.append(x * toolSize) yH.append((newHeight - y) * toolSize) fH.append(f) fPrec = f #Create points for horizontal scan xV = [] yV = [] fV = [] if (direction == "Horizontal" or direction == "Both"): r = range(0, newWidth) for y in reversed(range(0, newHeight)): fPrec = -1 for x in r: f = int(feedMin + ((feedMax - feedMin) * gMap[x][y] / 255.0)) if (f != fPrec or x == 0 or x == newWidth - 1): xV.append(x * toolSize) yV.append((newHeight - y) * toolSize) fV.append(f) fPrec = f r = r[::-1] #Gcode Horizontal if (len(xH) > 1 and len(yH) > 1): block.append(CNC.zsafe()) block.append(CNC.grapid(xH[0], yH[0])) block.append(CNC.zenter(depth)) for x, y, f in zip(xH, yH, fH): v = (x, y, depth) block.append(CNC.glinev(1, v, f)) #Gcode Vertical if (len(xV) > 1 and len(yV) > 1): block.append(CNC.zsafe()) block.append(CNC.grapid(xV[0], yV[0])) block.append(CNC.zenter(depth)) for x, y, f in zip(xV, yV, fV): v = (x, y, depth) block.append(CNC.glinev(1, v, f)) #Draw Border if required if drawBorder: block.append(CNC.zsafe()) block.append(CNC.grapid(0, 0)) block.append(CNC.zenter(depth)) block.append(CNC.gcode(1, [("f", feedMin)])) block.append(CNC.gline(newWidth * toolSize - toolSize, 0)) block.append( CNC.gline(newWidth * toolSize - toolSize, newHeight * toolSize)) block.append(CNC.gline(0, newHeight * toolSize)) block.append(CNC.gline(0, 0)) #Gcode Zsafe block.append(CNC.zsafe()) blocks.append(block) active = app.activeBlock() if active == 0: active = 1 app.gcode.insBlocks(active, blocks, "Pyrograph") app.refresh() app.setStatus( _("Generated Pyrograph W=%g x H=%g x D=%g") % (newWidth * toolSize, newHeight * toolSize, depth))
def execute(self, app): if Image is None: app.setStatus(_("Halftone abort: This plugin requires PIL/Pillow to read image data")) return n = self["name"] if not n or n=="default": n="Halftone" #Calc desired size channel = self["Channel"] invert = self["Invert"] drawSize = self["DrawSize"] cellSize = self["CellSize"] dMax = self["DiameterMax"] dMin = self["DiameterMin"] angle = self["Angle"] drawBorder = self["DrawBorder"] depth = self["Depth"] conical = self["Conical"] #Check parameters if drawSize < 1: app.setStatus(_("Halftone abort: Size too small to draw anything!")) return if dMin > dMax: app.setStatus(_("Halftone abort: Minimum diameter must be minor then Maximum")) return if dMax < 1: app.setStatus(_("Halftone abort: Maximum diameter too small")) return if cellSize < 1: app.setStatus(_("Halftone abort: Cell size too small")) return tool = app.tools["EndMill"] tool_shape = tool["shape"] if conical: if tool_shape== "V-cutting": try: v_angle = float(tool["angle"]) except: app.setStatus(_("Halftone abort: Angle in V-Cutting end mill is missing")) return else: app.setStatus(_("Halftone abort: Conical path need V-Cutting end mill")) return #Open picture file fileName = self["File"] try: img = Image.open(fileName) except: app.setStatus(_("Halftone abort: Can't read image file")) return #Create a scaled image to work faster with big image and better with small ones squareNorm = True if channel == 'Blue(sqrt)': img = img.convert('RGB') img = img.split()[0] elif channel == 'Green(sqrt)': img = img.convert('RGB') img = img.split()[1] elif channel == 'Red(sqrt)': img = img.convert('RGB') img = img.split()[2] else: img = img.convert ('L') #to calculate luminance squareNorm = False #flip image to ouput correct coordinates img = img.transpose(Image.FLIP_TOP_BOTTOM) #Calc divisions for halftone divisions = drawSize / cellSize #Get image size self.imgWidth, self.imgHeight = img.size if (self.imgWidth > self.imgHeight): scale = drawSize / float(self.imgWidth) sample = int(self.imgWidth / divisions) else: scale = drawSize / float(self.imgHeight) sample = int(self.imgHeight / divisions) self.ratio = scale #Halftone circles = self.halftone(img, sample, scale, angle, squareNorm, invert) #Init blocks blocks = [] #Border block if drawBorder: block = Block("%s-border"%(self.name)) block.append(CNC.zsafe()) block.append(CNC.grapid(0,0)) block.append(CNC.zenter(depth)) block.append(CNC.gcode(1, [("f",CNC.vars["cutfeed"])])) block.append(CNC.gline(self.imgWidth * self.ratio, 0)) block.append(CNC.gline(self.imgWidth * self.ratio, self.imgHeight*self.ratio)) block.append(CNC.gline(0, self.imgHeight*self.ratio)) block.append(CNC.gline(0,0)) blocks.append(block) #Draw block block = Block(self.name) #Change color if channel == 'Blue(sqrt)': block.color = "#0000ff" elif channel == 'Green(sqrt)': block.color = "#00ff00" elif channel == 'Red(sqrt)': block.color = "#ff0000" block.append("(Halftone size W=%d x H=%d x D=%d ,Total points:%i)" % (self.imgWidth * self.ratio, self.imgHeight * self.ratio, depth, len(circles))) block.append("(Channel = %s)" % channel) for c in circles: x,y,r = c r = min(dMax/2.0,r) if (r >= dMin/2.): block.append(CNC.zsafe()) block.append(CNC.grapid(x+r,y)) block.append(CNC.zenter(depth)) block.append(CNC.garc(CW,x+r,y,i=-r,)) block.append(CNC.zsafe()) if conical: block.enable = False blocks.append(block) if conical: blockCon = Block("%s-Conical"%(self.name)) for c in circles: x,y,r = c blockCon.append(CNC.zsafe()) blockCon.append(CNC.grapid(x,y)) dv = r / math.tan(math.radians(v_angle/2.)) blockCon.append(CNC.zenter(-dv)) blockCon.append(CNC.zsafe()) blocks.append(blockCon) #Gcode Zsafe active = app.activeBlock() app.gcode.insBlocks(active, blocks, "Halftone") app.refresh() app.setStatus(_("Generated Halftone size W=%d x H=%d x D=%d ,Total points:%i" % (self.imgWidth * self.ratio, self.imgHeight * self.ratio, depth, len(circles))))
def execute(self, app): if Image is None: app.setStatus( _("Halftone abort: This plugin requires PIL/Pillow to read image data" )) return n = self["name"] if not n or n == "default": n = "Halftone" # Calc desired size channel = self["Channel"] invert = self["Invert"] drawSize = self["DrawSize"] cellSize = self["CellSize"] dMax = self["DiameterMax"] dMin = self["DiameterMin"] angle = self["Angle"] drawBorder = self["DrawBorder"] depth = self["Depth"] conical = self["Conical"] # Check parameters if drawSize < 1: app.setStatus( _("Halftone abort: Size too small to draw anything!")) return if dMin > dMax: app.setStatus( _("Halftone abort: Minimum diameter must be minor then Maximum" )) return if dMax < 1: app.setStatus(_("Halftone abort: Maximum diameter too small")) return if cellSize < 1: app.setStatus(_("Halftone abort: Cell size too small")) return tool = app.tools["EndMill"] tool_shape = tool["shape"] if conical: if tool_shape == "V-cutting": try: v_angle = float(tool["angle"]) except: app.setStatus( _("Halftone abort: Angle in V-Cutting end mill is missing" )) return else: app.setStatus( _("Halftone abort: Conical path need V-Cutting end mill")) return # Open picture file fileName = self["File"] try: img = Image.open(fileName) except: app.setStatus(_("Halftone abort: Can't read image file")) return # Create a scaled image to work faster with big image and better with small ones squareNorm = True if channel == 'Blue(sqrt)': img = img.convert('RGB') img = img.split()[0] elif channel == 'Green(sqrt)': img = img.convert('RGB') img = img.split()[1] elif channel == 'Red(sqrt)': img = img.convert('RGB') img = img.split()[2] else: img = img.convert('L') # to calculate luminance squareNorm = False # flip image to ouput correct coordinates img = img.transpose(Image.FLIP_TOP_BOTTOM) # Calc divisions for halftone divisions = drawSize / cellSize # Get image size self.imgWidth, self.imgHeight = img.size if (self.imgWidth > self.imgHeight): scale = drawSize / float(self.imgWidth) sample = int(self.imgWidth / divisions) else: scale = drawSize / float(self.imgHeight) sample = int(self.imgHeight / divisions) self.ratio = scale # Halftone circles = self.halftone(img, sample, scale, angle, squareNorm, invert) # Init blocks blocks = [] # Border block if drawBorder: block = Block("%s-border" % (self.name)) block.append(CNC.zsafe()) block.append(CNC.grapid(0, 0)) block.append(CNC.zenter(depth)) block.append(CNC.gcode(1, [("f", CNC.vars["cutfeed"])])) block.append(CNC.gline(self.imgWidth * self.ratio, 0)) block.append( CNC.gline(self.imgWidth * self.ratio, self.imgHeight * self.ratio)) block.append(CNC.gline(0, self.imgHeight * self.ratio)) block.append(CNC.gline(0, 0)) blocks.append(block) # Draw block block = Block(self.name) # Change color if channel == 'Blue(sqrt)': block.color = "#0000ff" elif channel == 'Green(sqrt)': block.color = "#00ff00" elif channel == 'Red(sqrt)': block.color = "#ff0000" block.append("(Halftone size W=%d x H=%d x D=%d ,Total points:%i)" % (self.imgWidth * self.ratio, self.imgHeight * self.ratio, depth, len(circles))) block.append("(Channel = %s)" % channel) for c in circles: x, y, r = c r = min(dMax / 2.0, r) if (r >= dMin / 2.): block.append(CNC.zsafe()) block.append(CNC.grapid(x + r, y)) block.append(CNC.zenter(depth)) block.append(CNC.garc( CW, x + r, y, i=-r, )) block.append(CNC.zsafe()) if conical: block.enable = False blocks.append(block) if conical: blockCon = Block("%s-Conical" % (self.name)) for c in circles: x, y, r = c blockCon.append(CNC.zsafe()) blockCon.append(CNC.grapid(x, y)) dv = r / math.tan(math.radians(v_angle / 2.)) blockCon.append(CNC.zenter(-dv)) blockCon.append(CNC.zsafe()) blocks.append(blockCon) # Gcode Zsafe active = app.activeBlock() app.gcode.insBlocks(active, blocks, "Halftone") app.refresh() app.setStatus( _("Generated Halftone size W=%d x H=%d x D=%d ,Total points:%i" % (self.imgWidth * self.ratio, self.imgHeight * self.ratio, depth, len(circles))))
def make(self,app, XStart=0.0, YStart=0.0, FlatWidth=10., FlatHeight=10., \ FlatDepth=0, BorderPass=False, CutDirection="Climb", PocketType="Raster"): #GCode Blocks blocks = [] #Check parameters if CutDirection == "": app.setStatus(_("Flatten abort: Cut Direction is undefined")) return if PocketType == "": app.setStatus(_("Flatten abort: Pocket Type is undefined")) return if FlatWidth <= 0 or FlatHeight <= 0: app.setStatus( _("Flatten abort: Flatten Area dimensions must be > 0")) return if FlatDepth > 0: app.setStatus( _("Flatten abort: Hey this is only for subtractive machine! Check depth!" )) return #Add Region disabled to show worked area block = Block(self.name + " Outline") block.enable = False block.append(CNC.zsafe()) xR, yR = self.RectPath(XStart, YStart, FlatWidth, FlatHeight) for x, y in zip(xR, yR): block.append(CNC.gline(x, y)) blocks.append(block) # Load tool and material settings toolDiam = CNC.vars['diameter'] toolRadius = toolDiam / 2. #Calc tool diameter with Maximum Step Over allowed StepOverInUnitMax = toolDiam * CNC.vars['stepover'] / 100.0 #Offset for Border Cut BorderXStart = XStart + toolRadius BorderYStart = YStart + toolRadius BorderWidth = FlatWidth - toolDiam BorderHeight = FlatHeight - toolDiam BorderXEnd = XStart + FlatWidth - toolRadius BorderYEnd = YStart + FlatHeight - toolRadius PocketXStart = BorderXStart PocketYStart = BorderYStart PocketXEnd = BorderXEnd PocketYEnd = BorderYEnd #Calc space to work with/without border cut WToWork = FlatWidth - toolDiam HToWork = FlatHeight - toolDiam if (WToWork < toolRadius or HToWork < toolRadius): app.setStatus( _("Flatten abort: Flatten area is too small for this End Mill." )) return #Prepare points for pocketing xP = [] yP = [] #and border xB = [] yB = [] #--------------------------------------------------------------------- #Raster approach if PocketType == "Raster": #Correct sizes if border is used if (BorderPass): PocketXStart += StepOverInUnitMax PocketYStart += StepOverInUnitMax PocketXEnd -= StepOverInUnitMax PocketYEnd -= StepOverInUnitMax WToWork -= (StepOverInUnitMax) HToWork -= (StepOverInUnitMax) #Calc number of pass VerticalCount = (int)(HToWork / StepOverInUnitMax) #Calc step minor of Max step StepOverInUnit = HToWork / (VerticalCount + 1) flip = False ActualY = PocketYStart #Zig zag if StepOverInUnit == 0: StepOverInUnit = 0.001 #avoid infinite while loop while (True): #Zig xP.append(self.ZigZag(flip, PocketXStart, PocketXEnd)) yP.append(ActualY) flip = not flip #Zag xP.append(self.ZigZag(flip, PocketXStart, PocketXEnd)) yP.append(ActualY) if (ActualY >= PocketYEnd - StepOverInUnitMax + StepOverInUnit): break #Up ActualY += StepOverInUnit xP.append(self.ZigZag(flip, PocketXStart, PocketXEnd)) yP.append(ActualY) #Points for border cut depends on Zig/Zag end if (BorderPass): if flip: xB, yB = self.RectPath(BorderXStart, BorderYEnd, BorderWidth, -BorderHeight) else: xB, yB = self.RectPath(BorderXEnd, BorderYEnd, -BorderWidth, -BorderHeight) #Reverse in case of Climb if CutDirection == "Climb": xB = xB[::-1] yB = yB[::-1] #--------------------------------------------------------------------- #Offset approach if PocketType == "Offset": #Calc number of pass VerticalCount = (int)(HToWork / StepOverInUnitMax) HorrizontalCount = (int)(WToWork / StepOverInUnitMax) #Make them odd if VerticalCount % 2 == 0: VerticalCount += 1 if HorrizontalCount % 2 == 0: HorrizontalCount += 1 #Calc step minor of Max step StepOverInUnitH = HToWork / (VerticalCount) StepOverInUnitW = WToWork / (HorrizontalCount) #Start from border to center xS = PocketXStart yS = PocketYStart wS = WToWork hS = HToWork xC = 0 yC = 0 while (xC <= HorrizontalCount / 2 and yC <= VerticalCount / 2): #Pocket offset points xO, yO = self.RectPath(xS, yS, wS, hS) if CutDirection == "Conventional": xO = xO[::-1] yO = yO[::-1] xP = xP + xO yP = yP + yO xS += StepOverInUnitH yS += StepOverInUnitW hS -= 2.0 * StepOverInUnitH wS -= 2.0 * StepOverInUnitW xC += 1 yC += 1 #Reverse point to start from inside (less stress on the tool) xP = xP[::-1] yP = yP[::-1] #Blocks for pocketing block = Block(self.name) block.append("(Flatten from X=%g Y=%g)" % (XStart, YStart)) block.append("(W=%g x H=%g x D=%g)" % (FlatWidth, FlatHeight, FlatDepth)) block.append("(Approach: %s %s)" % (PocketType, CutDirection)) if BorderPass: block.append("(with border)") #Move safe to first point block.append(CNC.zsafe()) block.append(CNC.grapid(xP[0], yP[0])) #Init Depth currDepth = 0. stepz = CNC.vars['stepz'] if stepz == 0: stepz = 0.001 #avoid infinite while loop #Create GCode from points while True: currDepth -= stepz if currDepth < FlatDepth: currDepth = FlatDepth block.append(CNC.zenter(currDepth)) block.append(CNC.gcode(1, [("f", CNC.vars["cutfeed"])])) #Pocketing lastxy = None for x, y in zip(xP, yP): # block.append(CNC.gline(x,y)) if lastxy != CNC.gline(x, y) or None: block.append(CNC.gline(x, y)) lastxy = CNC.gline(x, y) #Border cut if request for x, y in zip(xB, yB): block.append(CNC.gline(x, y)) #Verify exit condition if currDepth <= FlatDepth: break #Move to the begin in a safe way block.append(CNC.zsafe()) block.append(CNC.grapid(xP[0], yP[0])) #Zsafe block.append(CNC.zsafe()) blocks.append(block) return blocks