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 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 execute(self, app): feed = self["feed"] zfeed = CNC.vars["cutfeedz"] rpm = self["rpm"] if self["zfeed"]: zfeed = self["zfeed"] zup = self["zup"] surface = CNC.vars["surface"] # zbeforecontact=surface+CNC.vars["zretract"] # hardcrust = surface - CNC.vars["hardcrust"] # feedbeforecontact = CNC.vars["feedbeforecontact"]/100.0 # hardcrustfeed = CNC.vars["hardcrustfeed"]/100.0 # Get selected blocks from editor selBlocks = app.editor.getSelectedBlocks() if not selBlocks: app.setStatus(_("Scaling 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 blocks = [] n = self["name"] # if not n or n=="default": n="Trochoidal_3D" n = "Scaling" scal_block = Block(n) for idx, segm in enumerate(bidSegment): if idx >= 0: if idx == 0: scal_block.append("M03") scal_block.append("S " + str(rpm)) scal_block.append(CNC.zsafe()) scal_block.append("F " + str(feed)) scal_block.append( "(--------------------------------------------------)") B = self.scaling(segm) if segm[0][2] > surface and segm[1][2] >= surface: scal_block.append("g0 x " + str(B[0]) + " y " + str(B[1]) + " z " + str(B[2])) else: scal_block.append("g1 x " + str(B[0]) + " y " + str(B[1]) + " z " + str(B[2])) scal_block.append(CNC.zsafe( )) #<<< Move rapid Z axis to the safe height in Stock Material blocks.append(scal_block) self.finish_blocks(app, 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 generate(self, board_width, board_height, number_of_pieces, random_seed=0, tap_shape='basic', threshold=3.0): blocks = [] block = Block(self.name) random.seed(random_seed) Arc.reset_used_arcs() Arc.set_diff_threshold(threshold) puzzle_cuts = self.__class__.make_puzzle_cuts(board_width, board_height, number_of_pieces, tap_shape, threshold) # Draw puzzle cuts x = 0 y = 0 for i in range(0, int(self.thickness / self.step_z)): for cut in puzzle_cuts: block.append(CNC.zsafe()) block.append(CNC.grapid(x + cut[0].x, y + cut[0].y)) block.append(CNC.zenter(0.0)) block.append(CNC.fmt("f", self.cut_feed)) block.append(CNC.zenter(-(i + 1) * self.step_z)) for arc in cut: if arc.r: block.append( CNC.garc(arc.direction, x + arc.x, y + arc.y, r=arc.r)) blocks.append(block) # Draw border block = Block(self.name + "_border") block.append(CNC.zsafe()) block.append(CNC.grapid(x, y)) for i in range(0, int(self.thickness / self.step_z)): block.append(CNC.fmt("f", self.cut_feed)) block.append(CNC.zenter(-(i + 1) * self.step_z)) block.append(CNC.gline(x + board_width, y)) block.append(CNC.gline(x + board_width, y + board_height)) block.append(CNC.gline(x, y + board_height)) block.append(CNC.gline(x, y)) block.append(CNC.zsafe()) blocks.append(block) return blocks
def execute(self, app): feed = self["feed"] zfeed = CNC.vars["cutfeedz"] rpm = self["rpm"] if self["zfeed"]: zfeed = self["zfeed"] zup = self["zup"] surface = CNC.vars["surface"] # zbeforecontact=surface+CNC.vars["zretract"] # hardcrust = surface - CNC.vars["hardcrust"] # feedbeforecontact = CNC.vars["feedbeforecontact"]/100.0 # hardcrustfeed = CNC.vars["hardcrustfeed"]/100.0 # Get selected blocks from editor selBlocks = app.editor.getSelectedBlocks() if not selBlocks: app.setStatus(_("Scaling 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 blocks = [] n = self["name"] # if not n or n=="default": n="Trochoidal_3D" n="Scaling" scal_block = Block(n) for idx, segm in enumerate(bidSegment): if idx >= 0: if idx == 0: scal_block.append("M03") scal_block.append("S "+str(rpm)) scal_block.append(CNC.zsafe()) scal_block.append("F "+str(feed)) scal_block.append("(--------------------------------------------------)") B=self.scaling(segm) if segm[0][2]> surface and segm[1][2]>=surface: scal_block.append("g0 x "+str(B[0])+" y "+str(B[1])+ " z "+str(B[2])) else: scal_block.append("g1 x "+str(B[0])+" y "+str(B[1])+ " z "+str(B[2])) scal_block.append(CNC.zsafe()) #<<< Move rapid Z axis to the safe height in Stock Material blocks.append(scal_block) self.finish_blocks(app, blocks)
def insertBlock(self, event=None): active = self.index(ACTIVE) if self._items: bid, lid = self._items[active] bid += 1 else: bid = 0 block = Block() block.expand = True block.append("G0 X0 Y0") block.append("G1 Z0") block.append(CNC.zsafe()) self.gcode.addUndo(self.gcode.addBlockUndo(bid,block)) self.selection_clear(0,END) self.fill() # find location of new block while active < self.size(): if self._items[active][0] == bid: break active += 1 self.selection_set(active) self.see(active) self.activate(active) self.edit() self.app.event_generate("<<Modified>>")
def insertBlock(self, event=None): active = self.index(ACTIVE) if self._items: bid, lid = self._items[active] bid += 1 else: bid = 0 block = Block() block.expand = True block.append("g0 x0 y0") block.append("g1 z0") block.append(CNC.zsafe()) self.gcode.addUndo(self.gcode.addBlockUndo(bid,block)) self.selection_clear(0,END) self.fill() # find location of new block while active < self.size(): if self._items[active][0] == bid: break active += 1 self.selection_set(active) self.see(active) self.activate(active) self.edit() self.winfo_toplevel().event_generate("<<Modified>>")
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 generate(self, board_width, board_height, number_of_pieces, random_seed = 0, tap_shape = 'basic', threshold = 3.0): blocks = [] block = Block(self.name) random.seed(random_seed) Arc.reset_used_arcs() Arc.set_diff_threshold(threshold) puzzle_cuts = self.__class__.make_puzzle_cuts(board_width, board_height, number_of_pieces, tap_shape, threshold) # Draw puzzle cuts x = 0 y = 0 for i in range(0, int(self.thickness / self.step_z)): for cut in puzzle_cuts: block.append(CNC.zsafe()) block.append(CNC.grapid(x + cut[0].x, y + cut[0].y)) block.append(CNC.zenter(0.0)) block.append(CNC.fmt("f", self.cut_feed)) block.append(CNC.zenter(-(i + 1) * self.step_z)) for arc in cut: if arc.r: block.append(CNC.garc(arc.direction, x + arc.x, y + arc.y, r=arc.r)) blocks.append(block) # Draw border block = Block(self.name + "_border") block.append(CNC.zsafe()) block.append(CNC.grapid(x, y)) for i in range(0, int(self.thickness / self.step_z)): block.append(CNC.fmt("f",self.cut_feed)) block.append(CNC.zenter(-(i + 1) * self.step_z)) block.append(CNC.gline(x + board_width, y)) block.append(CNC.gline(x + board_width, y + board_height)) block.append(CNC.gline(x, y + board_height)) block.append(CNC.gline(x, y)) 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 _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 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 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 calc(self, D, res, pocket): blocks = [] block = Block(self.name) # Load tool and material settings toolDiam = CNC.vars['diameter'] toolRadius = toolDiam/2. stepz = CNC.vars['stepz'] stepxy = toolDiam*(CNC.vars['stepover']/100.) if toolDiam <= 0 or stepxy <= 0 or stepz <= 0 or D <= 0 or res <= 0: return blocks currDepth = 0. def setCutFeedrate(): block.append(CNC.gcode(1, [("f",CNC.vars["cutfeed"])])) def addCircumference(radius): block.append(CNC.garc(2,radius, 0., i=-radius)) # Mills a circle, pocketing it if needed def addSingleCircle(radius, depth): if pocket: block.append(CNC.grapid(0., 0.)) block.append(CNC.zenter(depth)) setCutFeedrate() currRadius = 0. while radius > currRadius+stepxy: currRadius += stepxy block.append(CNC.gline(currRadius, 0)) addCircumference(currRadius) if radius-currRadius > 0: block.append(CNC.gline(radius, 0)) addCircumference(radius) else: block.append(CNC.grapid(radius, 0.)) block.append(CNC.zenter(depth)) setCutFeedrate() addCircumference(radius) # Mills a circle in steps of height "stepz" def addCircle(radius, depth, currDepth): while depth < currDepth-stepz: currDepth -= stepz addSingleCircle(radius, currDepth) if currDepth-depth > 0: addSingleCircle(radius, depth) return depth block.append(CNC.zsafe()) r = D/2. r -= toolRadius # Removes offset of ball-end tool angleInc = res currAngle = 0. angle = math.pi/2. # 90 degrees while angle > currAngle+angleInc: currAngle += angleInc radius = r * math.cos(-currAngle) # Removes vertical offset (centers the ball tool in Z=0, rather than the tip) depth = r * math.sin(-currAngle) - toolRadius currDepth = addCircle(radius, depth, currDepth) if angle-currAngle > 0: radius = r * math.cos(-angle) depth = r * math.sin(-angle) - toolRadius currDepth = addCircle(radius, depth, currDepth) blocks.append(block) return blocks
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 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 calc(self, D, res, pocket): blocks = [] block = Block(self.name) # Load tool and material settings toolDiam = CNC.vars['diameter'] toolRadius = toolDiam/2. stepz = CNC.vars['stepz'] stepxy = toolDiam*(CNC.vars['stepover']/100.) if toolDiam <= 0 or stepxy <= 0 or stepz <= 0 or D <= 0 or res <= 0: return blocks currDepth = 0. def setCutFeedrate(): block.append(CNC.gcode(1, [("f",CNC.vars["cutfeed"])])) def addCircumference(radius): block.append(CNC.garc(2,radius, 0., i=-radius)) # Mills a circle, pocketing it if needed def addSingleCircle(radius, depth): if pocket: block.append(CNC.grapid(0., 0.)) block.append(CNC.zenter(depth)) setCutFeedrate() currRadius = 0. while radius > currRadius+stepxy: currRadius += stepxy block.append(CNC.gline(currRadius, 0)) addCircumference(currRadius) if radius-currRadius > 0: block.append(CNC.gline(radius, 0)) addCircumference(radius) else: block.append(CNC.grapid(radius, 0.)) block.append(CNC.zenter(depth)) setCutFeedrate() addCircumference(radius) # Mills a circle in steps of height "stepz" def addCircle(radius, depth, currDepth): while depth < currDepth-stepz: currDepth -= stepz addSingleCircle(radius, currDepth) if currDepth-depth > 0: addSingleCircle(radius, depth) return depth block.append(CNC.zsafe()) r = D/2. r -= toolRadius # Removes offset of ball-end tool angleInc = res currAngle = 0. angle = math.pi/2. # 90 degrees while angle > currAngle+angleInc: currAngle += angleInc radius = r * math.cos(-currAngle) depth = r * math.sin(-currAngle) - toolRadius # Removes vertical offset (centers the ball tool in Z=0, rather than the tip) currDepth = addCircle(radius, depth, currDepth) if angle-currAngle > 0: radius = r * math.cos(-angle) depth = r * math.sin(-angle) - toolRadius currDepth = addCircle(radius, depth, currDepth) blocks.append(block) return blocks
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): #Get inputs fontSize = self["FontSize"] depth = self["Depth"] textToWrite = self["Text"] fontFileName = self["FontFile"] imageFileName = self["ImageToAscii"] charsWidth = self["CharsWidth"] #Check parameters!!! if fontSize <= 0: app.setStatus(_("Text abort: please input a Font size > 0")) return if fontFileName == "": app.setStatus(_("Text abort: please select a font file")) return if imageFileName != "": try: textToWrite = self.asciiArt(imageFileName, charsWidth) except: pass if textToWrite == "": textToWrite = "Nel mezzo del cammin di nostra vita..." return #Init blocks blocks = [] n = self["name"] if not n or n == "default": n = "Text" block = Block(n) if (u'\n' in textToWrite): block.append("(Text:)") for line in textToWrite.splitlines(): block.append("(%s)" % line) else: block.append("(Text: %s)" % textToWrite) try: import ttf font = ttf.TruetypeInfo(fontFileName) except: app.setStatus( _("Text abort: That embarrassing, I can't read this font file!" )) return cmap = font.get_character_map() kern = None try: kern = font.get_glyph_kernings() except: pass adv = font.get_glyph_advances() xOffset = 0 yOffset = 0 glyphIndxLast = cmap[' '] for c in textToWrite: #New line if c == u'\n': xOffset = 0.0 yOffset -= 1 #offset for new line continue if c in cmap: glyphIndx = cmap[c] if (kern and (glyphIndx, glyphIndxLast) in kern): k = kern[(glyphIndx, glyphIndxLast)] #FIXME: use kern for offset?? #Get glyph contours as line segmentes and draw them gc = font.get_glyph_contours(glyphIndx) if (not gc): gc = font.get_glyph_contours( 0) #standard glyph for missing glyphs (complex glyph) if ( gc and not c == ' ' ): #FIXME: for some reason space is not mapped correctly!!! self.writeGlyphContour(block, font, gc, fontSize, depth, xOffset, yOffset) if glyphIndx < len(adv): xOffset += adv[glyphIndx] else: xOffset += 1 glyphIndxLast = glyphIndx #Remeber to close Font font.close() #Gcode Zsafe block.append(CNC.zsafe()) blocks.append(block) active = app.activeBlock() app.gcode.insBlocks(active, blocks, "Text") app.refresh() app.setStatus("Generated Text")
def execute(self, app): #Get inputs fontSize = self.fromMm("FontSize") depth = self.fromMm("Depth") textToWrite = self["Text"] fontFileName = self["FontFile"] imageFileName = self["ImageToAscii"] charsWidth = self["CharsWidth"] #Check parameters!!! if fontSize <=0: app.setStatus(_("Text abort: please input a Font size > 0")) return if fontFileName == "": app.setStatus(_("Text abort: please select a font file")) return if imageFileName != "": try: textToWrite = self.asciiArt(imageFileName,charsWidth) except: pass if textToWrite == "": textToWrite = "Nel mezzo del cammin di nostra vita..." return #Init blocks blocks = [] n = self["name"] if not n or n == "default": n = "Text" block = Block(n) if(u'\n' in textToWrite): block.append("(Text:)") for line in textToWrite.splitlines(): block.append("(%s)" % line) else: block.append("(Text: %s)" % textToWrite) try: import ttf font = ttf.TruetypeInfo(fontFileName) except: app.setStatus(_("Text abort: That embarrassing, I can't read this font file!")) return cmap = font.get_character_map() kern = None try: kern = font.get_glyph_kernings() except: pass adv = font.get_glyph_advances() xOffset = 0 yOffset = 0 glyphIndxLast = cmap[' '] for c in textToWrite: #New line if c == u'\n': xOffset = 0.0 yOffset -= 1#offset for new line continue if c in cmap: glyphIndx = cmap[c] if (kern and (glyphIndx,glyphIndxLast) in kern): k = kern[(glyphIndx,glyphIndxLast)] #FIXME: use kern for offset?? #Get glyph contours as line segmentes and draw them gc = font.get_glyph_contours(glyphIndx) if(not gc): gc = font.get_glyph_contours(0)#standard glyph for missing glyphs (complex glyph) if(gc and not c==' '): #FIXME: for some reason space is not mapped correctly!!! self.writeGlyphContour(block, font, gc, fontSize, depth, xOffset, yOffset) if glyphIndx < len(adv): xOffset += adv[glyphIndx] else: xOffset += 1 glyphIndxLast = glyphIndx #Remeber to close Font font.close() #Gcode Zsafe block.append(CNC.zsafe()) blocks.append(block) active = app.activeBlock() if active==0: active=1 app.gcode.insBlocks(active, blocks, "Text") app.refresh() app.setStatus("Generated Text")
class Tool(Plugin): __doc__ = _("Create text using a ttf font") def __init__(self, master): Plugin.__init__(self, master, "Text") self.icon = "text" self.group = "Generator" self.variables = [("name", "db" , "", _("Name")), ("Text", "text", "Write this!", _("Text to generate")), ("Depth", "mm", 0.0, _("Working Depth")), ("FontSize", "mm", 10.0, _("Font size")), ("FontFile", "file", "", _("Font file")), ("Closed", "bool", True, _("Close Contours")), ("ImageToAscii","file", "", _("Image to Ascii")), ("CharsWidth", "int", 80, _("Image chars width"))] self.buttons.append("exe") # ---------------------------------------------------------------------- def execute(self, app): #Get inputs fontSize = self.fromMm("FontSize") depth = self.fromMm("Depth") textToWrite = self["Text"] fontFileName = self["FontFile"] closed = self["Closed"] imageFileName = self["ImageToAscii"] charsWidth = self["CharsWidth"] #Check parameters!!! if fontSize <=0: app.setStatus(_("Text abort: please input a Font size > 0")) return if fontFileName == "": app.setStatus(_("Text abort: please select a font file")) return if imageFileName != "": try: textToWrite = self.asciiArt(imageFileName,charsWidth) except: pass if textToWrite == "": textToWrite = "Nel mezzo del cammin di nostra vita..." return #Init blocks blocks = [] n = self["name"] if not n or n == "default": n = "Text" block = Block(n) if(u'\n' in textToWrite): block.append("(Text:)") for line in textToWrite.splitlines(): block.append("(%s)" % line) else: block.append("(Text: %s)" % textToWrite) try: import ttf font = ttf.TruetypeInfo(fontFileName) except: app.setStatus(_("Text abort: That embarrassing, I can't read this font file!")) return cmap = font.get_character_map() kern = None try: kern = font.get_glyph_kernings() except: pass adv = font.get_glyph_advances() xOffset = 0 yOffset = 0 glyphIndxLast = cmap[' '] for c in textToWrite: #New line if c == u'\n': xOffset = 0.0 yOffset -= 1#offset for new line continue if c in cmap: glyphIndx = cmap[c] if (kern and (glyphIndx,glyphIndxLast) in kern): k = kern[(glyphIndx,glyphIndxLast)] #FIXME: use kern for offset?? #Get glyph contours as line segmentes and draw them gc = font.get_glyph_contours(glyphIndx,closed) if(not gc): gc = font.get_glyph_contours(0,closed)#standard glyph for missing glyphs (complex glyph) if(gc and not c==' '): #FIXME: for some reason space is not mapped correctly!!! self.writeGlyphContour(block, font, gc, fontSize, depth, xOffset, yOffset) if glyphIndx < len(adv): xOffset += adv[glyphIndx] else: xOffset += 1 glyphIndxLast = glyphIndx #Remeber to close Font font.close() #Gcode Zsafe block.append(CNC.zsafe()) blocks.append(block) active = app.activeBlock() if active==0: active=1 app.gcode.insBlocks(active, blocks, "Text") app.refresh() app.setStatus("Generated Text")
def line(self, app, end_depth, mem_0, mem_1): """Create GCode for a Line""" x_start = min(OCV.WK_mems[mem_0][0], OCV.WK_mems[mem_1][0]) y_start = min(OCV.WK_mems[mem_0][1], OCV.WK_mems[mem_1][1]) x_end = max(OCV.WK_mems[mem_0][0], OCV.WK_mems[mem_1][0]) y_end = max(OCV.WK_mems[mem_0][1], OCV.WK_mems[mem_1][1]) z_start = OCV.WK_mems[mem_1][2] tool_dia = OCV.CD['diameter'] # tool_rad = tool_dia / 2. xy_stepover = tool_dia * OCV.CD['stepover'] / 100.0 z_stepover = OCV.CD['stepz'] # avoid infinite while loop if z_stepover == 0: z_stepover = 0.001 msg = ("Line Cut Operation: \n", "Start: \n\n{0}\n\n".format(OCV.showC(x_start, y_start, z_start)), "End: \n\n{0}\n\n".format(OCV.showC(x_end, y_end, end_depth)), "Tool diameter: {0:.{1}f} \n\n".format(tool_dia, OCV.digits), "StepDown: {0:.{1}f} \n\n".format(z_stepover, OCV.digits), "StepOver: {0:.{1}f} \n\n".format(xy_stepover, OCV.digits)) retval = tkMessageBox.askokcancel("Line Cut", "".join(msg)) if OCV.DEBUG is True: print("RetVal", retval) if retval is False: return # Reset the Gcode in the Editor # Loading an empty file # Set the Initialization file blocks = [] block = Block("Init") # Get the current WCS as the mem are related to it block.append(OCV.CD['WCS']) blocks.append(block) block = Block("Line") block.append("(Line Cut)") block.append("(From: {0})".format(OCV.gcodeCC(x_start, y_start, z_start))) block.append("(To: {0})".format(OCV.gcodeCC(x_end, y_end, end_depth))) block.append("(StepDown: {0:.{1}f} )".format(z_stepover, OCV.digits)) block.append("(StepOver: {0:.{1}f} )".format(xy_stepover, OCV.digits)) block.append("(Tool diameter = {0:.{1}f})".format(tool_dia, OCV.digits)) # Safe move to first point block.append(CNC.zsafe()) block.append(CNC.grapid(x_start, y_start)) # Init Depth corrected by z_stepover # for the correctness of the loop # the first instruction of the while loop is -= z_stepover # the check is done at the final depth curr_depth = z_start + z_stepover # Create GCode from points while True: curr_depth -= z_stepover if curr_depth < end_depth: curr_depth = end_depth block.append(CNC.zenter(curr_depth)) block.append(CNC.gcode_string(1, [("F", OCV.CD["cutfeed"])])) block.append(CNC.gline(x_end, y_end)) # Move to start in a safe way block.append(CNC.zsafe()) block.append(CNC.grapid(x_start, y_start)) # Check exit condition if curr_depth <= end_depth: break # return to a safe Z block.append(CNC.zsafe()) blocks.append(block) if blocks is not None: active = OCV.APP.activeBlock() if active == 0: active = 1 OCV.APP.gcode.insBlocks(active, blocks, "Line Cut") OCV.APP.refresh() OCV.APP.setStatus(_("Line Cut: Generated line cut code"))
def pocket(self, app, end_depth, mem_0, mem_1): """create GCode for a pocket""" x_start = min(OCV.WK_mems[mem_0][0], OCV.WK_mems[mem_1][0]) y_start = min(OCV.WK_mems[mem_0][1], OCV.WK_mems[mem_1][1]) x_end = max(OCV.WK_mems[mem_0][0], OCV.WK_mems[mem_1][0]) y_end = max(OCV.WK_mems[mem_0][1], OCV.WK_mems[mem_1][1]) z_start = OCV.WK_mems[mem_1][2] tool_dia = OCV.CD['diameter'] tool_rad = tool_dia / 2. xy_stepover = tool_dia * OCV.CD['stepover'] / 100.0 z_stepover = OCV.CD['stepz'] # avoid infinite while loop if z_stepover == 0: z_stepover = 0.001 msg = ("Pocket Cut Operation: \n", "Start: \n\n{0}\n\n".format(OCV.showC(x_start, y_start, z_start)), "End: \n\n{0}\n\n".format(OCV.showC(x_end, y_end, end_depth)), "Tool diameter: {0:.{1}f} \n\n".format(tool_dia, OCV.digits), "StepDown: {0:.{1}f} \n\n".format(z_stepover, OCV.digits), "StepOver: {0:.{1}f} \n\n".format(xy_stepover, OCV.digits)) retval = tkMessageBox.askokcancel("Pocket Cut", "".join(msg)) if retval is False: return # Set the Initialization file blocks = [] block = Block("Init") # Get the current WCS as the mem are related to it block.append(OCV.CD['WCS']) blocks.append(block) block = Block("Pocket") block.append("(Pocket)") block.append("(Start: {0})".format(OCV.gcodeCC(x_start, y_start, z_start))) block.append("(End: {0})".format(OCV.gcodeCC(x_end, y_end, end_depth))) block.append("(StepDown: {0:.{1}f} )".format(z_stepover, OCV.digits)) block.append("(StepOver: {0:.{1}f} )".format(xy_stepover, OCV.digits)) block.append("(Tool diameter = {0:.{1}f})".format(tool_dia, OCV.digits)) # Move safe to first point block.append(CNC.zsafe()) block.append(CNC.grapid(x_start, y_start)) # Init Depth f_width = x_end - x_start f_heigth = y_end - y_start cut_dir = "Conventional" x_start_pocket = x_start + tool_rad y_start_pocket = y_start + tool_rad # Calc space to work with/without border cut travel_width = f_width - tool_dia travel_height = f_heigth - tool_dia if travel_width < tool_rad or travel_height < tool_rad: msg = "(Pocket aborted: Pocket area is too small for this End Mill.)" retval = tkMessageBox.askokcancel("Pocket Cut", msg) return # Prepare points for pocketing x_p = [] y_p = [] # Calc number of pass v_count = (int)(travel_height / xy_stepover) h_count = (int)(travel_width / xy_stepover) # Make them odd if v_count % 2 == 0: v_count += 1 if h_count % 2 == 0: h_count += 1 # Calc step minor of Max step h_stepover = travel_height / v_count w_stepover = travel_width / h_count # Start from border to center x_s = x_start_pocket y_s = y_start_pocket w_s = travel_width h_s = travel_height x_c = 0 y_c = 0 while x_c <= h_count / 2 and y_c <= v_count / 2: # Pocket offset points x_0, y_0 = rect_path(x_s, y_s, w_s, h_s) if cut_dir == "Conventional": x_0 = x_0[::-1] y_0 = y_0[::-1] x_p = x_p + x_0 y_p = y_p + y_0 x_s += h_stepover y_s += w_stepover h_s -= 2.0 * h_stepover w_s -= 2.0 * w_stepover x_c += 1 y_c += 1 # Reverse point to start from inside (less stres on the tool) x_p = x_p[::-1] y_p = y_p[::-1] # Move safe to first point block.append(CNC.zsafe()) block.append(CNC.grapid(x_p[0], y_p[0])) # Init Depth corrected by z_stepover # for the correctness of the loop # the first instruction of the while loop is -= z_stepover # the check is done at the final depth curr_depth = z_start + z_stepover # Create GCode from points while True: curr_depth -= z_stepover if curr_depth < end_depth: curr_depth = end_depth block.append(CNC.zenter(curr_depth)) block.append(CNC.gcode_string(1, [("F", OCV.CD["cutfeed"])])) # Pocketing for x_l, y_l in zip(x_p, y_p): block.append(CNC.gline(x_l, y_l)) # Move to the begin in a safe way block.append(CNC.zsafe()) block.append(CNC.grapid(x_p[0], y_p[0])) # Verify exit condition if curr_depth <= end_depth: break # end of the loop # return to z_safe block.append(CNC.zsafe()) blocks.append(block) if blocks is not None: active = OCV.APP.activeBlock() if active == 0: active = 1 OCV.APP.gcode.insBlocks(active, blocks, "Line Cut") OCV.APP.refresh() OCV.APP.setStatus(_("Line Cut: Generated line cut code"))
def mop(self, app, mem_0, mem_1, mop_type): """create GCode for a pocket Values are stored in OCV.mop_vars as a dictionary""" end_depth = OCV.mop_vars["tdp"] x_start = min(OCV.WK_mems[mem_0][0], OCV.WK_mems[mem_1][0]) y_start = min(OCV.WK_mems[mem_0][1], OCV.WK_mems[mem_1][1]) x_end = max(OCV.WK_mems[mem_0][0], OCV.WK_mems[mem_1][0]) y_end = max(OCV.WK_mems[mem_0][1], OCV.WK_mems[mem_1][1]) z_start = OCV.WK_mems[mem_1][2] tool_dia = OCV.mop_vars["tdia"] tool_rad = tool_dia / 2. xy_stepover = OCV.mop_vars["mso"] z_stepover = OCV.mop_vars["msd"] # avoid infinite while loop if z_stepover == 0: z_stepover = 0.001 if mop_type == "PK": # Pocketing isSpiral = OCV.mop_vars["pks"] isInt = OCV.mop_vars["sin"] if isSpiral is True: op_name = "Spiral Pocket" else: op_name = "Rectangular Pocket" elif mop_type == "LN": op_name = "Line" """ cut_dir passed to the calculation method depends on starting point, If we cut from internal we minimize "full stock" cut in which the endmill is 'surrounded' by the material. For calculation these assumptions are done, maybe is wrong Conventionl cut is when piece is cut on left side of the cutter Climb cut is when piece is cut on right side """ if mop_type == "PK": # Pocketing if isInt is True: cut_dir = 1 reverse = True else: cut_dir = 0 reverse = False msg = ( "{} Operation: \n\n".format(op_name), "Start: \n\n{0}\n\n".format(OCV.showC(x_start, y_start, z_start)), "End: \n\n{0}\n\n".format(OCV.showC(x_end, y_end, end_depth)), "Tool diameter: {0:.{1}f} \n\n".format(tool_dia, OCV.digits), "StepDown: {0:.{1}f} \n\n".format(z_stepover, OCV.digits), "StepOver: {0:.{1}f} \n\n".format(xy_stepover, OCV.digits)) retval = tkMessageBox.askokcancel( "MOP {} Cut".format(op_name), "".join(msg)) if retval is False: return # Start Calculations start = (x_start, y_start) end = (x_end, y_end) if mop_type == "PK": # Pocketing # Assign points here if isSpiral is True: msg,x_p,y_p = spiral_pocket(start, end, tool_rad, xy_stepover, cut_dir) else: msg,x_p,y_p = spiral_pocket(start, end, tool_rad, xy_stepover, cut_dir) if msg != "OK": retval = tkMessageBox.askokcancel(op_name, msg) return if reverse is True: x_p.reverse() y_p.reverse() # Reset the editor and write the Gcode generated Here OCV.TK_MAIN.clear_gcode() OCV.TK_MAIN.clear_editor() OCV.TK_MAIN.reset_canvas() blocks = [] block = Block.Block("Init") # Get the current WCS as the mem are related to it block.append(OCV.CD['WCS']) blocks.append(block) block = Block.Block(op_name) block.append("({})".format(op_name)) block.append("(Tool diameter = {0:.{1}f})".format(tool_dia, OCV.digits)) block.append("(Start: {0})".format(OCV.gcodeCC(x_start, y_start, z_start))) block.append("(End: {0})".format(OCV.gcodeCC(x_end, y_end, end_depth))) block.append("(StepDown: {0:.{1}f} )".format(z_stepover, OCV.digits)) block.append("(StepOver: {0:.{1}f} )".format(xy_stepover, OCV.digits)) if mop_type == "PK": # Pocketing # Move safe to first point block.append(CNC.zsafe()) block.append(CNC.grapid(x_p[0], y_p[0])) elif mop_type == "LN": # Move safe to first point block.append(CNC.zsafe()) block.append(CNC.grapid(x_start, y_start)) else: return # the check is done at the final depth curr_depth = z_start print("curr_depth, z_start", curr_depth, z_start) # Create GCode from points while True: curr_depth -= z_stepover if curr_depth < end_depth: curr_depth = end_depth block.append(CNC.zenter(curr_depth)) block.append(CNC.gcode_string(1, [("F", OCV.CD["cutfeed"])])) if mop_type == "PK": # Pocketing for x_l, y_l in zip(x_p, y_p): block.append(CNC.gline(x_l, y_l)) # Move to the begin in a safe way block.append(CNC.zsafe()) block.append(CNC.grapid(x_p[0], y_p[0])) elif mop_type == "LN": block.append(CNC.gline(x_end, y_end)) # Move to the begin in a safe way block.append(CNC.zsafe()) block.append(CNC.grapid(x_start, y_start)) # Verify exit condition if curr_depth <= end_depth: break # end of the loop # return to z_safe block.append(CNC.zsafe()) blocks.append(block) if blocks is not None: active = OCV.TK_MAIN.activeBlock() if active == 0: active = 1 OCV.TK_MAIN.gcode.insBlocks(active, blocks, op_name) OCV.TK_MAIN.refresh() OCV.TK_MAIN.setStatus(_("{}: GCode Generated".format(op_name)))
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 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 GetStitches(self, app, FileName ): try: from struct import * except: app.setStatus("Embroidery abort: no module named struct") return print(FileName) try: f = open(FileName,'rb') except: app.setStatus(" Embroidery abort: Can't read image file") return app.setStatus(" Embroidery: file %s sucsessfully opened"%f.name) #DST header struct checking - parsing format = "3s16sc3s7sc3s3sc3s5sc3s5sc3s5sc3s5sc3s6sc3s6sc" data=f.read(94) LAN,LA,C,STN,ST,C,CLN,CL,C,POSX,posx,C,NEGX,negx,C,POSY,posy,C,NEGY,negy,C,AX,ax,C,AY,ay,c=unpack(format, data) CL=int(CL) ST=int(ST) if (LAN !='LA:'): app.setStatus(" Embroidery abort: Not a DST") print (LA) print(" St count: %d color changes=%d"%(ST ,CL)) f.seek(512); coordX=0;coordY=0;#initial coordinates to start sewing cnt=0;#just counter color = 0;#color code format="1b1b1b"# 3 unsigned bytes from data field prevCol=self.color i=0 blocks = [] for ColorCycles in range (0 , CL+1): #color cycles block = Block(self.name) while prevCol==self.color: ff=f.read(3);#read 24 bits cnt+=1 if len(ff)<3: break b0,b1,b2=unpack(format, ff) #data field unpacked with "format" to b0 b1 b2 dx = decode_dx(b0, b1, b2) dy = decode_dy(b0, b1, b2) coordX+=dx coordY+=dy block.color = colors[self.color] block.append(self.decode_flags( b2)(coordX,coordY)) block.append(CNC.zsafe())#safe height prevCol = self.color print("Stitches read=: %d"%cnt)#counter blocks.append(block) try: dx = float(self["dx"]) except: dx = 0.0 try: dy = float(self["dy"]) except: dy = 0.0 return blocks
def execute(self, app): # ae = self.fromMm("ae") if self["splicesteps"] =="" or self["splicesteps"]<4: steps=4/(2*pi) else: steps=self["splicesteps"]/(2*pi) # manualsetting = self["manualsetting"] manualsetting = 1 #=========== Converted to comment and changed for current compatibility ============================== # cutradius = CNC.vars["trochcutdiam"]/2.0 cutradius = self["diam"]/2.0 # cutradius = CNC.vars["trochcutdiam"]/2.0 #========================================================================================= zfeed = CNC.vars["cutfeedz"] feed =CNC.vars["cutfeed"] minimfeed =CNC.vars["cutfeed"] if manualsetting: if self["diam"]: cutradius = self["diam"]/2.0 if self["zfeed"] and self["zfeed"]!="": zfeed = self["zfeed"] # if self["minimfeed"] and self["minimfeed"]!="": # minimfeed = min (self["minimfeed"],feed) if self["feed"] and self["feed"]!="": feed = self["feed"] if self["endmill"]: self.master["endmill"].makeCurrent(self["endmill"]) # radius = CNC.vars["cutdiam"]/2.0 # radius = self["diam"]/2.0 toolRadius = CNC.vars["diameter"]/2.0 radius = max(0,cutradius-toolRadius) oldradius=radius #----------------------------------------------------------- # helicalRadius = self["helicalDiam"]/2.0 # if helicalRadius=="": # helicalRadius=radius # else: # helicalRadius=max(0,helicalRadius- toolRadius) helicalRadius=radius #----------------------------------------------------------- # helicalRadius=min(0.99*toolRadius,helicalRadius) # if radius!=0: # helicalRadius= min(helicalRadius,radius) helicalPerimeter=pi*2.0*helicalRadius # helicalangle = self["helicalangle"] # if helicalangle>89.5: # helicalangle=89.5 # if helicalangle<0.01: # helicalangle=0.01 # downPecking=helicalPerimeter*tan(radians(helicalangle)) cw = self["cw"] surface = CNC.vars["surface"] #=========== Converted to comment and changed for current compatibility ============================== # zbeforecontact=surface+CNC.vars["zretract"] # zbeforecontact=surface+CNC.vars["zretract"] # hardcrust = surface - CNC.vars["hardcrust"] # feedbeforecontact = CNC.vars["feedbeforecontact"]/100.0 # hardcrustfeed = CNC.vars["hardcrustfeed"]/100.0 zbeforecontact=surface zbeforecontact=surface hardcrust = surface feedbeforecontact = zfeed hardcrustfeed = feed #===================================================================================================== t_splice = self["TypeSplice"] dtadaptative = 0.0001 adaptativepolice=0 # minimradius = min(radius, toolRadius*self["MinTrochDiam"]/(100)) # minimradius = min(radius, toolRadius*self["MinTrochDiam"]/(100)) # minimradius = min(radius, toolRadius*CNC.vars["mintrochdiam"]/(100)) atot = self.fromMm("ae") # spiral_twists=(radius-helicalRadius)/atot#<<spiral ae smaller than ae (aprox 50%) # if (radius-helicalRadius)%atot: spiral_twists=1+(radius-helicalRadius)//atot spiral_twists=ceil(radius-helicalRadius)/atot#<<spiral ae smaller than ae (aprox 50%) rpm = self["rpm"] downPecking=helicalPerimeter*zfeed/feed helicalangle=degrees(atan2(downPecking,helicalPerimeter)) # steps=self["splicesteps"]/2*pi # K_Z = self["K_Z"] # if K_Z == "": # K_Z = 1.0 # K_XY = self["K_XY"] # if K_XY == "": # K_XY = 1.0 # s_z = self["S_z"] # s_xy = self["S_xy"] # xyfeed = CNC.vars["cutfeed"] # zfeed *= K_Z # xyfeed *=K_XY # Get selected blocks from editor # def trochprofile_bcnc(self, cutDiam=0.0, direction=None, offset=0.0, overcut=False,adaptative=False, adaptedRadius=0.0, tooldiameter=0.0, name=None): # app.trochprofile_bcnc(trochcutdiam, direction, self["offset"], self["overcut"], self["adaptative"], cornerradius, CNC.vars["diameter"], name) #<< diameter only to information # cornerradius = (cutradius - CNC.vars["diameter"]/2.0 direction=self["direction"] if direction!="on (3d Path)": targetDepth=self["targetDepth"] depthIncrement=self["depthIncrement"] # tabsnumber=self["tabsnumber"] # tabsWidth=self["tabsWidth"] # tabsHeight=self["tabsHeight"] tabsnumber=tabsWidth=tabsHeight=0 app.trochprofile_bcnc(2*cutradius, direction,self["offset"], self["overcut"], self["adaptative"], radius, CNC.vars["diameter"],\ targetDepth, depthIncrement, tabsnumber, tabsWidth, tabsHeight) app.refresh() # app.editor.selectAll() selBlocks = app.editor.getSelectedBlocks() # if not selBlocks: # app.editor.selectAll() # selBlocks = app.editor.getSelectedBlocks() if not selBlocks: app.setStatus(_("Trochoid abort: Please select some path")) return #Check inputs if cutradius <= toolRadius: app.setStatus(_("Trochoid Cut Diameter has to be greater than End mill")) return if helicalRadius <= 0.0: app.setStatus(_("Helical Descent Diameter has to be greater than End mill")) return if feed <= 0: app.setStatus(_("Feed has to be greater than 0")) return if zfeed <= 0: app.setStatus(_("Plunge Feed has to be greater than 0")) return if minimfeed <= 0: app.setStatus(_("Minimum Adaptative Feed has to be greater than 0")) return #Get all segments from gcode allSegments = self.extractAllSegments(app,selBlocks) #Create holes locations # allHoles=[] for bidSegment in allSegments: if len(bidSegment)==0: continue blocks = [] # n = self["name"] # if not n or n=="default": n="Trochoidal_3D" # newname = Block.operationName(path.name) n="Troch3d" tr_block = Block(n) phi=oldphi=0# oldadaptativephi=0 oldsegm=[[0,0,0],[0,0,0]] # segments --------------------------------------------- for idx, segm in enumerate(bidSegment): if idx >= 0: if cw: u = 1 arc = "G2" else: u = -1 arc = "G3" # ////////////--------------------------------------------------------------------- # information: --------------------------------------------------------------------- segLength = self.calcSegmentLength(segm) # --------------------------------------------- # tr_block.append("(seg length "+str(round(segLength,4))+" )") # ----------------------------------------------------------------------------- # ////////---------------------------------------------------------------------- if idx == 0: # tr_block.append("(-------------- PARAMETERS ------------------------)") tr_block.append("(Cut diam "+str( cutradius*2 )+" (troch "+str(radius*2.0)+"+End mill "+str(toolRadius*2.0)+" ) Advance "+str(atot)+" )") # tr_block.append("(Cut diam "+str(CNC.vars["trochcutdiam"])+" (troch "+str(radius*2.0)+" + End mill " + str(toolRadius*2.0)+" ) Advance "+str(atot)+" )") # tr_block.append("(Min troch "+str(int(CNC.vars["mintrochdiam"]))+"% = "+str(minimradius*2.0)+"mm , min cut diam "+str(2*(minimradius+toolRadius))+"mm )") tr_block.append("(Feed "+str(feed)+" Plunge feed "+ str(zfeed)+" )") #tr_block.append("(Helical diam "+str(round((helicalRadius+toolRadius)*2,2))+" ( helical diam "+str(helicalRadius*2.0)+"+End mill "+str(toolRadius*2.0)+" )") tr_block.append("(Helical descent angle " + str(round(helicalangle,2)) +" cut diam " + str(round(helicalRadius*2.0,3))+" drop by lap "\ + str(round(downPecking,2)) + " )") tr_block.append("(--------------------------------------------------)") tr_block.append("(M06 T0 "+str(toolRadius*2.0)+" mm)") tr_block.append("M03") tr_block.append("S "+str(rpm)) tr_block.append("F "+str(feed)) # phi = atan2(segm[1][1]-segm[0][1], segm[1][0]-segm[0][0]) # oldphi=phi #<< declare initial angle # l = self.pol2car(radius, phi+radians(90*u)) # r = self.pol2car(radius, phi+radians(-90*u)) # B = segm[1][0],segm[1][1],segm[1][2] # bl = self.pol2car(radius, phi+radians(90*u), B) # br = self.pol2car(radius, phi+radians(-90*u), B) tr_block.append("( Seg: "+str(idx)+" length "+str(round(segLength,4))+" phi "+str(round(degrees(phi),2))+" )")#+ " oldphi "+str(round(oldphi*57.29,2))+" )") tr_block.append("(Starting point)") if (round(segm[1][1]-segm[0][1],4)==0 and round(segm[1][0]-segm[0][0],4)==0): phi=1234567890 tr_block.append("(The original first movement is vertical)") else: tr_block.append("(The original first movement is not vertical)") tr_block.append(CNC.zsafe()) # tr_block.append("g0 x "+str(B[0])+" y"+str(B[1])+" )")#" z "+str(B[2])+" )") # tr_block.append(arc+" x"+str(bl[0])+" y"+str(bl[1])+" R "+str(radius/2.0)+" z"+str(B[2])) # tr_block.append(arc+" x"+str(br[0])+" y"+str(br[1])+" i"+str(r[0]/2.0)+" j"+str(r[1]/2.0)) #<< as cutting # tr_block.append(("g1 x "+str(br[0])+" y"+str(br[1])+" z"+str(B[2]))) # tr_block.append(arc+" x"+str(bl[0])+" y"+str(bl[1])+" i"+str(l[0])+" j"+str(l[1])) # tr_block.append(arc+" x"+str(br[0])+" y"+str(br[1])+" i"+str(r[0])+" j"+str(r[1])+" z"+str(round(B[2],5))) #<< as cutting # if t_splice=="Circular both sides rectified": # tr_block.append(arc+" x"+str(bl[0])+" y"+str(bl[1])+" i"+str(-r[0])+" j"+str(-r[1])) tr_block.append("(--------------------------------------------------)") # tr_block.append(CNC.grapid(br[0],br[1],B[2])) # tr_block.append(CNC.zsafe()) #<<< Move rapid Z axis to the safe height in Stock Material # tr_block.append(CNC.zenter(surface)) # <<< TROCHOID CENTER # tr_block.append(CNC.grapid(segm[1][0],segm[1][1],segm[1][2])) # tr_block.append(CNC.zbeforecontact()) # <<< TROCHOID CENTER # tr_block.append(CNC.xyslowentersurface(0,-45.0)) # <<< TROCHOID CENTER # tr_block.append(("g0 z "+str(zbeforecontact))) # tr_block.append("( new segment begins )") # distance to trochoid center # if there is movement in xy plane phi calculate if (segm[1][1]-segm[0][1]!=0 or segm[1][0]-segm[0][0]!=0): phi = atan2(segm[1][1]-segm[0][1], segm[1][0]-segm[0][0]) # On surface # if segm[0][2]>zbeforecontact and segm[1][2]>zbeforecontact: if segm[0][2]>surface and segm[1][2]>surface: tr_block.append("(Seg: "+str(idx)+" length "+str(round(segLength,4))+" phi "+str(round(degrees(phi),2))+ " On Surface)" ) tr_block.append(CNC.grapid(segm[1][0],segm[1][1],segm[1][2])) else: tr_distance = self.center_distance(segm,atot) A = segm[0][0],segm[0][1],segm[0][2] d=segLength ae = tr_distance[4] # ////////////--------------------------------------------------------------------- # information: --------------------------------------------------------------------- adv = tr_distance[3] #<<< ap = tr_distance[2] # << =zadd # --------------------------------------------- tr_block.append("(-----------------------------------------)") control_cameback = self.came_back(segm, oldsegm) if control_cameback: tr_block.append("(-------------> Came back !! <------------- )")#+str(control_cameback)+" )") # tr_block.append("( old Ax "+str(round(oldsegm[0][0],3))+" Ay "+str(round(oldsegm[0][1],3))+" Bx "+ str(round(oldsegm[1][0],3))+" By "+ str(round(oldsegm[1][1],3))+" )") # tr_block.append("( curr Ax "+str(round(segm[0][0],3))+" Ay "+str(round(segm[0][1],3))+" Bx "+ str(round(segm[1][0],3))+" By "+ str(round(segm[1][1],3))+" )") if round(segLength,5) <= dtadaptative: adaptativepolice+=1.0 tr_block.append("(Seg "+str(idx)+" adaptativepolice " +str(adaptativepolice)+" length "+str(round(segLength,5))+" )") # /////////// Trochoid method ////////////////////////////////////////////////////////////////////////////// if adaptativepolice==0 or adaptativepolice >2.5: tr_block.append("( Seg: "+str(idx)+" phi "+str(round(degrees(phi),2))+ " oldphi "+str(round(degrees(oldphi),2))+" length "+str(round(segLength,5))+" )") tr_block.append("(ae: "+str(round(ae,5))+" dz: "+str(round(ap,4))+"adv: "+str(round(adv,4))+" )") # tr_block.append("( Bx "+str(round(segm[1][0],2))+ " By "+ str(round(segm[1][1],2))) # ----------------------------------------------------------------------------- # ////////---------------------------------------------------------------------- if control_cameback: # adaptativepolice+=0.5 B = segm[1][0],segm[1][1],segm[1][2] # tr_block.append(CNC.gline(segm[1][0],segm[1][1],segm[1][2])) t_splice="came_back" # tr_block.extend(self.trochoid(t_splice,A,B,minimradius,radius,oldphi,phi,cw)) tr_block.extend(self.trochoid(t_splice,A,B,0.0,radius,oldphi,phi,cw)) tr_block.append("F "+ str(feed)) t_splice = self["TypeSplice"] else: # from POINT A -- to ---> POINT B if segLength<=adv: tr_block.append("(Only one trochoid, oldphi "+str(round(degrees(oldphi),2))+" )") tr_block.extend(self.trochoid(t_splice,A,B,oldradius,radius,oldphi,phi,cw)) while d >adv: # first trochoid # tr_block.append("d "+ str(d)) B = A[0]+tr_distance[0], A[1]+tr_distance[1], A[2]+tr_distance[2] # intermediates points = trochoids points # tr_block.append(CNC.gline(B[0],B[1],B[2])) # <<< TROCHOID CENTER # tr_block.extend(self.trochoid(A,B,radius,phi,oldphi,cw)) tr_block.append("(distance to end segment "+str(round(d,4))+" )") tr_block.extend(self.trochoid(t_splice,A,B,oldradius,radius,oldphi,phi,cw)) A=B d-=adv oldphi=phi # last point if B[0] != segm[1][0] or B[1] != segm[1][1] or B[2] != segm[1][2]: B = segm[1][0],segm[1][1],segm[1][2] # tr_block.append(CNC.gline(B[0],B[1],B[2])) # <<< TROCHOID CENTER tr_block.append("(---last trochoid, distance to end segment "+str(round(d,4))+" ---)") tr_block.extend(self.trochoid(t_splice,A,B,oldradius,radius,phi,phi,cw)) adaptativepolice=0 # /////// Adapative method ////////////////////////////////////////////////////////////////////////////////////////////////////////// # if oldphi==3600: else: if adaptativepolice==1: #goes to de two warning movements lastphi=oldphi tr_block.append("( Alarm "+ str(adaptativepolice)+" Seg: "+str(idx)+" phi " + str(round(degrees(phi),2))\ + "oldphi "+str(round(degrees(oldphi),2))+ " )") # difangle=(phi-oldadaptativephi) # tr_block.append("(dif angle:"+str(round(difangle,4))+" )") # oldadaptativephi=oldphi=phi # round(difangle,5)==round(pi,5): elif adaptativepolice==2: phi=lastphi if control_cameback:# abs(round(difangle,6)) == (round(pi,6)): tr_block.append("(Starts adaptative trochoids"+" adaptativepolice "+str(adaptativepolice) ) adaptativepolice +=0.5 elif adaptativepolice==2.5: # tr_block.append("(-----------------------------------------)") # adaptradius=minimradius tr_block.append("(Adaptative Seg: "+str(idx)+" length "+str(round(segLength,5))+" phi "+str(round(degrees(phi),2))\ +" oldphi "+str(round(degrees(oldphi),2))+" )") # tr_block.append("( Ax "+str(round(segm[0][0],2))+ " Ay "+ str(round(segm[0][1],2))) # tr_block.append(CNC.gline(segm[1][0],segm[1][1],segm[1][2])) # from POINT A -- to ---> POINT B # if adaptativepolice==1: tr_distance = self.center_distance(segm,atot/3.0) #<<< short advanc distances A = segm[0][0],segm[0][1],segm[0][2] d=segLength ae = tr_distance[4] adv = tr_distance[3] #<<< d-=adv while d >0:#adv: # first trochoid if d!=segLength-adv: oldphi=phi # tr_block.append("d "+ str(d)) B = A[0]+tr_distance[0], A[1]+tr_distance[1], A[2]+tr_distance[2] #------------------------------ # adaptradius= a*d + minimradius # if d=0 : adaptradius=minimradius # if d=seglength : adaptradius=radius # a=(radius-minimradius)/segLength # adaptradius=a*d+minimradius a=radius/segLength adaptradius=(self.roundup(a*d,4))#+minimradius #------------------------------ if t_splice!="Splices": t_splice="Warpedarc" tr_block.append("(from trochoid distance to end segment "+str(round(d,4))+" )") tr_block.append("(adaptradius "+ str(round(adaptradius,4))+" radius " + str(radius)+" )") # tr_block.append("F "+ str(feed*adaptradius//radius)) tr_block.append("F "+ str(minimfeed+(feed-minimfeed) *adaptradius//radius)) if adaptradius>0.0: tr_block.extend(self.trochoid(t_splice,A,B,oldradius,adaptradius,oldphi,phi,cw)) else: tr_block.append("(R= "+str(adaptradius)+ "not sent )") # tr_block.append("G1 x"+str(round(B[0],4))+" y "+str(round(B[1],4))+" z "+str(round(B[2],4))) A=B d-=adv oldradius=adaptradius # oldadaptativephi=0 #REVISAR, A COMENTADO # last point # d=0 # oldradius=adaptradius # adaptradius=minimradius # if B[0] != segm[1][0] or B[1] != segm[1][1] or B[2] != segm[1][2]: # B = segm[1][0],segm[1][1],segm[1][2] # tr_block.append(CNC.gline(B[0],B[1],B[2])) # <<< TROCHOID CENTER # tr_block.append("(last trochoid, from trochoid distance to end segment "+str(round(d,4))+" )") # tr_block.append("(adaptradius "+ str(adaptradius)+" )") # tr_block.append("F "+ str(feed*adaptradius//radius)) # tr_block.extend(self.trochoid(t_splice,A,B,oldradius,adaptradius,phi,phi,cw)) adaptativepolice=0 tr_block.append("(Adaptative Completed)") tr_block.append("F "+ str(feed//3)) # if adaptativepolice>1: t_splice = self["TypeSplice"] # adaptativepolice=0 oldradius=radius oldsegm=segm oldphi=phi # /////// Vertical movement /////////////////////////////////////////////////////////////////////////////////////////////////////////////// elif idx!=0: tr_block.append("(Seg: "+str(idx)+" length "+str(round(segLength,4))+" phi "+str(round(degrees(phi),2))+" oldphi "+str(round(degrees(oldphi),2))+" )" ) tr_block.append("(Helical descent") # descent A=segm[0][0],segm[0][1],segm[0][2] if segm[0][2] > segm[1][2]: if segm[0][2] >zbeforecontact:# and segm[1][2]<=surface: if segm[1][2]<=zbeforecontact: B = segm[1][0],segm[1][1],max(segm[1][2],zbeforecontact) tr_block.append("(Rapid helical to z before contact "+"helicalRadius "+str(helicalRadius)+" )") if idx==1 and oldphi==1234567890: tr_block.append("g0 x "+str(B[0])+" y"+str(B[1])+" )")#" z "+str(B[2])+" )") tr_block.extend(self.helical(A,B,helicalRadius,phi,u)) # Instead of decreasing the speed, to avoid the jerkl, decrease the drop by lap if segm[0][2] >surface:# and segm[1][2]<=surface: if segm[1][2]<=surface: tr_block.append("(Slow helical to surface )" ) A=A[0],A[1],zbeforecontact d=A[2]-surface adv=downPecking * feedbeforecontact while d > adv: B = segm[1][0],segm[1][1],max(segm[1][2],A[2]-adv) tr_block.extend(self.helical(A,B,helicalRadius,phi,u)) A=A[0],A[1],B[2] d-=adv B = segm[1][0],segm[1][1],surface tr_block.extend(self.helical(A,B,helicalRadius,phi,u)) if segm[0][2] >hardcrust:# and segm[1][2]<=surface: if hardcrust< surface: if segm[1][2]<=hardcrust: tr_block.append("(Helical in hard crust)" ) A=A[0],A[1],surface d=A[2]-hardcrust adv=downPecking * hardcrustfeed while d > adv: B = segm[1][0],segm[1][1],max(segm[1][2],A[2]-adv) tr_block.extend(self.helical(A,B,helicalRadius,phi,u)) A=A[0],A[1],B[2] d-=adv B = segm[1][0],segm[1][1],hardcrust tr_block.extend(self.helical(A,B,helicalRadius,phi,u)) tr_block.append("(Helical to target )" ) A=A[0],A[1],hardcrust d=A[2]-segm[1][2] adv=downPecking while d > adv: B = segm[1][0],segm[1][1],A[2]-adv tr_block.extend(self.helical(A,B,helicalRadius,phi,u)) A=A[0],A[1],B[2] d-=adv B = segm[1][0],segm[1][1],segm[1][2] tr_block.extend(self.helical(A,B,helicalRadius,phi,u)) tr_block.append("(Flatten)") tr_block.extend(self.helical(B,B,helicalRadius,phi,u)) if round(helicalRadius,4)!=round(radius,4): tr_block.append("(Spiral adjustement)") # tr_block.extend(self.trochoid(t_splice,B,B,radius,helicalRadius,phi,phi+4*pi,cw)) # steps=max(1,int(steps*radius*(spiral_twists)/2.0)) # steps=min(steps, 12*spiral_twists) # steps*=spiral_twists # tr_block.append("(Spiral steps "+str(steps)+" in "+str(int((spiral_twists/2.)+1))+" twists)") # tr_block.append("(Spiral "+str(int((spiral_twists/2.)+1))+" twists)") tr_block.append("(Spiral "+str(spiral_twists)+" twists)") tr_block.extend(self.splice_generator(B,B,helicalRadius,radius,phi,phi-spiral_twists*2*pi, radians(-90),radians(-90),u,1.2*steps)) tr_block.append("(Target diameter)") # tr_block.extend(self.helical(B,B,radius,phi,u)) tr_block.extend(self.trochoid(t_splice,B,B,radius,radius,phi,phi,cw)) # ascent elif segm[1][2] > segm[0][2]: tr_block.append("(Helical rapid ascentt "+"helicalRadius "+str(helicalRadius)+" )" ) B = segm[1][0],segm[1][1],segm[1][2] tr_block.extend(self.helical(A,B,helicalRadius,phi,u)) # tr_block.append(CNC.grapid(center[0],center[1],center[2])) # tr_block.extend(CNC.grapid(center)) # end of segment # tr_block.append(CNC.gline(segm[1][0],segm[1][1],segm[1][2])) # oldsegm=segm tr_block.append("(-----------------------------------------)") tr_block.append(CNC.zsafe()) #<<< Move rapid Z axis to the safe height in Stock Material blocks.append(tr_block) self.finish_blocks(app, blocks)
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): #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): 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: 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): # info =xnew,ynew,znew,dxy,dz xscale = self["xscale"] if xscale == "": xscale = 1 yscale = self["yscale"] if yscale == "": yscale = 1 zscale = self["zscale"] if zscale == "": zscale = 1 surface = CNC.vars["surface"] if zscale > 0: surface *= zscale feed = self["feed"] zfeed = CNC.vars["cutfeedz"] rpm = self["rpm"] if self["zfeed"]: zfeed = self["zfeed"] #zup = self["zup"] centered = self["centered"] # zbeforecontact=surface+CNC.vars["zretract"] # hardcrust = surface - CNC.vars["hardcrust"] # feedbeforecontact = CNC.vars["feedbeforecontact"]/100.0 # hardcrustfeed = CNC.vars["hardcrustfeed"]/100.0 # Get selected blocks from editor selBlocks = app.editor.getSelectedBlocks() if not selBlocks: app.setStatus(_("Scaling abort: Please select some path")) return elements = len(app.editor.getSelectedBlocks()) print("elements", elements) for bid in app.editor.getSelectedBlocks(): if len(app.gcode.toPath(bid)) < 1: continue path = app.gcode.toPath(bid)[0] if centered: center = path.center() else: center = 0, 0 print("center", center[0], center[1]) # if elements>=2: # center=0,0 #Get all segments from gcode allSegments = self.extractAllSegments(app, selBlocks)[0] name_block = self.extractAllSegments(app, selBlocks)[1] # num_block = self.extractAllSegments(app,selBlocks)[2] #Create holes locations all_blocks = [] for bidSegment in allSegments: if len(bidSegment) == 0: continue # all_blocks = [] n = self["name"] # if not n or n=="default": n="Trochoidal_3D" if elements > 1: n = "scale " else: if centered: n = "center scale " + str(name_block) else: n = "scale " + str(name_block) bid_block = Block(n) for idx, segm in enumerate(bidSegment): # if idx >= 0: # bid_block.append("(idx "+str(idx)+" -------------- )") info = self.scaling(segm, center, xscale, yscale, zscale) if idx == 0: bid_block.append("(---- Scale (x " + str(xscale) + " : 1.0),(y " + str(yscale) + " : 1.0),(z " + str(zscale) + " : 1.0) ---- )") bid_block.append("(center " + str(center[0]) + " ," + str(center[1]) + " )") bid_block.append("M03") bid_block.append("S " + str(rpm)) bid_block.append(CNC.zsafe()) bid_block.append("F " + str(zfeed)) bid_block.append("g0 x " + str(info[0]) + " y " + str(info[1])) currentfeed = oldfeed = zfeed else: # if B[5]>=0: #<< zsign # currentfeed=feed # else: #relationship if info[4] >= 0: currentfeed = feed else: rel = info[3] / (info[3] + abs(info[4])) currentfeed = int(rel * feed + (1 - rel) * zfeed) if segm[0][2] > surface and segm[1][2] >= surface: bid_block.append("g0 x " + str(info[0]) + " y " + str(info[1]) + " z " + str(info[2])) else: if currentfeed != oldfeed: bid_block.append("F " + str(currentfeed)) bid_block.append("g1 x " + str(info[0]) + " y " + str(info[1]) + " z " + str(info[2])) oldfeed = currentfeed bid_block.append(CNC.zsafe( )) #<<< Move rapid Z axis to the safe height in Stock Material all_blocks.append(bid_block) # print "bid", bid_block.name(), bid_block,"*****************" self.finish_blocks(app, all_blocks, elements)
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): # info =xnew,ynew,znew,dxy,dz xscale= self["xscale"] if xscale=="":xscale=1 yscale= self["yscale"] if yscale=="":yscale=1 zscale= self["zscale"] if zscale=="":zscale=1 surface = CNC.vars["surface"] if zscale>0: surface*=zscale feed = self["feed"] zfeed = CNC.vars["cutfeedz"] rpm = self["rpm"] if self["zfeed"]: zfeed = self["zfeed"] #zup = self["zup"] centered = self["centered"] # zbeforecontact=surface+CNC.vars["zretract"] # hardcrust = surface - CNC.vars["hardcrust"] # feedbeforecontact = CNC.vars["feedbeforecontact"]/100.0 # hardcrustfeed = CNC.vars["hardcrustfeed"]/100.0 # Get selected blocks from editor selBlocks = app.editor.getSelectedBlocks() if not selBlocks: app.setStatus(_("Scaling abort: Please select some path")) return elements=len(app.editor.getSelectedBlocks()) print("elements",elements) for bid in app.editor.getSelectedBlocks(): if len(app.gcode.toPath(bid)) < 1: continue path = app.gcode.toPath(bid)[0] if centered: center = path.center() else: center=0,0 print ("center",center[0],center[1]) # if elements>=2: # center=0,0 #Get all segments from gcode allSegments = self.extractAllSegments(app,selBlocks)[0] name_block = self.extractAllSegments(app,selBlocks)[1] # num_block = self.extractAllSegments(app,selBlocks)[2] #Create holes locations all_blocks=[] for bidSegment in allSegments: if len(bidSegment)==0: continue # all_blocks = [] n = self["name"] # if not n or n=="default": n="Trochoidal_3D" if elements>1: n="scale " else: if centered: n="center scale "+str(name_block) else: n="scale "+str(name_block) bid_block = Block(n) for idx, segm in enumerate(bidSegment): # if idx >= 0: # bid_block.append("(idx "+str(idx)+" -------------- )") info=self.scaling(segm,center,xscale,yscale,zscale) if idx == 0: bid_block.append("(---- Scale (x "+str(xscale)+" : 1.0),(y "+str(yscale)+" : 1.0),(z "+str(zscale)+" : 1.0) ---- )") bid_block.append("(center "+str(center[0])+" ,"+str(center[1])+" )") bid_block.append("M03") bid_block.append("S "+str(rpm)) bid_block.append(CNC.zsafe()) bid_block.append("F "+str(zfeed)) bid_block.append("g0 x "+str(info[0])+" y "+str(info[1])) currentfeed=oldfeed=zfeed else: # if B[5]>=0: #<< zsign # currentfeed=feed # else: #relationship if info[4]>=0: currentfeed=feed else: rel=info[3]/(info[3]+abs(info[4])) currentfeed=int(rel*feed+(1-rel)*zfeed) if segm[0][2]> surface and segm[1][2]>=surface: bid_block.append("g0 x "+str(info[0])+" y "+str(info[1])+ " z "+str(info[2])) else: if currentfeed!=oldfeed: bid_block.append("F "+str(currentfeed)) bid_block.append("g1 x "+str(info[0])+" y "+str(info[1])+ " z "+str(info[2])) oldfeed=currentfeed bid_block.append(CNC.zsafe()) #<<< Move rapid Z axis to the safe height in Stock Material all_blocks.append(bid_block) # print "bid", bid_block.name(), bid_block,"*****************" self.finish_blocks(app, all_blocks,elements)
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): 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): 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))