def appendCross(block): block.append("m5") block.append(CNC.grapid(x=x0, y=y0 + marksizehalf, f=movefeed)) block.append(self.getPowerLine(app)) block.append(CNC.gline(y=y0 - marksizehalf, f=drawfeed)) block.append("m5") block.append(CNC.grapid(x=x0 + marksizehalf, y=y0, f=movefeed)) block.append(self.getPowerLine(app)) block.append(CNC.gline(x=x0 - marksizehalf, f=drawfeed)) block.append("m5")
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 appendCross45(block): msh = marksizehalf * self.sin45 block.append("m5") block.append(CNC.grapid(x=x0 - msh, y=y0 + msh, f=movefeed)) block.append(self.getPowerLine(app)) block.append(CNC.gline(x=x0 + msh, y=y0 - msh, f=drawfeed)) block.append("m5") block.append(CNC.grapid(x=x0 + msh, y=y0 + msh, f=movefeed)) block.append(self.getPowerLine(app)) block.append(CNC.gline(x=x0 - msh, y=y0 - msh, f=drawfeed)) block.append("m5")
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 calc(self,x,y,depth,peck,dwell,drillFeed,safeZforG0): self.safeZforG0 =float(abs(safeZforG0)) peck=abs(float(peck)) currentz=0.0 self.blocks = [] self.block = Block(self.name) self.block.append(CNC.grapid(x=x,y=y)) self.block.append(CNC.grapid(z=CNC.vars["safe"])) self.accelerateIfNeeded(0.0,drillFeed) self.block.append("(entered)") while(currentz>depth): currentz-=peck if currentz < depth: currentz = depth kwargs={"f":float(drillFeed)} self.block.append(CNC.gline(None,None,float(currentz),**kwargs)) if self.safeZforG0 >0: self.block.append(CNC.grapid(z=0.0+self.safeZforG0)) else : self.block.append(CNC.grapid(z=CNC.vars["safe"])) self.block.append("g4 %s"%(CNC.fmt("p",float(dwell)))) if currentz > depth: self.accelerateIfNeeded(currentz,drillFeed) self.block.append("(exiting)") self.block.append(CNC.grapid(z=CNC.vars["safe"])) self.blocks.append(self.block) return self.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 appendSpikes(block): sinSpike = math.sin(math.atan(2.0 - math.sqrt(3))) cosSpike = math.cos(math.atan(2.0 - math.sqrt(3))) block.append("m5") block.append(CNC.grapid(x=x0, y=y0, f=movefeed)) block.append(self.getPowerLine(app)) block.append(CNC.gline(x=x0 + marksizehalf * sinSpike, y=y0 - marksizehalf * cosSpike, f=drawfeed)) block.append(CNC.gline(x=x0 - marksizehalf * sinSpike, y=y0 - marksizehalf * cosSpike, f=drawfeed)) block.append(CNC.gline(x=x0 + marksizehalf * sinSpike, y=y0 + marksizehalf * cosSpike, f=drawfeed)) block.append(CNC.gline(x=x0 - marksizehalf * sinSpike, y=y0 + marksizehalf * cosSpike, f=drawfeed)) block.append(CNC.gline(x=x0, y=y0, f=drawfeed)) block.append(CNC.gline(x=x0 - marksizehalf * cosSpike, y=y0 + marksizehalf * sinSpike, f=drawfeed)) block.append(CNC.gline(x=x0 - marksizehalf * cosSpike, y=y0 - marksizehalf * sinSpike, f=drawfeed)) block.append(CNC.gline(x=x0 + marksizehalf * cosSpike, y=y0 + marksizehalf * sinSpike, f=drawfeed)) block.append(CNC.gline(x=x0 + marksizehalf * cosSpike, y=y0 - marksizehalf * sinSpike, f=drawfeed)) block.append(CNC.gline(x=x0, y=y0, f=drawfeed)) block.append("m5")
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 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)
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 calc(self, xstart, ystart, xend, yend, radius, cw): self.Points = [] self.corners = [ min(float(xstart), float(xend)), min(float(ystart), float(yend)), max(float(xstart), float(xend)), max(float(ystart), float(yend)), ] xmin, ymin, xmax, ymax = self.corners[0], self.corners[ 1], self.corners[2], self.corners[3] r = min(radius, (xmax - xmin) / 2, (ymax - ymin) / 2) blocks = [] block = Block(self.name) block.append(CNC.grapid(x=xmin, y=ymin + r)) block.append(CNC.grapid(z=0.0)) block.append("(entered)") if cw: block.append(CNC.gline(x=xmin, y=ymax - r)) if r > 0: block.append(CNC.garc(2, x=xmin + r, y=ymax, i=r, j=0)) if (xmax - xmin) > 2 * r: block.append(CNC.gline(x=xmax - r, y=ymax)) if r > 0: block.append(CNC.garc(2, x=xmax, y=ymax - r, i=0, j=-r)) if (ymax - ymin) > 2 * r: block.append(CNC.gline(x=xmax, y=ymin + r)) if r > 0: block.append(CNC.garc(2, x=xmax - r, y=ymin, i=-r, j=0)) if (xmax - xmin) > 2 * r: block.append(CNC.gline(x=xmin + r, y=ymin)) if r > 0: block.append(CNC.garc(2, x=xmin, y=ymin + r, i=0, j=r)) else: if r > 0: block.append(CNC.garc(3, x=xmin + r, y=ymin, i=r, j=0)) if (xmax - xmin) > 2 * r: block.append(CNC.gline(x=xmax - r, y=ymin)) if r > 0: block.append(CNC.garc(3, x=xmax, y=ymin + r, i=0, j=r)) if (ymax - ymin) > 2 * r: block.append(CNC.gline(x=xmax, y=ymax - r)) if r > 0: block.append(CNC.garc(3, x=xmax - r, y=ymax, i=-r, j=0)) if (xmax - xmin) > 2 * r: block.append(CNC.gline(x=xmin + r, y=ymax)) if r > 0: block.append(CNC.garc(3, x=xmin, y=ymax - r, i=0, j=-r)) if (ymax - ymin) > 2 * r: block.append(CNC.gline(x=xmin, y=ymin + r)) block.append("(exiting)") block.append(CNC.grapid(z=CNC.vars["safe"])) 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 appendSpikes(block): sinSpike = math.sin(math.atan(2.0 - math.sqrt(3))) cosSpike = math.cos(math.atan(2.0 - math.sqrt(3))) block.append("m5") block.append(CNC.grapid(x=x0, y=y0, f=movefeed)) block.append(self.getPowerLine(app)) block.append( CNC.gline(x=x0 + marksizehalf * sinSpike, y=y0 - marksizehalf * cosSpike, f=drawfeed)) block.append( CNC.gline(x=x0 - marksizehalf * sinSpike, y=y0 - marksizehalf * cosSpike, f=drawfeed)) block.append( CNC.gline(x=x0 + marksizehalf * sinSpike, y=y0 + marksizehalf * cosSpike, f=drawfeed)) block.append( CNC.gline(x=x0 - marksizehalf * sinSpike, y=y0 + marksizehalf * cosSpike, f=drawfeed)) block.append(CNC.gline(x=x0, y=y0, f=drawfeed)) block.append( CNC.gline(x=x0 - marksizehalf * cosSpike, y=y0 + marksizehalf * sinSpike, f=drawfeed)) block.append( CNC.gline(x=x0 - marksizehalf * cosSpike, y=y0 - marksizehalf * sinSpike, f=drawfeed)) block.append( CNC.gline(x=x0 + marksizehalf * cosSpike, y=y0 + marksizehalf * sinSpike, f=drawfeed)) block.append( CNC.gline(x=x0 + marksizehalf * cosSpike, y=y0 - marksizehalf * sinSpike, f=drawfeed)) block.append(CNC.gline(x=x0, y=y0, f=drawfeed)) block.append("m5")
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 calc(self, xstart, ystart, xend, yend): points = [] points.append(Vector(xstart, ystart)) points.append(Vector(xend, yend)) first = points[0] last = points[1] blocks = [] block = Block(self.name) block.append(CNC.grapid(first.x(), first.y())) block.append(CNC.grapid(z=0.0)) block.append("(entered)") block.append(CNC.gline(last.x(), last.y())) block.append("(exiting)") block.append(CNC.grapid(z=CNC.vars["safe"])) blocks.append(block) return blocks
def accelerateIfNeeded(self,ztogo,drillfeed): if self.safeZforG0>0: self.block.append(CNC.grapid(z=ztogo+self.safeZforG0)) kwargs={"f":float(drillfeed)} self.block.append(CNC.gline(None,None,ztogo,**kwargs))
class GCode(object): """Gcode file""" LOOP_MERGE = False def __init__(self): self.cnc = CNC() self.undoredo = undo.UndoRedo() self.probe = Probe.Probe() self.orient = Orient() self.vars = {} # local variables self.init() def init(self): """Reusable part of GCode initialisation""" self.filename = "" self.header = "" self.footer = "" OCV.blocks = [] # list of blocks # dummy values for min_z and max_z to correctly test when setted OCV.max_z = -9999 OCV.min_z = 10000 # TODO: maybe this could be used to name the blocks ? OCV.gcp_mop_name = "" # OCV.gcodelines = ["(-)",] # Add a starting 0 pos to better align index self.vars.clear() self.undoredo.reset() # FIXME check if this is needed # self.probe.init() self._lastModified = 0 self._modified = False def calculateEnableMargins(self): """Recalculate enabled path margins""" self.cnc.resetEnableMargins() for block in OCV.blocks: if block.enable: OCV.CD["xmin"] = min(OCV.CD["xmin"], block.xmin) OCV.CD["ymin"] = min(OCV.CD["ymin"], block.ymin) OCV.CD["zmin"] = min(OCV.CD["zmin"], block.zmin) OCV.CD["xmax"] = max(OCV.CD["xmax"], block.xmax) OCV.CD["ymax"] = max(OCV.CD["ymax"], block.ymax) OCV.CD["zmax"] = max(OCV.CD["zmax"], block.zmax) def isModified(self): """return internal _modifiedvalue""" return self._modified def resetModified(self): """reset internal _modified""" self._modified = False def __getitem__(self, item): """get block item""" return OCV.blocks[item] def __setitem__(self, item, value): """set block item""" OCV.blocks[item] = value def evaluate(self, line, app=None): """Evaluate code expressions if any and return line""" if isinstance(line, int): return None elif isinstance(line, list): for i, expr in enumerate(line): if isinstance(expr, types.CodeType): result = eval(expr, OCV.CD, self.vars) if isinstance(result, float): line[i] = str(round(result, OCV.digits)) else: line[i] = str(result) return "".join(line) elif isinstance(line, types.CodeType): # import traceback # traceback.print_stack() v = self.vars v['os'] = os v['app'] = app return eval(line, OCV.CD, self.vars) else: return line def parse_gcode(self, filename, adv_heur=False): """scan lines from gcodelines and parse them to create a proper OCV.blocks structure """ if OCV.DEBUG_PAR is True: OCV.printout_header("Scanning {0}", filename) # preprocess file to find a Post processor marker prcs = True l_idx = 1 l_bound = min(10, len(OCV.gcodelines)) while prcs is True: line = OCV.gcodelines[l_idx] if line.startswith("( ver: okk-"): OCV.g_code_pp = "CamBam-OKK" else: pass if l_idx < (l_bound - 1): l_idx += 1 else: prcs = False # act depending on prostprocessor marker # for now only 'CamBam-OKK' is implemented, using 'custom' grbl.cbpp # file as postprocessor in CamBam # others could be implemented if relevant information are supplied if OCV.g_code_pp in ("CamBam-OKK",): self.pre_process_gcode() else: # Plain Gcode file or not implemented "generators" are processed # using "add_line" method, in this case the g_code_pp value is # left 'Generic' as set in OCV file for line in OCV.gcodelines: self.add_line(line) Heuristic.trim_blocks() if OCV.DEBUG_PAR is True: OCV.printout_header("{0}", "END SCAN") def debug_info(self, line, move, move_s, move_f, delta_z): print(line) if (move_s[0], move_s[1]) != (move_f[0], move_f[1]): print("Motion {} Move start = {} \nMove end = {}".format( move, move_s, move_f)) print("Delta Z = ", delta_z) else: if delta_z > 0: print("Z_UP Move from {} to {} at X{} Y{}".format( move_s[2], move_f[2], move_s[0], move_s[1])) print("Delta Z = {}".format(delta_z)) elif delta_z < 0: print("Z_DOWN Move from {} to {} at X{} Y{}".format( move_s[2], move_f[2], move_s[0], move_s[1])) print("Delta Z = {}".format(delta_z)) else: print("Stationary Move at Point {}".format(move_s)) print(OCV.str_sep) def pre_process_gcode(self): """scan gcode lines and inject some metadata, it create only one Block. The main scope is to populate OCV.blocks_ev list used in later elaboration done by Heuristic.process_blocks(). See the Documentation in Heuristic.process_blocks() for more info. """ # DEBUG_INFO activation only for this method INT_DEBUG = False OCV.infos = [] process = True l_idx = -1 OCV.blocks_ev = ["",] while process is True: if l_idx < (len(OCV.gcodelines) - 1): l_idx += 1 else: # continue here is to force the loop to terminate here # if not present last line is scanned again process = False continue line = OCV.gcodelines[l_idx] if INT_DEBUG is True: print("{0} Line > {1}".format(l_idx, line)) if l_idx == (len(OCV.gcodelines) - 1): print("last line fo gcode") # discard the dummy first item if line.startswith("(-)"): continue if not OCV.blocks: OCV.blocks.append(Block("Header")) # events are processes later if line[:10] == "(MOP Start": OCV.blocks_ev.append( ("MS", l_idx, line, ((self.cnc.x, self.cnc.y, self.cnc.z), self.cnc.zval, (self.cnc.dx, self.cnc.dy, self.cnc.dz)))) OCV.blocks[-1].append(line) continue if line[:8] == "(MOP End": # if there is a MOP end OCV.blocks_ev.append( ("ME", l_idx, line, ((self.cnc.x, self.cnc.y, self.cnc.z), self.cnc.zval, (self.cnc.dx, self.cnc.dy, self.cnc.dz)))) OCV.blocks[-1].append(line) continue cmds = Heuristic.parse_line(line) # Debug infos do not delete # print(cmds) if cmds is None: # the line contains comments or no valid commands OCV.blocks[-1].append(line) continue # self.cnc.motionStart(cmds), analyze the move and populate the # positions, but the action is ended by sel.cnc.motionEnd(cmds) # in some condition the self.cnc.x(yz) variables hold a different # value at the start and at the end of operation, theese values # are both significative for the event, so this block of code # take care to "generate" the start value and the end value for # each line self.cnc.motionStart(cmds) move = cmds[0] move_s = (self.cnc.x, self.cnc.y, self.cnc.z) move_s_dz = self.cnc.dz self.cnc.motionEnd() move_f = (self.cnc.x, self.cnc.y, self.cnc.z) # at this point we have all the motion infos neede to generate # properly an event delta_z = move_f[2] - move_s[2] move_c = ((move_s[0], move_s[1], move_s[2]), delta_z, (move_f[0], move_f[1], move_f[2])) OCV.min_z = min(OCV.min_z, move_f[2]) OCV.max_z = max(OCV.max_z, move_f[2]) # debug info useful only for development self.debug_info(line, move, move_s, move_f, delta_z) # analyze moves if move in ("G1", "G2", "G3"): # 'cut move' with feedrate if cmds[1][0] == "F": if cmds[2][0] == "Z": ev_label = "GMZ" else: ev_label = "GMXY" OCV.blocks_ev.append( (ev_label, l_idx, line, move_c, cmds)) OCV.blocks[-1].append(line) continue else: # 'cut move' with no feedrate, generally a plain move no # event to process OCV.blocks[-1].append(line) elif move == "G0": # original code using self.cnc.gcode == 0 # will also detect come G0 move that don't contains Z value # leading to some 'false' positive if cmds[1][0] == "Z" and move_s_dz > 0.0: # rapid Z move up detected OCV.blocks_ev.append(("ZU", l_idx, line, move_c)) OCV.blocks[-1].append(line) continue elif cmds[1][0] == "Z" and move_s_dz < 0: # rapid Z move down detected OCV.blocks_ev.append(("ZD", l_idx, line, move_c)) OCV.blocks[-1].append(line) elif cmds[1][0] == "Z" and move_s_dz == 0: # Z neutral move this catch G0 Z(same level of prior move) # that sometimes could appear in code OCV.blocks_ev.append(("ZN", l_idx, line, move_c)) OCV.blocks[-1].append(line) else: # a normal G0 move is detected # this could catch "G0 Zxx" moves OCV.blocks_ev.append(("G0", l_idx, line, move_c, cmds)) OCV.blocks[-1].append(line) continue elif move in OCV.end_cmds: # catch the end commands OCV.blocks_ev.append((move, l_idx, line, move_c)) OCV.blocks[-1].append(line) else: # other 'moves' T, M () not catched as end_cmds and S OCV.blocks[-1].append(line) # one line to pass the work to Heuristic module single that take care # of susbsequent work on parsing and block splitting Heuristic.process_blocks() def add_line(self, line): """plain addLine method from bCNC used by setLinesUndo method and if no postprocessor is detected in GCode file """ if line.startswith("(-)"): return if line.startswith("(Block-name:"): self._blocksExist = True pat = OCV.RE_BLOCK.match(line) if pat: value = pat.group(2).strip() if not OCV.blocks or len(OCV.blocks[-1]): OCV.blocks.append(Block(value)) else: OCV.blocks[-1].b_name = value return if not OCV.blocks: OCV.blocks.append(Block("Header")) cmds = Heuristic.parse_line(line) if cmds is None: OCV.blocks[-1].append(line) return self.cnc.motionStart(cmds) # rapid move up = end of block if self._blocksExist: OCV.blocks[-1].append(line) elif self.cnc.gcode == 0 and self.cnc.dz > 0.0: OCV.blocks[-1].append(line) OCV.blocks.append(Block()) elif self.cnc.gcode == 0 and len(OCV.blocks) == 1: OCV.blocks.append(Block()) OCV.blocks[-1].append(line) else: OCV.blocks[-1].append(line) self.cnc.motionEnd() def load(self, filename=None): """Load a file into editor""" if filename is None: filename = self.filename self.init() self.filename = filename try: f_handle = open(self.filename, "r") except Exception as e: return False self._lastModified = os.stat(self.filename).st_mtime self.cnc.initPath() self.cnc.resetAllMargins() self._blocksExist = False for line in f_handle: # Add line to the gcodelines used for display and heuristic OCV.gcodelines.append(line[:-1].replace("\x0d", "")) f_handle.close() self.parse_gcode(filename, False) return True def save(self, filename=None): """Save to a file""" if filename is not None: self.filename = filename try: f = open(self.filename, "w") except Exception: return False for block in OCV.blocks: block.write(f) f.close() self._lastModified = os.stat(self.filename).st_mtime self._modified = False return True def saveNGC(self, filename, comments=False): """Save in NGC format Cleaned from Block OKKCNC metadata with or without comments """ f_handle = open(filename, 'w') for block in OCV.blocks: # print(block.enable) if block.enable: for line in block: if comments is False: cmds = Heuristic.parse_line(line) # print(cmds) if cmds is None: continue f_handle.write("{0}\n".format(line)) f_handle.close() return True def saveOKK(self, filename): """Save in OKK format with OKKCNC metadata and comments """ okkf = open(filename, 'w') for block in OCV.blocks: block.write(okkf) okkf.close() return True def addBlockFromString(self, name, text): if not text: return block = Block(name) block.extend(text.splitlines()) OCV.blocks.append(block) def headerFooter(self): """Check if Block is empty: If empty insert a header and a footer """ if not OCV.blocks: currDate = strftime("%Y-%m-%d - %H:%M:%S", localtime()) curr_header = "(Created By {0} version {1}) \n".format( OCV.PRG_NAME, OCV.PRG_VER) curr_header += "(Date: {0})\n".format(currDate) curr_header += self.header self.addBlockFromString("Header", curr_header) self.addBlockFromString("Footer", self.footer) return True return False def toPath(self, bid): """convert a block to path""" block = OCV.blocks[bid] paths = [] path = Path(block.name()) self.initPath(bid) start = bmath.Vector(self.cnc.x, self.cnc.y) # get only first path that enters the surface # ignore the deeper ones passno = 0 for line in block: # flatten helical paths line = re.sub(r"\s?z-?[0-9\.]+", "", line) # break after first depth pass if line == "( ---------- cut-here ---------- )": passno = 0 if path: paths.append(path) path = Path(block.name()) if line[:5] == "(pass": passno += 1 if passno > 1: continue cmds = Heuristic.parse_line(line) if cmds is None: continue self.cnc.motionStart(cmds) end = bmath.Vector(self.cnc.xval, self.cnc.yval) if self.cnc.gcode == 0: # rapid move (new block) if path: paths.append(path) path = Path(block.name()) elif self.cnc.gcode == 1: # line if self.cnc.dx != 0.0 or self.cnc.dy != 0.0: path.append(Segment(1, start, end)) elif self.cnc.gcode in (2, 3): # arc xc, yc = self.cnc.motionCenter() center = bmath.Vector(xc, yc) path.append(Segment(self.cnc.gcode, start, end, center)) self.cnc.motionEnd() start = end if path: paths.append(path) return paths def fromPath(self, path, block=None, z=None, retract=True, entry=False, exit=True, zstart=None, ramp=None, comments=True, exitpoint=None, truncate=None): """Create a block from Path @param z I ending depth @param zstart I starting depth """ # Recursion for multiple paths if not isinstance(path, Path): block = Block("new") for p in path: block.extend( self.fromPath( p, None, z, retract, entry, exit, zstart, ramp, comments, exitpoint, truncate)) block.append("( ---------- cut-here ---------- )") del block[-1] # remove trailing cut-here return block if z is None: z = self.cnc["surface"] if zstart is None: zstart = z # Calculate helix step zstep = abs(z-zstart) # Preprocess ramp if ramp is None: ramp = 0 if ramp == 0: ramp = path.length() # full helix (default) ramp = min(ramp, path.length()) # Never ramp longer than single pass! # Calculate helical feedrate helixfeed = self.cnc["cutfeed"] if zstep > 0: # Compensate helix feed # so we never plunge too fast on short/steep paths # FIXME: Add UI to disable this feature??? # Not sure if that's needed. rampratio = zstep/min(path.length(), ramp) helixfeed2 = round(self.cnc["cutfeedz"] / rampratio) helixfeed = min(self.cnc["cutfeed"], helixfeed2) if block is None: if isinstance(path, Path): block = Block(path.name) else: block = Block(path[0].name) def syncFileTime(self): """sync file timestamp""" try: self._lastModified = os.stat(self.filename).st_mtime except Exception: return False def checkFile(self): """Check if a new version exists""" try: return os.stat(self.filename).st_mtime > self._lastModified except Exception: return False def undo(self): """Undo operation""" # print ">u>",self.undoredo.undoText() self.undoredo.undo() def redo(self): """Redo operation""" # print ">r>",self.undoredo.redoText() self.undoredo.redo() def addUndo(self, undoinfo, msg=None): if not undoinfo: return self.undoredo.add(undoinfo, msg) self._modified = True def canUndo(self): return self.undoredo.canUndo() def canRedo(self): return self.undoredo.canRedo() def setLinesUndo(self, lines): """Change all lines in editor""" undoinfo = (self.setLinesUndo, list(self.lines())) # Delete all blocks and create new ones del OCV.blocks[:] self.cnc.initPath() self._blocksExist = False for line in lines: self.add_line(line) Heuristic.trim_blocks() return undoinfo def setAllBlocksUndo(self, blocks=[]): undoinfo = [self.setAllBlocksUndo, OCV.blocks] OCV.blocks = blocks return undoinfo def setLineUndo(self, bid, lid, line): """Change a single line in a block""" undoinfo = (self.setLineUndo, bid, lid, OCV.blocks[bid][lid]) OCV.blocks[bid][lid] = line return undoinfo def insLineUndo(self, bid, lid, line): """Insert a new line into block""" undoinfo = (self.delLineUndo, bid, lid) block = OCV.blocks[bid] if lid >= len(block): block.append(line) else: block.insert(lid, line) return undoinfo def cloneLineUndo(self, bid, lid): """Clone line inside a block""" return self.insLineUndo(bid, lid, OCV.blocks[bid][lid]) def delLineUndo(self, bid, lid): """Delete line from block""" block = OCV.blocks[bid] undoinfo = (self.insLineUndo, bid, lid, block[lid]) del block[lid] return undoinfo def addBlockUndo(self, bid, block): """Add a block""" if bid is None: bid = len(OCV.blocks) if bid >= len(OCV.blocks): undoinfo = (self.delBlockUndo, len(OCV.blocks)) OCV.blocks.append(block) else: undoinfo = (self.delBlockUndo, bid) OCV.blocks.insert(bid, block) return undoinfo def cloneBlockUndo(self, bid, pos=None): """Clone a block""" if pos is None: pos = bid return self.addBlockUndo(pos, Block(OCV.blocks[bid])) def delBlockUndo(self, bid): """Delete a whole block""" block = OCV.blocks.pop(bid) undoinfo = (self.addBlockUndo, bid, block) return undoinfo def insBlocksUndo(self, bid, blocks): """Insert a list of other blocks from another gcode file probably""" if bid is None or bid >= len(OCV.blocks): bid = len(OCV.blocks) undoinfo = ("Insert blocks", self.delBlocksUndo, bid, bid+len(blocks)) OCV.blocks[bid:bid] = blocks return undoinfo def delBlocksUndo(self, from_, to_): """Delete a range of blocks""" blocks = OCV.blocks[from_:to_] undoinfo = ("Delete blocks", self.insBlocksUndo, from_, blocks) del OCV.blocks[from_:to_] return undoinfo def insBlocks(self, bid, blocks, msg=""): """Insert blocks and push the undo info""" if self.headerFooter(): # just in case bid = 1 self.addUndo(self.insBlocksUndo(bid, blocks), msg) def setBlockExpandUndo(self, bid, expand): """Set block expand""" undoinfo = (self.setBlockExpandUndo, bid, OCV.blocks[bid].expand) OCV.blocks[bid].expand = expand return undoinfo def setBlockEnableUndo(self, bid, enable): """Set block state""" undoinfo = (self.setBlockEnableUndo, bid, OCV.blocks[bid].enable) OCV.blocks[bid].enable = enable return undoinfo def setBlockColorUndo(self, bid, color): """Set block color""" undoinfo = (self.setBlockColorUndo, bid, OCV.blocks[bid].color) OCV.blocks[bid].color = color return undoinfo def swapBlockUndo(self, a, b): """Swap two blocks""" undoinfo = (self.swapBlockUndo, a, b) tmp = OCV.blocks[a] OCV.blocks[a] = OCV.blocks[b] OCV.blocks[b] = tmp return undoinfo def moveBlockUndo(self, src, dst): """Move block from location src to location dst""" if src == dst: return None undoinfo = (self.moveBlockUndo, dst, src) if dst > src: OCV.blocks.insert(dst-1, OCV.blocks.pop(src)) else: OCV.blocks.insert(dst, OCV.blocks.pop(src)) return undoinfo def invertBlocksUndo(self, blocks): """Invert selected blocks""" undoinfo = [] first = 0 last = len(blocks) - 1 while first < last: undoinfo.append(self.swapBlockUndo(blocks[first], blocks[last])) first += 1 last = 1 return undoinfo def orderUpBlockUndo(self, bid): """Move block upwards""" if bid == 0: return None undoinfo = (self.orderDownBlockUndo, bid - 1) # swap with the block above before = OCV.blocks[bid-1] OCV.blocks[bid-1] = OCV.blocks[bid] OCV.blocks[bid] = before return undoinfo def orderDownBlockUndo(self, bid): """Move block downwards""" if bid >= len(OCV.blocks) - 1: return None undoinfo = (self.orderUpBlockUndo, bid+1) # swap with the block below after = self[bid+1] self[bid+1] = self[bid] self[bid] = after return undoinfo def insBlockLinesUndo(self, bid, lines): """Insert block lines""" undoinfo = (self.delBlockLinesUndo, bid) block = Block() for line in lines: block.append(line) OCV.blocks.insert(bid, block) return undoinfo def delBlockLinesUndo(self, bid): """Delete a whole block lines""" lines = [x for x in OCV.blocks[bid]] undoinfo = (self.insBlockLinesUndo, bid, lines) del OCV.blocks[bid] return undoinfo def setBlockNameUndo(self, bid, name): """Set Block name""" undoinfo = (self.setBlockNameUndo, bid, OCV.blocks[bid].b_name) OCV.blocks[bid].b_name = name return undoinfo def addBlockOperationUndo(self, bid, operation, remove=None): """Add an operation code in the name as [drill, cut, in/out...]""" undoinfo = (self.setBlockNameUndo, bid, OCV.blocks[bid].b_name) OCV.blocks[bid].addOperation(operation, remove) return undoinfo def setBlockLinesUndo(self, bid, lines): """Replace the lines of a block""" block = OCV.blocks[bid] undoinfo = (self.setBlockLinesUndo, bid, block[:]) del block[:] block.extend(lines) return undoinfo def orderUpLineUndo(self, bid, lid): """Move line upwards""" if lid == 0: return None block = OCV.blocks[bid] undoinfo = (self.orderDownLineUndo, bid, lid-1) block.insert(lid-1, block.pop(lid)) return undoinfo def orderDownLineUndo(self, bid, lid): """Move line downwards""" block = OCV.blocks[bid] if lid >= len(block) - 1: return None undoinfo = (self.orderUpLineUndo, bid, lid+1) block.insert(lid+1, block.pop(lid)) return undoinfo def autolevelBlock(self, block): """Expand block with autolevel information""" new = [] autolevel = not self.probe.isEmpty() for line in block: # newcmd = [] # seems to be not used cmds = CNC.compileLine(line) if cmds is None: new.append(line) continue elif isinstance(cmds, str): cmds = CNC.breakLine(cmds) else: new.append(line) continue self.cnc.motionStart(cmds) if autolevel and self.cnc.gcode in (0, 1, 2, 3) and\ self.cnc.mval == 0: xyz = self.cnc.motionPath() if not xyz: # while auto-levelling, do not ignore non-movement # commands, just append the line as-is new.append(line) else: extra = "" for c in cmds: if c[0].upper() not in ( 'G', 'X', 'Y', 'Z', 'I', 'J', 'K', 'R'): extra += c x1, y1, z1 = xyz[0] if self.cnc.gcode == 0: g = 0 else: g = 1 for x2, y2, z2 in xyz[1:]: for x, y, z in self.probe.splitLine( x1, y1, z1, x2, y2, z2): new.append("G{0:d} {1} {2} {3} {4}".format( g, OCV.fmt('X', x/OCV.unit), OCV.fmt('Y', y/OCV.unit), OCV.fmt('Z', z/OCV.unit), extra)) extra = "" x1, y1, z1 = x2, y2, z2 self.cnc.motionEnd() else: self.cnc.motionEnd() new.append(line) return new def autolevel(self, items): """Execute autolevel on selected blocks""" undoinfo = [] operation = "autolevel" for bid in items: block = OCV.blocks[bid] if block.name() in ("Header", "Footer"): continue if not block.enable: continue lines = self.autolevelBlock(block) undoinfo.append(self.addBlockOperationUndo(bid, operation)) undoinfo.append(self.setBlockLinesUndo(bid, lines)) if undoinfo: self.addUndo(undoinfo) def __repr__(self): """Return string representation of whole file""" return "\n".join(list(self.lines())) def iterate(self, items): """Iterate over the items""" for bid, lid in items: if lid is None: block = OCV.blocks[bid] for i in range(len(block)): yield bid, i else: yield bid, lid def lines(self): """Iterate over all lines""" for block in OCV.blocks: for line in block: yield line def initPath(self, bid=0): """initialize cnc path based on block bid""" if bid == 0: self.cnc.initPath() else: # Use the ending point of the previous block # since the starting (sxyz is after the rapid motion) block = OCV.blocks[bid-1] self.cnc.initPath(block.ex, block.ey, block.ez) def orderUp(self, items): """Move blocks/lines up""" sel = [] # new selection undoinfo = [] for bid, lid in items: if isinstance(lid, int): undoinfo.append(self.orderUpLineUndo(bid, lid)) sel.append((bid, lid - 1)) elif lid is None: undoinfo.append(self.orderUpBlockUndo(bid)) if bid == 0: return items else: sel.append((bid - 1, None)) self.addUndo(undoinfo, "Move Up") return sel def orderDown(self, items): """Move blocks/lines down""" sel = [] # new selection undoinfo = [] for bid, lid in reversed(items): if isinstance(lid, int): undoinfo.append(self.orderDownLineUndo(bid, lid)) sel.append((bid, lid + 1)) elif lid is None: undoinfo.append(self.orderDownBlockUndo(bid)) if bid >= len(OCV.blocks) - 1: return items else: sel.append((bid + 1, None)) self.addUndo(undoinfo, "Move Down") sel.reverse() return sel def close(self, items): """Close paths by joining end with start with a line segment""" undoinfo = [] for bid in items: block = OCV.blocks[bid] if block.name() in ("Header", "Footer"): continue undoinfo.append(self.insLineUndo( bid, OCV.MAXINT, self.cnc.gline(block.sx, block.sy))) self.addUndo(undoinfo) def info(self, bid): """Return information for a block return XXX """ # block = OCV.blocks[bid] # seems to be unused paths = self.toPath(bid) if not paths: return None, 1 if len(paths) > 1: closed = paths[0].isClosed() return int(closed), paths[0]._direction(closed) # No treatment for closed not 0 or 1 or None # len(paths) could return 2 or plus # return len(paths), paths[0]._direction(closed) else: closed = paths[0].isClosed() return int(closed), paths[0]._direction(closed) def modify(self, items, func, tabFunc, *args): """Modify the lines according to the supplied function and arguments""" undoinfo = [] old = {} # Motion commands: Last value new = {} # Motion commands: New value relative = False for bid, lid in self.iterate(items): block = OCV.blocks[bid] if isinstance(lid, int): cmds = Heuristic.parse_line(block[lid]) if cmds is None: continue self.cnc.motionStart(cmds) # Collect all values new.clear() for cmd in cmds: if cmd.upper() == 'G91': relative = True if cmd.upper() == 'G90': relative = False c = cmd[0].upper() # record only coordinates commands if c not in "XYZIJKR": continue try: new[c] = old[c] = float(cmd[1:])*OCV.unit except Exception: new[c] = old[c] = 0.0 # Modify values with func if func(new, old, relative, *args): # Reconstruct new line newcmd = [] present = "" for cmd in cmds: c = cmd[0].upper() if c in "XYZIJKR": # Coordinates newcmd.append(OCV.fmt(c, new[c]/OCV.unit)) elif c == "G" and int(cmd[1:]) in (0, 1, 2, 3): # Motion newcmd.append("G{0}".format(self.cnc.gcode)) else: # the rest leave unchanged newcmd.append(cmd) present += c # Append motion commands if not exist and changed check = "XYZ" if 'I' in new or 'J' in new or 'K' in new: check += "IJK" for c in check: try: if c not in present and new.get(c) != old.get(c): newcmd.append( OCV.fmt(c, new[c]/OCV.unit)) except Exception: pass undoinfo.append( self.setLineUndo(bid, lid, " ".join(newcmd))) self.cnc.motionEnd() # reset arc offsets for i in "IJK": if i in old: old[i] = 0.0 # FIXME I should add it later, check all functions using it self.addUndo(undoinfo) def moveFunc(self, new, old, relative, dx, dy, dz): """Move position by dx,dy,dz""" if relative: return False changed = False if 'X' in new: changed = True new['X'] += dx if 'Y' in new: changed = True new['Y'] += dy if 'Z' in new: changed = True new['Z'] += dz return changed def orderLines(self, items, direction): """Order Lines""" if direction == "UP": self.orderUp(items) elif direction == "DOWN": self.orderDown(items) else: pass def moveLines(self, items, dx, dy, dz=0.0): """Move position by dx,dy,dz""" return self.modify(items, self.moveFunc, None, dx, dy, dz) def rotateFunc(self, new, old, relative, c, s, x0, y0): """Rotate position by c(osine), s(ine) of an angle around center (x0,y0) """ if 'X' not in new and 'Y' not in new: return False x = get_dict_value('X', new, old) y = get_dict_value('Y', new, old) new['X'] = c*(x-x0) - s*(y-y0) + x0 new['Y'] = s*(x-x0) + c*(y-y0) + y0 if 'I' in new or 'J' in new: i = get_dict_value('I', new, old) j = get_dict_value('J', new, old) if self.cnc.plane in (OCV.CNC_XY, OCV.CNC_XZ): new['I'] = c*i - s*j if self.cnc.plane in (OCV.CNC_XY, OCV.CNC_YZ): new['J'] = s*i + c*j return True def transformFunc(self, new, old, relative, c, s, xo, yo): """Transform (rototranslate) position with the following function: xn = c*x - s*y + xo yn = s*x + c*y + yo it is like the rotate but the rotation center is not defined """ if 'X' not in new and 'Y' not in new: return False x = get_dict_value('X', new, old) y = get_dict_value('Y', new, old) new['X'] = c*x - s*y + xo new['Y'] = s*x + c*y + yo if 'I' in new or 'J' in new: i = get_dict_value('I', new, old) j = get_dict_value('J', new, old) new['I'] = c*i - s*j new['J'] = s*i + c*j return True def rotateLines(self, items, ang, x0=0.0, y0=0.0): """Rotate items around optional center (on XY plane) ang in degrees (counter-clockwise) """ a = math.radians(ang) c = math.cos(a) s = math.sin(a) if ang in (0.0, 90.0, 180.0, 270.0, -90.0, -180.0, -270.0): # round numbers to avoid nasty extra digits c = round(c) s = round(s) return self.modify(items, self.rotateFunc, None, c, s, x0, y0) def orientLines(self, items): """Use the orientation information to orient selected code""" if not self.orient.valid: return "ERROR: Orientation information is not valid" c = math.cos(self.orient.phi) s = math.sin(self.orient.phi) return self.modify( items, self.transformFunc, None, c, s, self.orient.xo, self.orient.yo) def mirrorHFunc(self, new, old, relative, *kw): """Mirror Horizontal""" changed = False for axis in 'XI': if axis in new: new[axis] = -new[axis] changed = True if self.cnc.gcode in (2, 3): # Change 2<->3 self.cnc.gcode = 5 - self.cnc.gcode changed = True return changed def mirrorVFunc(self, new, old, relative, *kw): """Mirror Vertical""" changed = False for axis in 'YJ': if axis in new: new[axis] = -new[axis] changed = True if self.cnc.gcode in (2, 3): # Change 2<->3 self.cnc.gcode = 5 - self.cnc.gcode changed = True return changed def mirrorHLines(self, items): """Mirror horizontally""" return self.modify(items, self.mirrorHFunc, None) def mirrorVLines(self, items): """"Mirror vertically""" return self.modify(items, self.mirrorVFunc, None) def roundFunc(self, new, old, relative): """Round all digits with accuracy""" for name, value in new.items(): new[name] = round(value, OCV.digits) return bool(new) def roundLines(self, items, acc=None): """Round line by the amount of digits""" if acc is not None: OCV.digits = acc return self.modify(items, self.roundFunc, None) def removeNlines(self, items): """Remove the line number for lines""" pass def optimize(self, items): """Re-arrange using genetic algorithms a set of blocks to minimize rapid movements. """ n = len(items) matrix = [] for i in range(n): matrix.append([0.0] * n) # Find distances between blocks (end to start) for i in range(n): block = OCV.blocks[items[i]] x1 = block.ex y1 = block.ey for j in range(n): if i == j: continue block = OCV.blocks[items[j]] x2 = block.sx y2 = block.sy dx = x1-x2 dy = y1-y2 # Compensate for machines, # which have different speed of X and Y: dx /= OCV.feedmax_x dy /= OCV.feedmax_y matrix[i][j] = math.sqrt(dx*dx + dy*dy) # from pprint import pprint # pprint(matrix) best = [0] unvisited = range(1, n) while unvisited: last = best[-1] row = matrix[last] # from all the unvisited places search the closest one mindist = 1e30 for i, u in enumerate(unvisited): d = row[u] if d < mindist: mindist = d si = i best.append(unvisited.pop(si)) # print "best=",best undoinfo = [] for i in range(len(best)): b = best[i] if i == b: continue ptr = best.index(i) # swap i,b in items undoinfo.append(self.swapBlockUndo(items[i], items[b])) # swap i,ptr in best best[i], best[ptr] = best[ptr], best[i] self.addUndo(undoinfo, "Optimize") def comp_level(self, queue, stopFunc=None): """Use probe information (if exist) to modify the g-code to autolevel""" paths = [] # empty the gctos value OCV.gctos = [] def add(line, path): if line is not None: if isinstance(line, str): queue.put(line + "\n") OCV.gctos.append(line) else: queue.put(line) OCV.gctos.append(line) paths.append(path) # check the existence of an autolevel file autolevel = not self.probe.isEmpty() self.initPath() for line in CNC.compile_pgm(OCV.startup.splitlines()): add(line, None) every = 1 for i, block in enumerate(OCV.blocks): if not block.enable: continue for j, line in enumerate(block): every -= 1 if every <= 0: if stopFunc is not None and stopFunc(): return None every = 50 newcmd = [] cmds = CNC.compileLine(line) if cmds is None: continue elif isinstance(cmds, str): cmds = CNC.breakLine(cmds) else: # either CodeType or tuple, list[] append at it as is if isinstance(cmds, types.CodeType) or\ isinstance(cmds, int): add(cmds, None) else: add(cmds, (i, j)) continue skip = False expand = None self.cnc.motionStart(cmds) # FIXME append feed on cut commands. # It will be obsolete in grbl v1.0 if OCV.appendFeed and self.cnc.gcode in (1, 2, 3): # Check is not existing in cmds for c in cmds: if c[0] in ('f', 'F'): break else: cmds.append( OCV.fmt( 'F', self.cnc.feed / OCV.unit)) if autolevel and self.cnc.gcode in (0, 1, 2, 3) and \ self.cnc.mval == 0: xyz = self.cnc.motionPath() if not xyz: # while auto-levelling, do not ignore non-movement # commands, just append the line as-is add(line, None) else: extra = "" for c in cmds: if c[0].upper() not in ( 'G', 'X', 'Y', 'Z', 'I', 'J', 'K', 'R'): extra += c x1, y1, z1 = xyz[0] if self.cnc.gcode == 0: g = 0 else: g = 1 for x2, y2, z2 in xyz[1:]: for x, y, z in self.probe.splitLine( x1, y1, z1, x2, y2, z2): add("G{0:d} {1} {2} {3} {4}".format( g, OCV.fmt('X', x/OCV.unit), OCV.fmt('Y', y/OCV.unit), OCV.fmt('Z', z/OCV.unit), extra), (i, j)) extra = "" x1, y1, z1 = x2, y2, z2 self.cnc.motionEnd() continue else: # FIXME expansion policy here variable needed # Canned cycles if OCV.drillPolicy == 1 and \ self.cnc.gcode in (81, 82, 83, 85, 86, 89): expand = self.cnc.macroGroupG8X() # Tool change elif self.cnc.mval == 6: if OCV.toolPolicy == 0: # send to grbl pass elif OCV.toolPolicy == 1: # skip whole line skip = True elif OCV.toolPolicy >= 2: expand = CNC.compile_pgm(self.cnc.toolChange()) self.cnc.motionEnd() if expand is not None: for line in expand: add(line, None) expand = None continue elif skip: skip = False continue for cmd in cmds: c = cmd[0] try: value = float(cmd[1:]) except Exception: value = 0.0 if c.upper() in ( "F", "X", "Y", "Z", "I", "J", "K", "R", "P"): cmd = OCV.fmt(c, value) else: opt = OCV.ERROR_HANDLING.get(cmd.upper(), 0) if opt == OCV.GSTATE_SKIP: cmd = None if cmd is not None: newcmd.append(cmd) add("".join(newcmd), (i, j)) return paths
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 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 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 execute(self, app): name = self["name"] if not name or name=="default": name="Default Name" sel_Blocks = self["Sel_Blocks"] #Get inputs x = self["X"] y = self["Y"] z = self["Z"] if z == "": z=CNC.vars["surface"] cutDiam = self["CutDiam"] cutRadius = cutDiam/2.0 if self["endmill"]: self.master["endmill"].makeCurrent(self["endmill"]) toolDiam = CNC.vars["diameter"] #Radio = self["RadioHelix"] pitch = self["Pitch"] Depth = self["Depth"] Mult_F_Z = self["Mult_Feed_Z"] helicalCut = self["HelicalCut"] clearanceEntry = self["ClearanceEntry"] clearanceExit = self["ClearanceExit"] clearance = clearanceEntry entry = self["Entry"] returnToSafeZ = self["ReturnToSafeZ"] toolDiam = CNC.vars['diameter'] toolRadius = toolDiam/2.0 Radio = cutRadius - toolRadius if(Radio < 0): Radio = 0 toolDiam = CNC.vars['diameter'] toolRadius = toolDiam/2.0 Radio = cutRadius - toolRadius if clearanceEntry =="": clearanceEntry =0 if clearanceExit =="": clearanceExit =0 if helicalCut == "Helical Cut": turn = 2 p="HelicalCut " elif helicalCut == "Internal Right Thread": turn = 2 p= "IntRightThread " elif helicalCut == "Internal Left Thread": turn = 3 p= "IntLeftThread " elif helicalCut == "External Right Thread": Radio = cutRadius + toolRadius turn = 2 p= "ExtRightThread " elif helicalCut == "External Left Thread": Radio = cutRadius + toolRadius turn = 3 p= "ExtLeftThread " # ------------------------------------------------------------------------------------------------------------------ #Check inputs if sel_Blocks == 0: if x == "" or y == "" : app.setStatus(_("If block selected false, please make a value of x")) return elif helicalCut == "": app.setStatus(_("Helical Abort: Please select helical type")) return elif cutDiam < toolDiam or cutDiam == "": app.setStatus(_("Helical Abort: Helix diameter must be greater than the end mill")) return elif cutDiam <= 0: app.setStatus(_("Helical Abort: Helix diameter must be positive")) return elif pitch <= 0 or pitch =="": app.setStatus(_("Helical Abort: Drop must be greater than 0")) return elif Mult_F_Z <= 0 or Mult_F_Z == "": app.setStatus(_("Helical Abort: Z Feed Multiplier must be greater than 0")) return elif entry == "": app.setStatus(_("Helical Abort: Please selecte Entry and Exit type")) return elif clearanceEntry < 0 or clearanceEntry == "": app.setStatus(_("Helical Abort: Entry Edge Clearence may be positive")) return elif clearanceExit < 0 or clearanceExit == "": app.setStatus(_("Helical Abort: Exit Edge Clearence may be positive")) return # ------------------------------------------------------------------------------------------------------------------ #Initialize blocks that will contain our gCode blocks = [] #block = Block(name) block = Block( p + str(cutDiam) + " Pitch " + str(pitch) + " Bit " + str(toolDiam) + " depth " + str(Depth)) cutFeed = CNC.vars["cutfeedz"] #<<< Get cut feed Z for the current material cutFeedMax = CNC.vars["cutfeed"] #<<< Get cut feed XY for the current material # ------------------------------------------------------------------------------------------------------------------ # Get selected blocks from editor selBlocks = app.editor.getSelectedBlocks() if not selBlocks: app.editor.selectAll() selBlocks = app.editor.getSelectedBlocks() if not selBlocks: if sel_Blocks == 1: app.setStatus(_("Helical abort: Please select some path")) return # ------------------------------------------------------------------------------------------------------------------ # Get selected blocks from editor if sel_Blocks == 1: selBlocks = app.editor.getSelectedBlocks() if not selBlocks: app.editor.selectAll() selBlocks = app.editor.getSelectedBlocks() #Get all segments from gcode allSegments = self.extractAllSegments(app,selBlocks) #Create holes locations allHoles=[] for bidSegment in allSegments: if len(bidSegment)==0: continue bidHoles = [] for idx, anchor in enumerate(bidSegment): if idx ==2: newHolePoint = (anchor[0][0],anchor[0][1],anchor[0][2]) bidHoles.append(newHolePoint) #Add bidHoles to allHoles allHoles.append(bidHoles) # ------------------------------------------------------------------------------------------------------------------ holesCount = 0 for bid in allHoles: for xH,yH,zH in bid: x = xH y = yH # ------------------------------------------------------------------------------------------------------------------ # Init: Adjust feed and rapid move to Z safe if Mult_F_Z is"": Mult_F_Z = 1 if Mult_F_Z == 0: Mult_F_Z = 1 if Mult_F_Z * cutFeed > cutFeedMax: cutFeed = cutFeedMax else: cutFeed = cutFeed*Mult_F_Z block.append(CNC.zsafe()) #<<< Move rapid Z axis to the safe height in Stock Material # Move rapid to X and Y coordinate if helicalCut == "Helical Cut" or helicalCut == "Internal Right Thread" or helicalCut == "Internal Left Thread": if entry == "Center": block.append(CNC.grapid(x,y)) else: block.append(CNC.grapid(x-Radio+clearance ,y)) if helicalCut == "External Right Thread" or helicalCut == "External Left Thread": if entry == "Center": clearance = 0.0 block.append(CNC.grapid(x-Radio-clearance ,y)) #cutFeed = int(cutFeed) block.append(CNC.fmt("f",cutFeed)) #<<< Set cut feed # block.append(CNC.gline(x,y) # while (z < 1): block.append(CNC.zenter(z)) block.append(CNC.gline(x-Radio,y)) # cutFeed = int((CNC.vars["cutfeed"] + CNC.vars["cutfeedz"])/2) #<<< Get cut feed for the current material #cutFeed = int(cutFeed) block.append(CNC.fmt("F",cutFeed)) #<<< Set cut feed #----------------------------------------------------------------------------------------------------- # Uncomment for first flat pass if helicalCut == "Helical Cut": block.append(CNC.gcode(turn, [("X",x-Radio),("Y",y),("Z", z),("I",Radio), ("J",0)])) #----------------------------------------------------------------------------------------------------- if (z < Depth): pitch = -pitch while ((z-pitch) < Depth) : z = z-pitch block.append(CNC.gcode(turn, [("X",x-Radio),("Y",y),("Z", z),("I",Radio), ("J",0)])) else: while ((z-pitch) >= Depth) : z = z-pitch block.append(CNC.gcode(turn, [("X",x-Radio),("Y",y),("Z", z),("I",Radio), ("J",0)])) #Target Level if entry == "Center": clearanceExit = 0.0 clearance = clearanceExit alpha= round(Depth / pitch, 4 ) - round(Depth / pitch, 0) alpha = alpha * 2*pi Radiox = Radio * cos(alpha) Radioy = Radio * sin(alpha) xsi = Radiox - clearance* cos(alpha) ysi =Radioy - clearance* sin(alpha) xse = Radiox + clearance* cos(alpha) yse =Radioy + clearance* sin(alpha) z = Depth if helicalCut == "Helical Cut": block.append(CNC.gcode(turn, [("X",x-Radio),("Y",y),("Z", z),("I",Radio), ("J",0)])) #Last flat pass block.append(CNC.gcode(turn, [("X",x-Radio),("Y",y),("Z", z),("I",Radio), ("J",0)])) elif helicalCut == "Internal Right Thread" or helicalCut == "External Right Thread": block.append(CNC.gcode(turn, [("X",x-Radiox),("Y",y-Radioy),("Z", z),("I",Radio), ("J",0)])) elif helicalCut == "Internal Left Thread" or helicalCut == "External Left Thread": block.append(CNC.gcode(turn, [("X",x-Radiox),("Y",y+Radioy),("Z", z),("I",Radio), ("J",0)])) # Exit clearance if helicalCut == "Internal Right Thread": block.append(CNC.gline(x-xsi,y-ysi)) elif helicalCut == "Internal Left Thread": block.append(CNC.gline(x-xsi,y+ysi)) if helicalCut == "External Right Thread": block.append(CNC.gline(x-xse,y-yse)) elif helicalCut == "External Left Thread": block.append(CNC.gline(x-xse,y+yse)) # Return to Z Safe if returnToSafeZ == 1: if helicalCut == "Helical Cut" or helicalCut == "Internal Right Thread" or helicalCut == "Internal Left Thread": if entry == "Center": block.append(CNC.gline(x,y)) block.append(CNC.zsafe()) blocks.append(block) active = app.activeBlock() app.gcode.insBlocks(active, blocks, "Helical_Descent inserted") #<<< insert blocks over active block in the editor app.refresh() #<<< refresh editor app.setStatus(_("Generated: Helical_Descent Result")) #<<< feed back result
def execute(self, app): try: from PIL import Image except: app.setStatus( _("Sketch abort: This plugin requires PIL/Pillow to read image data" )) return n = self["name"] if not n or n == "default": n = "Sketch" #Calc desired size grundgy = self["Grundgy"] maxSize = self["MaxSize"] squiggleTotal = self["SquiggleTotal"] squiggleLength = self["SquiggleLength"] depth = self["Depth"] drawBorder = self["DrawBorder"] channel = self["Channel"] 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 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 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 trochoid(self, typesplice, A, B, oldradius, radius, oldphi,phi, cw=True): if self["splicesteps"] =="" or self["splicesteps"]<4: steps=4/(2*pi) else: steps=self["splicesteps"]/(2*pi) t_splice = typesplice block = [] if cw: u = 1 arc = "G2" cut_splice="G3" else: u = -1 arc = "G3" cut_splice="G2" # phi = atan2(B[1]-A[1], B[0]-A[0]) # step = sqrt((A[0]-B[0])**2+(A[1]-B[1])**2) l = self.pol2car(radius, phi+radians(90*u)) r = self.pol2car(radius, phi+radians(-90*u)) al = self.pol2car(oldradius, phi+radians(90*u), A) ar = self.pol2car(oldradius, phi+radians(-90*u), A) bl = self.pol2car(radius, phi+radians(90*u), B) br = self.pol2car(radius, phi+radians(-90*u), B) # prev_al = self.pol2car(oldradius, phi+radians(90*u), A) # prev_ar = self.pol2car(oldradius, phi+radians(-90*u), A) # old_l = self.pol2car(oldradius, oldphi+radians(90*u)) # old_r = self.pol2car(oldradius, oldphi+radians(-90*u)) # infinite radius inf_radius=1 # inf_l = self.pol2car(500*radius, phi+radians(90*u)) # inf_r = self.pol2car(500*radius, phi+radians(-90*u)) # inf_Cl = self.pol2car(inf_radius*radius, phi+radians(-90*u), B) # inf_Cl= inf_Cl[0],inf_Cl[1],B[2] # inf_Cr = self.pol2car(inf_radius*radius, phi+radians(90*u), B) # inf_Cl= inf_Cr[0],inf_Cr[1],B[2] splice_dist=sqrt((br[0]-al[0])**2+(br[1]-al[1])**2) splice_radius=splice_dist/2.0 # This schematic drawing represents naming convention # of points and vectors calculated in previous block # # <--L--- # ---R--> # # * * # * * # * * # BL B BR # * * # * ^ * # * | * # * | * # * * # AL A AR # * * # * * # * * #TODO: improve strategies # block.append("g1 x"+str(al[0])+" y"+str(al[1])+" z"+str(A[2])) # First splice to next trochoid ======================================================== # steps=self["splicesteps"] # steps = max(steps*oldradius,steps*radius)#self["splicesteps"] # steps = int(min(10,steps))#self["splicesteps"] block.append("(---------trochoid center x "+str(round(B[0],4))+" y "+str(round(B[1],4))+" ---------)") if t_splice == "came_back": # block.append(CNC.gline(round(A[0],5),round(A[1],5),round(B[2],5))) block.append(CNC.gline(round(B[0],5),round(B[1],5),round(B[2],5))) # block.append(arc+" x"+str(round(br[0],4))+" y"+str(round(br[1],4))+" R"+str(1.001*radius/2.0)+" z"+str(round(B[2],4))) block.append(arc+" x"+str(round(br[0],4))+" y"+str(round(br[1],4))+" R"+str(self.roundup(radius/2.0,3))+" z"+str(round(B[2],4))) # block.extend(self.splice_generator(steps,A,B,oldradius,radius/2.0,oldphi,phi,radians(270),radians(60),u)) # block.append("(ppp)") # block.extend(self.splice_generator(steps,B,B,radius/2.0,radius,phi,phi,radians(60),radians(-90),u)) else: block.append("( phi "+str(round(degrees(phi),2))+" oldphi "+str(round(degrees(oldphi),2))+" )") if oldphi!= phi: block.append("(=============== Direction changed olldhi ==================)") #if oldphi!=phi and t_splice!="Splices" and t_splice!="Warpedarc" and t_splice!="Circular one side rectified": # steps = int(15*radius) # block.append("()") # if t_splice!="Circular both sides rectified" : block.append("( Splice arch for direction change )") # block.extend(self.splice_generator(A,A,oldradius,oldradius,oldphi,phi,radians(270),radians(-90),u)) # block.extend(self.curve_splice_generator(A,A,oldradius,oldradius,oldphi,phi,radians(270),radians(-90),u,steps)) block.extend(self.curve_splice_generator(A,A,oldradius,oldradius,oldphi,phi,radians(270),radians(-90),u,4)) #<< steps=4 block.append("(=============== End Direction changed ==================)") # block.append("(new phi)") # block.append(CNC.gline(old_ar[0],old_ar[1])) # block.append(arc +" x"+str(ar[0])+" y"+str(ar[1])+" i"+str(-old_r[0])+" j"+str(-old_r[1])) #<<in adaptativee presents problems of location of the center in change of angle # block.append(arc +" x"+str(prev_ar[0])+" y"+str(prev_ar[1])+" i"+str(-old_r[0])+" j"+str(-old_r[1])) #<<in adaptativee presents problems of location of the center in change of angle # block.append(arc +" x"+str(prev_al[0])+" y"+str(prev_al[1])+" R"+str(oldradius)) # block.append(arc+" x"+str(prev_al[0])+" y"+str(prev_al[1])+" i"+str(-old_l[0])+" j"+str(-old_l[1])+" )") # block.append(arc +" x"+str(prev_ar[0])+" y"+str(prev_ar[1])+" R"+str(oldradius)) # else: # block.append("( Splice arch for direction change )") # block.append(arc +" x"+str(al[0])+" y"+str(al[1])+" i"+str(old_r[0])+" j"+str(old_r[1])) # block.append(arc +" x"+str(round(ar[0],4))+" y"+str(round(ar[1],4))+" R"+str(self.roundup(oldradius,3))) # block.append(arc +" x"+str(round(al[0],4))+" y"+str(round(al[1],4))+" R"+str(self.roundup(oldradius,3))) # block.append("()") if t_splice == "Splices": # block.append("(Soft steps "+str(steps)+" )") block.extend(self.splice_generator(A,B,oldradius,radius,oldphi,phi,radians(-90),radians(90),u,steps)) elif t_splice == "Circular one side rectified": # block.append(arc+" x"+str(al[0])+" y"+str(al[1])+" i"+str(old_l[0])+" j"+str(old_l[1])+" z"+str(round(B[2],5)))#<<in adaptativee presents problems of location of the center in change of angle block.append(arc+" x"+str(round(al[0],4))+" y"+str(round(al[1],4))+" R"+str(self.roundup(oldradius,3))+" z"+str(round(B[2],5))) block.append("g1 x"+str(round(bl[0],4))+" y"+str(round(bl[1],4))+" z"+str(round(B[2],4))) elif t_splice == "Circular both sides rectified" : block.append("g1 x"+str(round(bl[0],4))+" y"+str(round(bl[1],4))+" z"+str(round(B[2],4))) elif t_splice == "Straight": block.append("g1 x"+str(round(bl[0],4))+" y"+str(round(bl[1],4))+" z"+str(round(B[2],4))) elif t_splice == "Warpedarc": warped_radius=(sqrt((ar[0]-bl[0])**2+(ar[1]-bl[1])**2))/2.0 block.append("(Warpedarc)") block.append("(control previous position arx "+str(round(ar[0],4))+" ary "+str(round(ar[1],4))+" R "+ str(warped_radius)+" )") block.append(arc+" x"+str(round(bl[0],4))+" y"+str(round(bl[1],4))+" R"+str(self.roundup(warped_radius,3))+" z"+str(round(B[2],5))) elif t_splice == "Straight on side rectified": block.append("g1 x"+str(round(al[0],4))+" y"+str(round(al[1],4))) block.append("g1 x"+str(round(bl[0],4))+" y"+str(round(bl[1],4))+" z"+str(round(B[2],4))) elif t_splice == "Cut": block.append("g1 x"+str(round(ar[0],4))+" y"+str(round(ar[1],4))) block.append(cut_splice+"x"+str(round(bl[0],4))+" y"+str(round(bl[1],4))+"R"+str(self.roundup(splice_radius,3))+" z"+str(round(B[2],4))) # ======================================================================================================== # cut # 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))) block.append("(cuting)") block.append(arc+" x"+str(round(br[0],4))+" y"+str(round(br[1],4))+" R"+str(self.roundup(radius,3))+" z"+str(round(B[2],4))) # block.append("(cut)") # ======================================================================================================== if t_splice== "Circular both sides rectified": block.append("g1 x"+str(round(ar[0],4))+" y"+str(round(ar[1],4))+" z"+str(round(A[2],4))) # block.append(arc+" x"+str(al[0])+" y"+str(al[1])+" i"+str(old_l[0])+" j"+str(old_l[1])+" z"+str(round(A[2],5))) block.append(arc+" x"+str(round(al[0],4))+" y"+str(round(al[1],4))+" R"+str(self.roundup(oldradius,3))+" z"+str(round(A[2],4))) #<<in adaptativee presents problems of location of the center in change of angle block.append("g1 x"+str(round(bl[0],4))+" y"+str(round(bl[1],4))+" z"+str(round(B[2],4))) return block
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): name = self['name'] if not name or name == 'default': name = 'Function' # Initialize blocks that will contain our gCode blocks = [] block = Block(name) #Variable definitions formula = self['form'] res = self['res'] # X resolution ran = [self['ranX'], self['ranY']] # Range of X,Y, from -10, to 10 range is 20 cent = [ self['centX'], self['centY'] ] # Coordinates X,Y of the center from bottom left of the coordinate system dim = [self['dimX'], self['dimY']] # Real dimensions in gcode units spacX = self['spacX'] # Spacing of X axis lines spacY = self['spacY'] # Spacing of Y axis lines lin = self['lin'] # Small value - length of a line in gcode units draw = self['draw'] # Draw the coordinate system block.append("(Generated with a script by kswiorek)\n") block.append("(Equation: " + formula + ")\n") block.append("(Resolution: " + str(res) + ")\n") block.append("(Range: " + str(ran) + ")\n") block.append("(Center: " + str(cent) + ")\n") block.append("(Dimensions: " + str(dim) + ")\n") block.append("(SpacingXY: " + str(spacX) + ", " + str(spacY) + ")\n") def mapc(var, axis): #Map coordinate systems return (var * (dim[axis] / ran[axis])) #Define coordinate system mins and maxes minX = -cent[0] maxX = ran[0] - cent[0] minY = -cent[1] maxY = ran[1] - cent[1] #Define domain and codomain X = [] Y = [] e_old = "" #Store old exception to comapre #Calculate values for arguments with a resolution for i in range(0, int(ran[0] / res + 1)): #Complaints about values beeing floats x = i * res + minX #Iterate x X.append(x) try: Y.append(eval(formula)) except Exception as exc: #Append None, not to loose sync with X Y.append(None) e = str(exc) if e != e_old: #If there is a different exception - display it print("Warning: " + str(e)) app.setStatus(_("Warning: " + str(e))) e_old = e raised = True # Z axis is raised at start #Clip values out of bounds, replace with None, not to loose sync with X for i, item in enumerate(Y): y = Y[i] if not y is None and (y < minY or y > maxY): Y[i] = None #Y without "None", min() and max() can't compare them Ynn = [] #Y no Nones for i, item in enumerate(Y): if not Y[i] is None: Ynn.append(Y[i]) block.append(CNC.gcode(1, [("f", CNC.vars["cutfeed"])])) #Set feedrate if draw: #If the user selected to draw the coordinate system #X axis block.append(CNC.grapid(z=3)) block.append(CNC.grapid(0, mapc(cent[1], 1))) #1st point of X axis line block.append(CNC.grapid(z=0)) block.append(CNC.gline(dim[0] + lin * 1.2, mapc( cent[1], 1))) #End of X axis line + a bit more for the arrow block.append( CNC.gline(dim[0] - lin / 2, mapc(cent[1], 1) - lin / 2)) #bottom part of the arrow block.append(CNC.grapid(z=3)) block.append(CNC.grapid(dim[0] + lin * 1.2, mapc(cent[1], 1), 0)) #End of X axis line block.append(CNC.grapid(z=0)) block.append( CNC.gline(dim[0] - lin / 2, mapc(cent[1], 1) + lin / 2)) #top part of the arrow block.append(CNC.grapid(z=3)) #Y axis, just inverted x with y block.append(CNC.grapid(z=3)) block.append(CNC.grapid(mapc(cent[0], 0), 0)) #1st point of Y axis line block.append(CNC.grapid(z=0)) block.append(CNC.gline( mapc(cent[0], 0), dim[1] + lin * 1.2)) #End of Y axis line + a bit more for the arrow block.append( CNC.gline(mapc(cent[0], 0) - lin / 2, dim[1] - lin / 2)) #left part of the arrow block.append(CNC.grapid(z=3)) block.append(CNC.grapid(mapc(cent[0], 0), dim[1] + lin * 1.2)) #End of Y axis line block.append(CNC.grapid(z=0)) block.append( CNC.gline(mapc(cent[0], 0) + lin / 2, dim[1] - lin / 2)) #right part of the arrow block.append(CNC.grapid(z=3)) #X axis number lines i = 0 while i < ran[0] - cent[0]: #While i is on the left of the arrow i += spacX #Add line spacing #Draw lines right of the center block.append( CNC.grapid(mapc(i + cent[0], 0), mapc(cent[1], 1) + lin / 2)) block.append(CNC.grapid(z=0)) block.append( CNC.gline(mapc(i + cent[0], 0), mapc(cent[1], 1) - lin / 2)) block.append(CNC.grapid(z=3)) i = 0 while i > -cent[ 0]: #While i is lower than center coordinate, inverted for easier math i -= spacX #Add line spacing #Draw lines left of the center block.append( CNC.grapid(mapc(i + cent[0], 0), mapc(cent[1], 1) + lin / 2)) block.append(CNC.grapid(z=0)) block.append( CNC.gline(mapc(i + cent[0], 0), mapc(cent[1], 1) - lin / 2)) block.append(CNC.grapid(z=3)) #Y axis number lines i = 0 while i < ran[1] - cent[ 1]: #While i is between the center and the arrow i += spacX #Add line spacing #Draw lines top of the center (everything just inverted) block.append( CNC.grapid( mapc(cent[0], 0) + lin / 2, mapc(i + cent[1], 1))) block.append(CNC.grapid(z=0)) block.append( CNC.gline( mapc(cent[0], 0) - lin / 2, mapc(i + cent[1], 1))) block.append(CNC.grapid(z=3)) i = 0 while i > -1 * cent[1]: i -= spacX #Add line spacing #Draw lines bottom of the center block.append( CNC.grapid( mapc(cent[0], 0) + lin / 2, mapc(i + cent[1], 1))) block.append(CNC.grapid(z=0)) block.append( CNC.gline( mapc(cent[0], 0) - lin / 2, mapc(i + cent[1], 1))) block.append(CNC.grapid(z=3)) raised = True #Z was raised #Draw graph for i, item in enumerate(Y): if not Y[i] is None: x = mapc(X[i] + cent[0], 0) #Take an argument y = mapc(Y[i] + cent[1], 1) #Take a value else: y = Y[i] #only for tne None checks next if y is None and not raised: #If a None "period" just started raise Z raised = True block.append(CNC.grapid(z=3)) elif not y is None and raised: #If Z was raised and the None "period" ended move to new coordinates block.append(CNC.grapid(round(x, 2), round(y, 2))) block.append(CNC.grapid(z=0)) #Lower Z raised = False elif not y is None and not raised: #Nothing to do with Nones? Just draw block.append(CNC.gline(round(x, 2), round(y, 2))) block.append(CNC.grapid(z=3)) #Raise on the end blocks.append(block) active = app.activeBlock() app.gcode.insBlocks(active, blocks, 'Function inserted' ) #insert blocks over active block in the editor app.refresh() #refresh editor app.setStatus(_('Generated function graph')) #feed back result print()
def execute(self, app): try: 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 make(self,app, XStart=0.0, YStart=0.0, ZStart=30., AlignAxis="Y", \ RotAxis="A", StockLeng=20, ReduceDepth=-1, PassDepth=1, \ Stepover=1, ZApproach=35, SpiralType="Spiral", CutBoth="True", LiftPass="******"): #GCode Blocks blocks = [] # Load tool and material settings toolDiam = CNC.vars['diameter'] toolRadius = toolDiam / 2. #Calc tool diameter with Maximum Step Over allowed StepOverInUnitMax = toolDiam * CNC.vars['stepover'] / 100.0 #Check parameters if RotAxis == "": app.setStatus(_("Spiral abort: Rotary Axis is undefined")) return if SpiralType == "": app.setStatus(_("Spiral abort: Spiral Type is undefined")) return if ZApproach <= ZStart : app.setStatus(_("Spiral abort: Approach height must be greater than Z Start")) return if ReduceDepth > 0 : app.setStatus(_("Spiral abort: Depth Reduction must be negative")) return if Stepover > StepOverInUnitMax and SpiralType == "Spiral": #if Type is Lines then stepover is degrees not mm app.setStatus(_("Spiral abort: Step Over exceeds tool limits")) return elif Stepover > StepOverInUnitMax and SpiralType == "Lines": # This could cause a tool crash, but could also be used to make faceted shapes. dr=tkMessageBox.askyesno("Crash Risk","WARNING: Using a larger stepover value than tool's maximum with lines operation may result in a tool crash. Do you want to continue?") sys.stdout.write("%s"%(dr)) if dr == True or dr == "yes" : app.setStatus(_("Risk Accepted")) #Using positive logic, if python returns ANYTHING other than True/yes this will not make g-code. Incase Python uses No instead of False else: return if StockLeng <= 0 : app.setStatus(_("Spiral abort: Stock Length to cut must be positive")) return #Add Region disabled to show worked area block = Block(self.name + " Outline") block.enable = False block.append(CNC.grapid(CNC.vars["wx"],CNC.vars["wy"],ZApproach)) ## Cannot trust Safe-Z with 4th axis!! if AlignAxis == "X": outlineWidth = StockLeng else: outlineWidth = 0 if AlignAxis == "Y": outlineHeight = StockLeng else: outlineHeight = 0 xR,yR = self.RectPath(XStart,YStart,outlineWidth,outlineHeight) for x,y in zip(xR,yR): block.append(CNC.gline(x,y)) blocks.append(block) if StockLeng < toolDiam : app.setStatus(_("Spiral abort: Stock Length is too small for this End Mill.")) return #Prepare points for pocketing xP=[] yP=[] rP=[] zP=[] gP=[] #--------------------------------------------------------------------- #Line approach if SpiralType == "Lines": #Calc number of indexes IndexNum = math.ceil(360/Stepover) # Using the step over as Degrees #Calc number of pass VerticalCount = math.ceil(abs(ReduceDepth) / PassDepth) #Calc even depths of cut EvenCutDepths = ReduceDepth / VerticalCount currentR = 0 currentZ = ZStart - EvenCutDepths direction = 1 if AlignAxis == "X" : currentX = XStart + toolRadius currentY = YStart elif AlignAxis == "Y": currentX = XStart currentY = YStart + toolRadius else: app.setStatus(_("Spiral abort: Rotary Axis Not Assigned.")) return while (currentZ >= (ZStart + ReduceDepth)): #sys.stdout.write("~~~~~%s,%s,%s,%s,%s!"%(currentZ,ZStart,ReduceDepth,EvenCutDepths,VerticalCount)) while (currentR < 360): #sys.stdout.write("~~~~~%s,%s,%s,%s,%s!"%(currentR,Stepover,currentX,currentY,VerticalCount)) #Plunge in gP.append(1) rP.append(currentR) zP.append(currentZ) xP.append(currentX) yP.append(currentY) if direction == 1: if AlignAxis == "X" : currentX = StockLeng - toolRadius currentY = YStart elif AlignAxis == "Y": currentX = XStart currentY = StockLeng - toolRadius else: app.setStatus(_("Spiral abort: Rotary Axis Not Assigned.")) return if CutBoth == "True" : direction = -1 else : if AlignAxis == "X" : currentX = XStart + toolRadius currentY = YStart elif AlignAxis == "Y": currentX = XStart currentY = YStart + toolRadius else: app.setStatus(_("Spiral abort: Rotary Axis Not Assigned.")) return direction = 1 gP.append(1) zP.append(currentZ) xP.append(currentX) yP.append(currentY) rP.append(currentR) # Lift before rotating if required, useful to make non-round shape if CutBoth == "False" : # Return to start #Lift Before return gP.append(0) rP.append(currentR) zP.append(ZApproach) xP.append(currentX) yP.append(currentY) #Return to start if AlignAxis == "X" : currentX = XStart + toolRadius currentY = YStart elif AlignAxis == "Y": currentX = XStart currentY = YStart + toolRadius else: app.setStatus(_("Spiral abort: Rotary Axis Not Assigned.")) return gP.append(0) xP.append(currentX) yP.append(currentY) rP.append(currentR) zP.append(ZApproach) #Rotate currentR += Stepover gP.append(0) xP.append(currentX) yP.append(currentY) rP.append(currentR) zP.append(ZApproach) elif LiftPass == "True" and CutBoth == "True" : gP.append(0) rP.append(currentR) zP.append(ZApproach) xP.append(currentX) yP.append(currentY) currentR += Stepover gP.append(0) xP.append(currentX) yP.append(currentY) rP.append(currentR) zP.append(ZApproach) elif LiftPass == "False" and CutBoth == "True" : currentR += Stepover gP.append(0) xP.append(currentX) yP.append(currentY) rP.append(currentR) zP.append(ZApproach) currentR=0 gP.append(0) xP.append(currentX) yP.append(currentY) rP.append(currentR) zP.append(ZApproach) #Step Down currentZ += EvenCutDepths #--------------------------------------------------------------------- #Spiral approach if SpiralType == "Spiral": #Calc number of pass StepsPerRot = math.ceil(StockLeng/Stepover) TotalRot = 360 * StepsPerRot #Calc steps in depth VerticalCount = math.ceil(abs(ReduceDepth) / PassDepth) #Calc even depths of cut EvenCutDepths = ReduceDepth / VerticalCount direction = 1 currentZ = ZStart - EvenCutDepths if AlignAxis == "X" : currentX = XStart + toolRadius currentY = YStart elif AlignAxis == "Y": currentX = XStart currentY = YStart + toolRadius else: app.setStatus(_("Spiral abort: Rotary Axis Not Assigned.")) return currentR = 0 while (currentZ >= (ZStart + ReduceDepth)): # Plunge to depth currentR += 90 # Ramp the Plunge gP.append(1) rP.append(currentR) zP.append(currentZ) xP.append(currentX) yP.append(currentY) # One Full Rotation for a clean shoulder currentR += 360 gP.append(1) rP.append(currentR) zP.append(currentZ) xP.append(currentX) yP.append(currentY) if AlignAxis == "X" : if direction == 1: currentX = StockLeng - toolRadius else: currentX = XStart + toolRadius currentY = YStart elif AlignAxis == "Y": currentX = XStart if direction == 1: currentY = StockLeng - toolRadius else: currentY = YStart + toolRadius else: app.setStatus(_("Spiral abort: Rotary Axis Not Assigned.")) return currentR += TotalRot gP.append(1) rP.append(currentR) zP.append(currentZ) xP.append(currentX) yP.append(currentY) # One Full Rotation for a clean shoulder currentR += 360 gP.append(1) rP.append(currentR) zP.append(currentZ) xP.append(currentX) yP.append(currentY) if CutBoth == "True" : direction *= - 1 else: #Retract gP.append(0) rP.append(currentR) zP.append(ZApproach) xP.append(currentX) yP.append(currentY) # Return and Rewind gP.append(0) rP.append(currentR) zP.append(ZApproach) if AlignAxis == "X" : currentX = XStart + toolRadius currentY = YStart elif AlignAxis == "Y": currentX = XStart currentY = YStart + toolRadius else: app.setStatus(_("Spiral abort: Rotary Axis Not Assigned.")) return xP.append(currentX) yP.append(currentY) currentZ += EvenCutDepths #Start G-Code Processes #Blocks for pocketing block = Block(self.name) block.append("(Reduce Rotary by Y=%g)"%(ReduceDepth)) block.append("(Approach: %s )" % (SpiralType)) #Move safe to first point block.append(CNC.grapid(CNC.vars["mx"],CNC.vars["my"],ZApproach)) ## Cannot trust Safe-Z with 4th axis!! if AlignAxis == "X" : block.append(CNC.grapid(XStart + toolRadius,YStart)) elif AlignAxis == "Y": block.append(CNC.grapid(XStart,YStart + toolRadius)) else: app.setStatus(_("Spiral abort: Rotary Axis Not Assigned.")) return block.append(CNC.zenter(ZApproach)) block.append(CNC.gcode(1, [("f",CNC.vars["cutfeed"])])) for g,x,y,z,r in zip(gP, xP,yP,zP, rP): if RotAxis == "A" : if g==0: block.append(CNC.grapidABC(x,y,z,r,CNC.vars["wb"],CNC.vars["wc"])) #sys.stdout.write("%s,%s,%s,%s,%s"%(g,x,y,z,r)) else: block.append(CNC.glineABC(x,y,z,r,CNC.vars["wb"],CNC.vars["wc"])) #sys.stdout.write("%s,%s,%s,%s,%s"%(g,x,y,z,r)) elif RotAxis == "B" : if g==0: block.append(CNC.grapidABC(x,y,z,CNC.vars["wa"],r,CNC.vars["wc"])) else: block.append(CNC.glineABC(x,y,z,CNC.vars["wa"],r,CNC.vars["wc"])) elif RotAxis == "C" : if g==0: block.append(CNC.grapidABC(x,y,z,CNC.vars["wa"],CNC.vars["wb"],r)) else: block.append(CNC.glineABC(x,y,z,CNC.vars["wa"],CNC.vars["wb"],r)) block.append(CNC.grapid(CNC.vars["wx"],CNC.vars["wy"],ZApproach)) ## Cannot trust Safe-Z with 4th axis!! if AlignAxis == "X" : block.append(CNC.grapid(XStart + toolRadius,YStart)) elif AlignAxis == "Y": block.append(CNC.grapid(XStart,YStart + toolRadius)) else: app.setStatus(_("Spiral abort: Rotary Axis Not Assigned.")) return block.append(CNC.zexit(ZApproach)) blocks.append(block) tkMessageBox.showinfo("Crash Risk","WARNING: Check CAM file Header for Z move. If it exists, remove it to prevent tool crash.") return blocks
def execute(self, app): 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 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 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): n = self["name"] if not n or n == "default": n = "Pyrograph" #Import parameters toolSize = self.fromMm("ToolSize") if toolSize <= 0: app.setStatus(_("Pyrograph abort: Tool Size must be > 0")) return filename = self["File"] #Open gcode file if not (filename == ""): app.load(filename) inOut = self["In-Out"] xadd = self["xAdd"] yadd = self["yAdd"] if xadd == "": xadd = 1 if yadd == "": yadd = 1 #Create the external box if inOut == "Out": box = Block("Box") external_box = [] box.append( CNC.grapid(CNC.vars["xmin"] - xadd, CNC.vars["ymin"] - yadd)) box.append( CNC.gline(CNC.vars["xmin"] - xadd, CNC.vars["ymax"] + yadd)) box.append( CNC.gline(CNC.vars["xmax"] + xadd, CNC.vars["ymax"] + yadd)) box.append( CNC.gline(CNC.vars["xmax"] + xadd, CNC.vars["ymin"] - yadd)) box.append( CNC.gline(CNC.vars["xmin"] - xadd, CNC.vars["ymin"] - yadd)) #Insert the external block external_box.append(box) app.gcode.insBlocks(1, external_box, "External_Box") app.refresh() #Value for creating an offset from the margins of the gcode margin = self.fromMm( "Margin" ) #GIVING RANDOM DECIMALS SHOULD AVOID COINCIDENT SEGMENTS BETWEEN ISLAND AND BASE PATHS THAT CONFUSE THE ALGORITHM. WORKS IN MOST CASES. #Add and subtract the margin max_x = CNC.vars["xmax"] + margin min_x = CNC.vars["xmin"] - margin max_y = CNC.vars["ymax"] + margin min_y = CNC.vars["ymin"] - margin #Difference between offtset margins dx = max_x - min_x dy = max_y - min_y #Number of vertical divisions according to the toolsize divisions = dy / toolSize #Distance between horizontal lines step_y = toolSize n_steps_y = int(divisions) + 1 #Create the snake pattern according to the number of divisions pattern = Block(self.name) pattern_base = [] for n in range(n_steps_y): if n == 0: pattern.append(CNC.grapid(min_x, min_y)) #go to bottom left else: y0 = min_y + step_y * (n - 1) y1 = min_y + step_y * (n) if not (n % 2 == 0): pattern.append(CNC.glinev( 1, [max_x, y0])) #write odd lines from left to right pattern.append(CNC.grapid(max_x, y1)) else: pattern.append(CNC.glinev( 1, [min_x, y0])) #write even lines from right to left pattern.append(CNC.grapid(min_x, y1)) #Insert the pattern block pattern_base.append(pattern) app.gcode.insBlocks(1, pattern_base, "pattern") app.refresh() #Mark pattern as island for bid in pattern_base: app.gcode.island([1]) #Select all blocks app.editor.selectAll() paths_base = [] paths_isl = [] points = [] #Compare blocks to separate island from other blocks for bid in app.editor.getSelectedBlocks(): if app.gcode[bid].operationTest('island'): paths_isl.extend(app.gcode.toPath(bid)) else: paths_base.extend(app.gcode.toPath(bid)) #Make intersection between blocks while len(paths_base) > 0: base = paths_base.pop() for island in paths_isl: #points.extend(base.intersectPath(island)) points.extend(island.intersectPath(base)) ###SORTING POINTS### x = [] y = [] #Get (x,y) intersection points for i in range(len(points)): x.append(points[i][2][0]) y.append(points[i][2][1]) #Save (x,y) intersection points in a matrix matrix = [[0 for i in range(2)] for j in range(len(x))] for i in range(len(x)): matrix[i][0] = x[i] matrix[i][1] = y[i] # for i in range(len(x)): # print('puntos',points[i][0],points[i][1],matrix[i][0], matrix[i][1]) #print(matrix) #Sort points in increasing y coordinate matrix.sort(key=itemgetter(1, 0), reverse=False) # for i in range(len(x)): # print('puntos',points[i][0],points[i][1],matrix[i][0], matrix[i][1]) #print(matrix) index = 0 pair = 0 new_matrix = [] for i in range(len(x)): if i == 0: index = index + 1 elif i < len(x) - 1: if matrix[i][1] == matrix[i - 1][1]: index = index + 1 else: if pair % 2 == 0: logic = False else: logic = True submatrix = matrix[i - index:i] submatrix.sort(key=itemgetter(0, 1), reverse=logic) new_matrix.extend(submatrix) index = 1 pair = pair + 1 else: #print('entered') if pair % 2 == 0: logic = False else: logic = True if matrix[i][1] == matrix[i - 1][1]: submatrix = matrix[i - index:] submatrix.sort(key=itemgetter(0, 1), reverse=logic) new_matrix.extend(submatrix) else: index = 1 submatrix = matrix[-1] new_matrix.extend(submatrix) # for i in range(len(x)): # print('puntos',new_matrix[i][0], new_matrix[i][1]) # for i in range(len(x)): # print('puntos',new_matrix[i][0], new_matrix[i][1]) #print(x, y) ###SORTING POINTS END### #Generate the gcode from points obtained blocks = [] block = Block(self.name) for i in range(len(x)): if i == 0: block.append(CNC.grapid(new_matrix[0][0], new_matrix[0][1])) inside = 0 else: if inside == 0: block.append(CNC.gline(new_matrix[i][0], new_matrix[i][1])) inside = 1 else: block.append(CNC.grapid(new_matrix[i][0], new_matrix[i][1])) inside = 0 # for i in range(len(x)): # print('puntos',x[i], y[i]) blocks.append(block) app.gcode.delBlockUndo(1) app.gcode.insBlocks(1, blocks, "Line to Line Burning") app.editor.disable() for block in blocks: block.enable = 1 #app.editor.enable() #app.editor.unselectAll() app.refresh() app.setStatus(_("Generated Line to Line Burning"))
def make(self,app, XStart=0.0, YStart=0.0, FlatWidth=10., FlatHeight=10., \ FlatDepth=0, BorderPass=False, CutDirection="Climb", PocketType="Raster"): #GCode Blocks blocks = [] #Check parameters if CutDirection == "": app.setStatus(_("Flatten abort: Cut Direction is undefined")) return if PocketType == "": app.setStatus(_("Flatten abort: Pocket Type is undefined")) return if FlatWidth <= 0 or FlatHeight <= 0: app.setStatus( _("Flatten abort: Flatten Area dimensions must be > 0")) return if FlatDepth > 0: app.setStatus( _("Flatten abort: Hey this is only for subtractive machine! Check depth!" )) return #Add Region disabled to show worked area block = Block(self.name + " Outline") block.enable = False block.append(CNC.zsafe()) xR, yR = self.RectPath(XStart, YStart, FlatWidth, FlatHeight) for x, y in zip(xR, yR): block.append(CNC.gline(x, y)) blocks.append(block) # Load tool and material settings toolDiam = CNC.vars['diameter'] toolRadius = toolDiam / 2. #Calc tool diameter with Maximum Step Over allowed StepOverInUnitMax = toolDiam * CNC.vars['stepover'] / 100.0 #Offset for Border Cut BorderXStart = XStart + toolRadius BorderYStart = YStart + toolRadius BorderWidth = FlatWidth - toolDiam BorderHeight = FlatHeight - toolDiam BorderXEnd = XStart + FlatWidth - toolRadius BorderYEnd = YStart + FlatHeight - toolRadius PocketXStart = BorderXStart PocketYStart = BorderYStart PocketXEnd = BorderXEnd PocketYEnd = BorderYEnd #Calc space to work with/without border cut WToWork = FlatWidth - toolDiam HToWork = FlatHeight - toolDiam if (WToWork < toolRadius or HToWork < toolRadius): app.setStatus( _("Flatten abort: Flatten area is too small for this End Mill." )) return #Prepare points for pocketing xP = [] yP = [] #and border xB = [] yB = [] #--------------------------------------------------------------------- #Raster approach if PocketType == "Raster": #Correct sizes if border is used if (BorderPass): PocketXStart += StepOverInUnitMax PocketYStart += StepOverInUnitMax PocketXEnd -= StepOverInUnitMax PocketYEnd -= StepOverInUnitMax WToWork -= (StepOverInUnitMax) HToWork -= (StepOverInUnitMax) #Calc number of pass VerticalCount = (int)(HToWork / StepOverInUnitMax) #Calc step minor of Max step StepOverInUnit = HToWork / (VerticalCount + 1) flip = False ActualY = PocketYStart #Zig zag if StepOverInUnit == 0: StepOverInUnit = 0.001 #avoid infinite while loop while (True): #Zig xP.append(self.ZigZag(flip, PocketXStart, PocketXEnd)) yP.append(ActualY) flip = not flip #Zag xP.append(self.ZigZag(flip, PocketXStart, PocketXEnd)) yP.append(ActualY) if (ActualY >= PocketYEnd - StepOverInUnitMax + StepOverInUnit): break #Up ActualY += StepOverInUnit xP.append(self.ZigZag(flip, PocketXStart, PocketXEnd)) yP.append(ActualY) #Points for border cut depends on Zig/Zag end if (BorderPass): if flip: xB, yB = self.RectPath(BorderXStart, BorderYEnd, BorderWidth, -BorderHeight) else: xB, yB = self.RectPath(BorderXEnd, BorderYEnd, -BorderWidth, -BorderHeight) #Reverse in case of Climb if CutDirection == "Climb": xB = xB[::-1] yB = yB[::-1] #--------------------------------------------------------------------- #Offset approach if PocketType == "Offset": #Calc number of pass VerticalCount = (int)(HToWork / StepOverInUnitMax) HorrizontalCount = (int)(WToWork / StepOverInUnitMax) #Make them odd if VerticalCount % 2 == 0: VerticalCount += 1 if HorrizontalCount % 2 == 0: HorrizontalCount += 1 #Calc step minor of Max step StepOverInUnitH = HToWork / (VerticalCount) StepOverInUnitW = WToWork / (HorrizontalCount) #Start from border to center xS = PocketXStart yS = PocketYStart wS = WToWork hS = HToWork xC = 0 yC = 0 while (xC <= HorrizontalCount / 2 and yC <= VerticalCount / 2): #Pocket offset points xO, yO = self.RectPath(xS, yS, wS, hS) if CutDirection == "Conventional": xO = xO[::-1] yO = yO[::-1] xP = xP + xO yP = yP + yO xS += StepOverInUnitH yS += StepOverInUnitW hS -= 2.0 * StepOverInUnitH wS -= 2.0 * StepOverInUnitW xC += 1 yC += 1 #Reverse point to start from inside (less stress on the tool) xP = xP[::-1] yP = yP[::-1] #Blocks for pocketing block = Block(self.name) block.append("(Flatten from X=%g Y=%g)" % (XStart, YStart)) block.append("(W=%g x H=%g x D=%g)" % (FlatWidth, FlatHeight, FlatDepth)) block.append("(Approach: %s %s)" % (PocketType, CutDirection)) if BorderPass: block.append("(with border)") #Move safe to first point block.append(CNC.zsafe()) block.append(CNC.grapid(xP[0], yP[0])) #Init Depth currDepth = 0. stepz = CNC.vars['stepz'] if stepz == 0: stepz = 0.001 #avoid infinite while loop #Create GCode from points while True: currDepth -= stepz if currDepth < FlatDepth: currDepth = FlatDepth block.append(CNC.zenter(currDepth)) block.append(CNC.gcode(1, [("f", CNC.vars["cutfeed"])])) #Pocketing lastxy = None for x, y in zip(xP, yP): # block.append(CNC.gline(x,y)) if lastxy != CNC.gline(x, y) or None: block.append(CNC.gline(x, y)) lastxy = CNC.gline(x, y) #Border cut if request for x, y in zip(xB, yB): block.append(CNC.gline(x, y)) #Verify exit condition if currDepth <= FlatDepth: break #Move to the begin in a safe way block.append(CNC.zsafe()) block.append(CNC.grapid(xP[0], yP[0])) #Zsafe block.append(CNC.zsafe()) blocks.append(block) return blocks
def execute(self, app): if Image is None: app.setStatus( _("Halftone abort: This plugin requires PIL/Pillow to read image data" )) return n = self["name"] if not n or n == "default": n = "Halftone" # Calc desired size channel = self["Channel"] invert = self["Invert"] drawSize = self["DrawSize"] cellSize = self["CellSize"] dMax = self["DiameterMax"] dMin = self["DiameterMin"] angle = self["Angle"] drawBorder = self["DrawBorder"] depth = self["Depth"] conical = self["Conical"] # Check parameters if drawSize < 1: app.setStatus( _("Halftone abort: Size too small to draw anything!")) return if dMin > dMax: app.setStatus( _("Halftone abort: Minimum diameter must be minor then Maximum" )) return if dMax < 1: app.setStatus(_("Halftone abort: Maximum diameter too small")) return if cellSize < 1: app.setStatus(_("Halftone abort: Cell size too small")) return tool = app.tools["EndMill"] tool_shape = tool["shape"] if conical: if tool_shape == "V-cutting": try: v_angle = float(tool["angle"]) except: app.setStatus( _("Halftone abort: Angle in V-Cutting end mill is missing" )) return else: app.setStatus( _("Halftone abort: Conical path need V-Cutting end mill")) return # Open picture file fileName = self["File"] try: img = Image.open(fileName) except: app.setStatus(_("Halftone abort: Can't read image file")) return # Create a scaled image to work faster with big image and better with small ones squareNorm = True if channel == 'Blue(sqrt)': img = img.convert('RGB') img = img.split()[0] elif channel == 'Green(sqrt)': img = img.convert('RGB') img = img.split()[1] elif channel == 'Red(sqrt)': img = img.convert('RGB') img = img.split()[2] else: img = img.convert('L') # to calculate luminance squareNorm = False # flip image to ouput correct coordinates img = img.transpose(Image.FLIP_TOP_BOTTOM) # Calc divisions for halftone divisions = drawSize / cellSize # Get image size self.imgWidth, self.imgHeight = img.size if (self.imgWidth > self.imgHeight): scale = drawSize / float(self.imgWidth) sample = int(self.imgWidth / divisions) else: scale = drawSize / float(self.imgHeight) sample = int(self.imgHeight / divisions) self.ratio = scale # Halftone circles = self.halftone(img, sample, scale, angle, squareNorm, invert) # Init blocks blocks = [] # Border block if drawBorder: block = Block("%s-border" % (self.name)) block.append(CNC.zsafe()) block.append(CNC.grapid(0, 0)) block.append(CNC.zenter(depth)) block.append(CNC.gcode(1, [("f", CNC.vars["cutfeed"])])) block.append(CNC.gline(self.imgWidth * self.ratio, 0)) block.append( CNC.gline(self.imgWidth * self.ratio, self.imgHeight * self.ratio)) block.append(CNC.gline(0, self.imgHeight * self.ratio)) block.append(CNC.gline(0, 0)) blocks.append(block) # Draw block block = Block(self.name) # Change color if channel == 'Blue(sqrt)': block.color = "#0000ff" elif channel == 'Green(sqrt)': block.color = "#00ff00" elif channel == 'Red(sqrt)': block.color = "#ff0000" block.append("(Halftone size W=%d x H=%d x D=%d ,Total points:%i)" % (self.imgWidth * self.ratio, self.imgHeight * self.ratio, depth, len(circles))) block.append("(Channel = %s)" % channel) for c in circles: x, y, r = c r = min(dMax / 2.0, r) if (r >= dMin / 2.): block.append(CNC.zsafe()) block.append(CNC.grapid(x + r, y)) block.append(CNC.zenter(depth)) block.append(CNC.garc( CW, x + r, y, i=-r, )) block.append(CNC.zsafe()) if conical: block.enable = False blocks.append(block) if conical: blockCon = Block("%s-Conical" % (self.name)) for c in circles: x, y, r = c blockCon.append(CNC.zsafe()) blockCon.append(CNC.grapid(x, y)) dv = r / math.tan(math.radians(v_angle / 2.)) blockCon.append(CNC.zenter(-dv)) blockCon.append(CNC.zsafe()) blocks.append(blockCon) # Gcode Zsafe active = app.activeBlock() app.gcode.insBlocks(active, blocks, "Halftone") app.refresh() app.setStatus( _("Generated Halftone size W=%d x H=%d x D=%d ,Total points:%i" % (self.imgWidth * self.ratio, self.imgHeight * self.ratio, depth, len(circles))))
def execute(self, app): name = self['name'] if not name or name == 'default': name = 'Function' # Initialize blocks that will contain our gCode blocks = [] block = Block(name) #Variable definitions formula = self['form'] res = self['res'] # X resolution ran = [self['ranX'], self['ranY']] # Range of X,Y, from -10, to 10 range is 20 cent = [self['centX'], self['centY']] # Coordinates X,Y of the center from bottom left of the coordinate system dim = [self['dimX'], self['dimY']] # Real dimensions in gcode units spacX = self['spacX'] # Spacing of X axis lines spacY = self['spacY'] # Spacing of Y axis lines lin = self['lin'] # Small value - length of a line in gcode units draw = self['draw'] # Draw the coordinate system block.append("(Generated with a script by kswiorek)\n") block.append("(Equation: " + formula +")\n") block.append("(Resolution: " + str(res) +")\n") block.append("(Range: " + str(ran) +")\n") block.append("(Center: " + str(cent) +")\n") block.append("(Dimensions: " + str(dim) +")\n") block.append("(SpacingXY: " + str(spacX) +", " + str(spacY) +")\n") def mapc(var, axis): #Map coordinate systems return (var * (dim[axis]/ran[axis])) #Define coordinate system mins and maxes minX = -cent[0] maxX = ran[0]-cent[0] minY = -cent[1] maxY = ran[1]-cent[1] #Define domain and codomain X = [] Y = [] e_old = "" #Store old exception to comapre #Calculate values for arguments with a resolution for i in range(0, int(ran[0]/res+1)): #Complaints about values beeing floats x = i*res + minX #Iterate x X.append(x) try: Y.append(eval(formula)) except Exception as exc: #Append None, not to loose sync with X Y.append(None) e = str(exc) if e != e_old: #If there is a different exception - display it print("Warning: " + str(e)) app.setStatus(_("Warning: " + str(e))) e_old = e raised = True # Z axis is raised at start #Clip values out of bounds, replace with None, not to loose sync with X for i, item in enumerate(Y): y = Y[i] if not y is None and (y < minY or y > maxY): Y[i] = None #Y without "None", min() and max() can't compare them Ynn = [] #Y no Nones for i, item in enumerate(Y): if not Y[i] is None: Ynn.append(Y[i]) block.append(CNC.gcode(1, [("f",CNC.vars["cutfeed"])])) #Set feedrate if draw: #If the user selected to draw the coordinate system #X axis block.append(CNC.grapid(z=3)) block.append(CNC.grapid(0, mapc(cent[1], 1))) #1st point of X axis line block.append(CNC.grapid(z=0)) block.append(CNC.gline(dim[0] + lin*1.2, mapc(cent[1], 1))) #End of X axis line + a bit more for the arrow block.append(CNC.gline(dim[0] - lin/2, mapc(cent[1], 1) - lin / 2)) #bottom part of the arrow block.append(CNC.grapid(z=3)) block.append(CNC.grapid(dim[0] + lin*1.2, mapc(cent[1], 1), 0)) #End of X axis line block.append(CNC.grapid(z=0)) block.append(CNC.gline(dim[0] - lin/2, mapc(cent[1], 1) + lin / 2)) #top part of the arrow block.append(CNC.grapid(z=3)) #Y axis, just inverted x with y block.append(CNC.grapid(z=3)) block.append(CNC.grapid(mapc(cent[0], 0), 0)) #1st point of Y axis line block.append(CNC.grapid(z=0)) block.append(CNC.gline(mapc(cent[0], 0), dim[1] + lin*1.2)) #End of Y axis line + a bit more for the arrow block.append(CNC.gline(mapc(cent[0], 0) - lin / 2, dim[1] - lin/2)) #left part of the arrow block.append(CNC.grapid(z=3)) block.append(CNC.grapid(mapc(cent[0], 0), dim[1] + lin*1.2)) #End of Y axis line block.append(CNC.grapid(z=0)) block.append(CNC.gline(mapc(cent[0], 0) + lin / 2, dim[1] - lin/2)) #right part of the arrow block.append(CNC.grapid(z=3)) #X axis number lines i = 0 while i < ran[0] - cent[0]: #While i is on the left of the arrow i +=spacX #Add line spacing #Draw lines right of the center block.append(CNC.grapid(mapc(i+cent[0],0), mapc(cent[1], 1) + lin/2)) block.append(CNC.grapid(z=0)) block.append(CNC.gline(mapc(i+cent[0],0), mapc(cent[1], 1) - lin/2)) block.append(CNC.grapid(z=3)) i = 0 while i > -cent[0]: #While i is lower than center coordinate, inverted for easier math i -=spacX #Add line spacing #Draw lines left of the center block.append(CNC.grapid(mapc(i+cent[0],0), mapc(cent[1], 1) + lin/2)) block.append(CNC.grapid(z=0)) block.append(CNC.gline(mapc(i+cent[0],0), mapc(cent[1], 1) - lin/2)) block.append(CNC.grapid(z=3)) #Y axis number lines i = 0 while i < ran[1] - cent[1]: #While i is between the center and the arrow i +=spacX #Add line spacing #Draw lines top of the center (everything just inverted) block.append(CNC.grapid(mapc(cent[0], 0) + lin/2, mapc(i+cent[1],1))) block.append(CNC.grapid(z=0)) block.append(CNC.gline(mapc(cent[0], 0) - lin/2, mapc(i+cent[1],1))) block.append(CNC.grapid(z=3)) i = 0 while i > -1*cent[1]: i -=spacX #Add line spacing #Draw lines bottom of the center block.append(CNC.grapid(mapc(cent[0], 0) + lin/2, mapc(i+cent[1],1))) block.append(CNC.grapid(z=0)) block.append(CNC.gline(mapc(cent[0], 0) - lin/2, mapc(i+cent[1],1))) block.append(CNC.grapid(z=3)) raised = True #Z was raised #Draw graph for i, item in enumerate(Y): if not Y[i] is None: x = mapc(X[i]+cent[0], 0) #Take an argument y = mapc(Y[i]+cent[1], 1) #Take a value else: y = Y[i] #only for tne None checks next if y is None and not raised: #If a None "period" just started raise Z raised = True block.append(CNC.grapid(z=3)) elif not y is None and raised: #If Z was raised and the None "period" ended move to new coordinates block.append(CNC.grapid(round(x, 2),round(y, 2))) block.append(CNC.grapid(z=0)) #Lower Z raised = False elif not y is None and not raised: #Nothing to do with Nones? Just draw block.append(CNC.gline(round(x, 2),round(y, 2))) block.append(CNC.grapid(z=3)) #Raise on the end blocks.append(block) active = app.activeBlock() app.gcode.insBlocks(active, blocks, 'Function inserted') #insert blocks over active block in the editor app.refresh() #refresh editor app.setStatus(_('Generated function graph')) #feed back result print()