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 calc(self, N, phi, Pc): N = abs(N) # Pitch Circle D = N * Pc / math.pi R = D / 2.0 # Diametrical pitch Pd = N / D # Base Circle Db = D * math.cos(phi) Rb = Db / 2.0 # Addendum a = 1.0 / Pd # Outside Circle Ro = R + a Do = 2.0 * Ro # Tooth thickness T = math.pi * D / (2 * N) # undercut? U = 2.0 / (math.sin(phi) * (math.sin(phi))) needs_undercut = N < U # sys.stderr.write("N:%s R:%s Rb:%s\n" % (N,R,Rb)) # Clearance c = 0.0 # Dedendum b = a + c # Root Circle Rr = R - b Dr = 2.0 * Rr two_pi = 2.0 * math.pi half_thick_angle = two_pi / (4.0 * N) pitch_to_base_angle = self.involute_intersect_angle(Rb, R) pitch_to_outer_angle = self.involute_intersect_angle( Rb, Ro) # pitch_to_base_angle points = [] for x in range(1, N + 1): c = x * two_pi / N # angles pitch1 = c - half_thick_angle base1 = pitch1 - pitch_to_base_angle outer1 = pitch1 + pitch_to_outer_angle pitch2 = c + half_thick_angle base2 = pitch2 + pitch_to_base_angle outer2 = pitch2 - pitch_to_outer_angle # points b1 = self.point_on_circle(Rb, base1) p1 = self.point_on_circle(R, pitch1) o1 = self.point_on_circle(Ro, outer1) o2 = self.point_on_circle(Ro, outer2) p2 = self.point_on_circle(R, pitch2) b2 = self.point_on_circle(Rb, base2) if Rr >= Rb: pitch_to_root_angle = pitch_to_base_angle - self.involute_intersect_angle( Rb, Rr) root1 = pitch1 - pitch_to_root_angle root2 = pitch2 + pitch_to_root_angle r1 = self.point_on_circle(Rr, root1) r2 = self.point_on_circle(Rr, root2) points.append(r1) points.append(p1) points.append(o1) points.append(o2) points.append(p2) points.append(r2) else: r1 = self.point_on_circle(Rr, base1) r2 = self.point_on_circle(Rr, base2) points.append(r1) points.append(b1) points.append(p1) points.append(o1) points.append(o2) points.append(p2) points.append(b2) points.append(r2) first = points[0] del points[0] blocks = [] block = Block(self.name) blocks.append(block) block.append(CNC.grapid(first.x(), first.y())) block.append(CNC.zenter(0.0)) #print first.x(), first.y() for v in points: block.append(CNC.gline(v.x(), v.y())) #print v.x(), v.y() #print first.x(), first.y() block.append(CNC.gline(first.x(), first.y())) block.append(CNC.zsafe()) #block = Block("%s-center"%(self.name)) block = Block("%s-basecircle" % (self.name)) block.enable = False block.append(CNC.grapid(Db / 2, 0.)) block.append(CNC.zenter(0.0)) block.append(CNC.garc(CW, Db / 2, 0., i=-Db / 2)) block.append(CNC.zsafe()) blocks.append(block) return blocks
def execute(self, app): n = self["name"] if not n or n == "default": n = "Pyrograph" #Import parameters toolSize = self.fromMm("ToolSize") if toolSize <= 0: app.setStatus(_("Pyrograph abort: Tool Size must be > 0")) return filename = self["File"] #Open gcode file if not (filename == ""): app.load(filename) inOut = self["In-Out"] xadd = self["xAdd"] yadd = self["yAdd"] if xadd == "": xadd = 1 if yadd == "": yadd = 1 #Create the external box if inOut == "Out": box = Block("Box") external_box = [] box.append( CNC.grapid(CNC.vars["xmin"] - xadd, CNC.vars["ymin"] - yadd)) box.append( CNC.gline(CNC.vars["xmin"] - xadd, CNC.vars["ymax"] + yadd)) box.append( CNC.gline(CNC.vars["xmax"] + xadd, CNC.vars["ymax"] + yadd)) box.append( CNC.gline(CNC.vars["xmax"] + xadd, CNC.vars["ymin"] - yadd)) box.append( CNC.gline(CNC.vars["xmin"] - xadd, CNC.vars["ymin"] - yadd)) #Insert the external block external_box.append(box) app.gcode.insBlocks(1, external_box, "External_Box") app.refresh() #Value for creating an offset from the margins of the gcode margin = self.fromMm( "Margin" ) #GIVING RANDOM DECIMALS SHOULD AVOID COINCIDENT SEGMENTS BETWEEN ISLAND AND BASE PATHS THAT CONFUSE THE ALGORITHM. WORKS IN MOST CASES. #Add and subtract the margin max_x = CNC.vars["xmax"] + margin min_x = CNC.vars["xmin"] - margin max_y = CNC.vars["ymax"] + margin min_y = CNC.vars["ymin"] - margin #Difference between offtset margins dx = max_x - min_x dy = max_y - min_y #Number of vertical divisions according to the toolsize divisions = dy / toolSize #Distance between horizontal lines step_y = toolSize n_steps_y = int(divisions) + 1 #Create the snake pattern according to the number of divisions pattern = Block(self.name) pattern_base = [] for n in range(n_steps_y): if n == 0: pattern.append(CNC.grapid(min_x, min_y)) #go to bottom left else: y0 = min_y + step_y * (n - 1) y1 = min_y + step_y * (n) if not (n % 2 == 0): pattern.append(CNC.glinev( 1, [max_x, y0])) #write odd lines from left to right pattern.append(CNC.grapid(max_x, y1)) else: pattern.append(CNC.glinev( 1, [min_x, y0])) #write even lines from right to left pattern.append(CNC.grapid(min_x, y1)) #Insert the pattern block pattern_base.append(pattern) app.gcode.insBlocks(1, pattern_base, "pattern") app.refresh() #Mark pattern as island for bid in pattern_base: app.gcode.island([1]) #Select all blocks app.editor.selectAll() paths_base = [] paths_isl = [] points = [] #Compare blocks to separate island from other blocks for bid in app.editor.getSelectedBlocks(): if app.gcode[bid].operationTest('island'): paths_isl.extend(app.gcode.toPath(bid)) else: paths_base.extend(app.gcode.toPath(bid)) #Make intersection between blocks while len(paths_base) > 0: base = paths_base.pop() for island in paths_isl: #points.extend(base.intersectPath(island)) points.extend(island.intersectPath(base)) ###SORTING POINTS### x = [] y = [] #Get (x,y) intersection points for i in range(len(points)): x.append(points[i][2][0]) y.append(points[i][2][1]) #Save (x,y) intersection points in a matrix matrix = [[0 for i in range(2)] for j in range(len(x))] for i in range(len(x)): matrix[i][0] = x[i] matrix[i][1] = y[i] # for i in range(len(x)): # print('puntos',points[i][0],points[i][1],matrix[i][0], matrix[i][1]) #print(matrix) #Sort points in increasing y coordinate matrix.sort(key=itemgetter(1, 0), reverse=False) # for i in range(len(x)): # print('puntos',points[i][0],points[i][1],matrix[i][0], matrix[i][1]) #print(matrix) index = 0 pair = 0 new_matrix = [] for i in range(len(x)): if i == 0: index = index + 1 elif i < len(x) - 1: if matrix[i][1] == matrix[i - 1][1]: index = index + 1 else: if pair % 2 == 0: logic = False else: logic = True submatrix = matrix[i - index:i] submatrix.sort(key=itemgetter(0, 1), reverse=logic) new_matrix.extend(submatrix) index = 1 pair = pair + 1 else: #print('entered') if pair % 2 == 0: logic = False else: logic = True if matrix[i][1] == matrix[i - 1][1]: submatrix = matrix[i - index:] submatrix.sort(key=itemgetter(0, 1), reverse=logic) new_matrix.extend(submatrix) else: index = 1 submatrix = matrix[-1] new_matrix.extend(submatrix) # for i in range(len(x)): # print('puntos',new_matrix[i][0], new_matrix[i][1]) # for i in range(len(x)): # print('puntos',new_matrix[i][0], new_matrix[i][1]) #print(x, y) ###SORTING POINTS END### #Generate the gcode from points obtained blocks = [] block = Block(self.name) for i in range(len(x)): if i == 0: block.append(CNC.grapid(new_matrix[0][0], new_matrix[0][1])) inside = 0 else: if inside == 0: block.append(CNC.gline(new_matrix[i][0], new_matrix[i][1])) inside = 1 else: block.append(CNC.grapid(new_matrix[i][0], new_matrix[i][1])) inside = 0 # for i in range(len(x)): # print('puntos',x[i], y[i]) blocks.append(block) app.gcode.delBlockUndo(1) app.gcode.insBlocks(1, blocks, "Line to Line Burning") app.editor.disable() for block in blocks: block.enable = 1 #app.editor.enable() #app.editor.unselectAll() app.refresh() app.setStatus(_("Generated Line to Line Burning"))
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 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 calc(self, N, phi, Pc): N = abs(N) # Pitch Circle D = N * Pc / math.pi R = D / 2.0 # Diametrical pitch Pd = N / D # Base Circle Db = D * math.cos(phi) Rb = Db / 2.0 # Addendum a = 1.0 / Pd # Outside Circle Ro = R + a Do = 2.0 * Ro # Tooth thickness T = math.pi*D / (2*N) # undercut? U = 2.0 / (math.sin(phi) * (math.sin(phi))) needs_undercut = N < U # sys.stderr.write("N:%s R:%s Rb:%s\n" % (N,R,Rb)) # Clearance c = 0.0 # Dedendum b = a + c # Root Circle Rr = R - b Dr = 2.0*Rr two_pi = 2.0*math.pi half_thick_angle = two_pi / (4.0*N) pitch_to_base_angle = self.involute_intersect_angle(Rb, R) pitch_to_outer_angle = self.involute_intersect_angle(Rb, Ro) # pitch_to_base_angle points = [] for x in range(1,N+1): c = x * two_pi / N # angles pitch1 = c - half_thick_angle base1 = pitch1 - pitch_to_base_angle outer1 = pitch1 + pitch_to_outer_angle pitch2 = c + half_thick_angle base2 = pitch2 + pitch_to_base_angle outer2 = pitch2 - pitch_to_outer_angle # points b1 = self.point_on_circle(Rb, base1) p1 = self.point_on_circle(R, pitch1) o1 = self.point_on_circle(Ro, outer1) o2 = self.point_on_circle(Ro, outer2) p2 = self.point_on_circle(R, pitch2) b2 = self.point_on_circle(Rb, base2) if Rr >= Rb: pitch_to_root_angle = pitch_to_base_angle - self.involute_intersect_angle(Rb, Rr) root1 = pitch1 - pitch_to_root_angle root2 = pitch2 + pitch_to_root_angle r1 = self.point_on_circle(Rr, root1) r2 = self.point_on_circle(Rr, root2) points.append(r1) points.append(p1) points.append(o1) points.append(o2) points.append(p2) points.append(r2) else: r1 = self.point_on_circle(Rr, base1) r2 = self.point_on_circle(Rr, base2) points.append(r1) points.append(b1) points.append(p1) points.append(o1) points.append(o2) points.append(p2) points.append(b2) points.append(r2) first = points[0] del points[0] blocks = [] block = Block(self.name) blocks.append(block) block.append(CNC.grapid(first.x(), first.y())) block.append(CNC.zenter(0.0)) #print first.x(), first.y() for v in points: block.append(CNC.gline(v.x(), v.y())) #print v.x(), v.y() #print first.x(), first.y() block.append(CNC.gline(first.x(), first.y())) block.append(CNC.zsafe()) #block = Block("%s-center"%(self.name)) block = Block("%s-basecircle"%(self.name)) block.enable = False block.append(CNC.grapid(Db/2, 0.)) block.append(CNC.zenter(0.0)) block.append(CNC.garc(CW, Db/2, 0., i=-Db/2)) block.append(CNC.zsafe()) blocks.append(block) return blocks
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
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): 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): 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))