diff --git a/src/Mod/Path/CMakeLists.txt b/src/Mod/Path/CMakeLists.txt index 1de3dac479..12d4cf4dbb 100644 --- a/src/Mod/Path/CMakeLists.txt +++ b/src/Mod/Path/CMakeLists.txt @@ -48,7 +48,7 @@ SET(PathScripts_SRCS PathScripts/PathFromShape.py PathScripts/DlgSettingsPath.ui PathScripts/PathKurveUtils.py - PathScripts/PathKurve.py + PathScripts/PathAreaUtils.py PathScripts/slic3r_pre.py PathScripts/PathFaceProfile.py PathScripts/PathFacePocket.py @@ -56,6 +56,13 @@ SET(PathScripts_SRCS PathScripts/PathCustom.py PathScripts/PathInspect.py PathScripts/PathSimpleCopy.py + PathScripts/PathEngrave.py + PathScripts/nc/nc.py + PathScripts/nc/iso.py + PathScripts/nc/__init__.py + PathScripts/nc/format.py + PathScripts/nc/iso_codes.py + ) ADD_CUSTOM_TARGET(PathScripts ALL diff --git a/src/Mod/Path/Gui/Resources/Path.qrc b/src/Mod/Path/Gui/Resources/Path.qrc index b9c8785405..9bc4acf407 100644 --- a/src/Mod/Path/Gui/Resources/Path.qrc +++ b/src/Mod/Path/Gui/Resources/Path.qrc @@ -30,7 +30,7 @@ icons/Path-Inspect.svg icons/Path-ToolChange.svg icons/Path-SimpleCopy.svg - icons/PathWorkbench.svg + icons/Path-Engrave.svg translations/Path_de.qm translations/Path_af.qm translations/Path_zh-CN.qm diff --git a/src/Mod/Path/Gui/Resources/icons/Path-Engrave.svg b/src/Mod/Path/Gui/Resources/icons/Path-Engrave.svg new file mode 100644 index 0000000000..ed70d569fd --- /dev/null +++ b/src/Mod/Path/Gui/Resources/icons/Path-Engrave.svg @@ -0,0 +1,564 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + diff --git a/src/Mod/Path/Gui/Resources/icons/Path-Holding.svg b/src/Mod/Path/Gui/Resources/icons/Path-Holding.svg new file mode 100644 index 0000000000..a5278caf5c --- /dev/null +++ b/src/Mod/Path/Gui/Resources/icons/Path-Holding.svg @@ -0,0 +1,553 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + diff --git a/src/Mod/Path/Gui/Resources/icons/Path-Tags.svg b/src/Mod/Path/Gui/Resources/icons/Path-Tags.svg new file mode 100644 index 0000000000..a5278caf5c --- /dev/null +++ b/src/Mod/Path/Gui/Resources/icons/Path-Tags.svg @@ -0,0 +1,553 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + diff --git a/src/Mod/Path/InitGui.py b/src/Mod/Path/InitGui.py index 5c3d9eddb0..d0b664874b 100644 --- a/src/Mod/Path/InitGui.py +++ b/src/Mod/Path/InitGui.py @@ -56,19 +56,18 @@ class PathWorkbench ( Workbench ): from PathScripts import PathStop from PathScripts import PathMachine from PathScripts import PathFromShape - from PathScripts import PathKurve from PathScripts import PathArray from PathScripts import PathFaceProfile from PathScripts import PathFacePocket from PathScripts import PathCustom from PathScripts import PathInspect from PathScripts import PathSimpleCopy + from PathScripts import PathEngrave # build commands list projcmdlist = ["Path_Project", "Path_ToolTableEdit","Path_Post","Path_Inspect"] - prepcmdlist = ["Path_Plane","Path_Fixture","Path_LoadTool","Path_ToolLenOffset","Path_Comment","Path_Stop", - "Path_FaceProfile","Path_FacePocket","Path_Custom","Path_FromShape"] - opcmdlist = ["Path_Profile","Path_Kurve","Path_Pocket","Path_Drilling"] + prepcmdlist = ["Path_Plane","Path_Fixture","Path_LoadTool","Path_ToolLenOffset","Path_Comment","Path_Stop","Path_FaceProfile","Path_FacePocket","Path_Custom","Path_FromShape"] + opcmdlist = ["Path_Profile","Path_Pocket","Path_Drilling","Path_Engrave"] modcmdlist = ["Path_Copy","Path_CompoundExtended","Path_Dressup","Path_Hop","Path_Array","Path_SimpleCopy"] diff --git a/src/Mod/Path/PathScripts/PathAreaUtils.py b/src/Mod/Path/PathScripts/PathAreaUtils.py new file mode 100644 index 0000000000..5845422f02 --- /dev/null +++ b/src/Mod/Path/PathScripts/PathAreaUtils.py @@ -0,0 +1,435 @@ +import area +from nc.nc import * +import PathScripts.nc.iso +import math +import PathKurveUtils + +# some globals, to save passing variables as parameters too much +area_for_feed_possible = None +tool_radius_for_pocket = None + +def cut_curve(curve, need_rapid, p, rapid_safety_space, current_start_depth, final_depth): + prev_p = p + first = True + + for vertex in curve.getVertices(): + if need_rapid and first: + # rapid across + rapid(vertex.p.x, vertex.p.y) + ##rapid down + rapid(z = current_start_depth + rapid_safety_space) + #feed down + feed(z = final_depth) + first = False + else: + if vertex.type == 1: + arc_ccw(vertex.p.x, vertex.p.y, i = vertex.c.x, j = vertex.c.y) + elif vertex.type == -1: + arc_cw(vertex.p.x, vertex.p.y, i = vertex.c.x, j = vertex.c.y) + else: + feed(vertex.p.x, vertex.p.y) + prev_p = vertex.p + return prev_p + +def area_distance(a, old_area): + best_dist = None + + for curve in a.getCurves(): + for vertex in curve.getVertices(): + c = old_area.NearestPoint(vertex.p) + d = c.dist(vertex.p) + if best_dist == None or d < best_dist: + best_dist = d + + for curve in old_area.getCurves(): + for vertex in curve.getVertices(): + c = a.NearestPoint(vertex.p) + d = c.dist(vertex.p) + if best_dist == None or d < best_dist: + best_dist = d + + return best_dist + +def make_obround(p0, p1, radius): + dir = p1 - p0 + d = dir.length() + dir.normalize() + right = area.Point(dir.y, -dir.x) + obround = area.Area() + c = area.Curve() + vt0 = p0 + right * radius + vt1 = p1 + right * radius + vt2 = p1 - right * radius + vt3 = p0 - right * radius + c.append(area.Vertex(0, vt0, area.Point(0, 0))) + c.append(area.Vertex(0, vt1, area.Point(0, 0))) + c.append(area.Vertex(1, vt2, p1)) + c.append(area.Vertex(0, vt3, area.Point(0, 0))) + c.append(area.Vertex(1, vt0, p0)) + obround.append(c) + return obround + +def feed_possible(p0, p1): + if p0 == p1: + return True + obround = make_obround(p0, p1, tool_radius_for_pocket) + a = area.Area(area_for_feed_possible) + obround.Subtract(a) + if obround.num_curves() > 0: + return False + return True + +def cut_curvelist1(curve_list, rapid_safety_space, current_start_depth, depth, clearance_height, keep_tool_down_if_poss): + p = area.Point(0, 0) + first = True + for curve in curve_list: + need_rapid = True + if first == False: + s = curve.FirstVertex().p + if keep_tool_down_if_poss == True: + # see if we can feed across + if feed_possible(p, s): + need_rapid = False + elif s.x == p.x and s.y == p.y: + need_rapid = False + if need_rapid: + rapid(z = clearance_height) + p = cut_curve(curve, need_rapid, p, rapid_safety_space, current_start_depth, depth) + first = False + + rapid(z = clearance_height) + +def cut_curvelist2(curve_list, rapid_safety_space, current_start_depth, depth, clearance_height, keep_tool_down_if_poss,start_point): + p = area.Point(0, 0) + start_x,start_y=start_point + first = True + for curve in curve_list: + need_rapid = True + if first == True: + direction = "on";radius = 0.0;offset_extra = 0.0; roll_radius = 0.0;roll_on = 0.0; roll_off = 0.0; rapid_safety_space; step_down = math.fabs(depth);extend_at_start = 0.0;extend_at_end = 0.0 + kurve_funcs.make_smaller( curve, start = area.Point(start_x,start_y)) + kurve_funcs.profile(curve, direction, radius , offset_extra, roll_radius, roll_on, roll_off, rapid_safety_space , clearance_height, current_start_depth, step_down , depth, extend_at_start, extend_at_end) + else: + s = curve.FirstVertex().p + if keep_tool_down_if_poss == True: + + # see if we can feed across + if feed_possible(p, s): + need_rapid = False + elif s.x == p.x and s.y == p.y: + need_rapid = False + + cut_curve(curve, need_rapid, p, rapid_safety_space, current_start_depth, depth) + first = False #change to True if you want to rapid back to start side before zigging again with unidirectional set + rapid(z = clearance_height) + +def recur(arealist, a1, stepover, from_center): + # this makes arealist by recursively offsetting a1 inwards + + if a1.num_curves() == 0: + return + + if from_center: + arealist.insert(0, a1) + else: + arealist.append(a1) + + a_offset = area.Area(a1) + a_offset.Offset(stepover) + + # split curves into new areas + if area.holes_linked(): + for curve in a_offset.getCurves(): + a2 = area.Area() + a2.append(curve) + recur(arealist, a2, stepover, from_center) + + else: + # split curves into new areas + a_offset.Reorder() + a2 = None + + for curve in a_offset.getCurves(): + if curve.IsClockwise(): + if a2 != None: + a2.append(curve) + else: + if a2 != None: + recur(arealist, a2, stepover, from_center) + a2 = area.Area() + a2.append(curve) + + if a2 != None: + recur(arealist, a2, stepover, from_center) + +def get_curve_list(arealist, reverse_curves = False): + curve_list = list() + for a in arealist: + for curve in a.getCurves(): + if reverse_curves == True: + curve.Reverse() + curve_list.append(curve) + return curve_list + +curve_list_for_zigs = [] +rightward_for_zigs = True +sin_angle_for_zigs = 0.0 +cos_angle_for_zigs = 1.0 +sin_minus_angle_for_zigs = 0.0 +cos_minus_angle_for_zigs = 1.0 +one_over_units = 1.0 + +def make_zig_curve(curve, y0, y, zig_unidirectional): + if rightward_for_zigs: + curve.Reverse() + + # find a high point to start looking from + high_point = None + for vertex in curve.getVertices(): + if high_point == None: + high_point = vertex.p + elif vertex.p.y > high_point.y: + # use this as the new high point + high_point = vertex.p + elif math.fabs(vertex.p.y - high_point.y) < 0.002 * one_over_units: + # equal high point + if rightward_for_zigs: + # use the furthest left point + if vertex.p.x < high_point.x: + high_point = vertex.p + else: + # use the furthest right point + if vertex.p.x > high_point.x: + high_point = vertex.p + + zig = area.Curve() + + high_point_found = False + zig_started = False + zag_found = False + + for i in range(0, 2): # process the curve twice because we don't know where it will start + prev_p = None + for vertex in curve.getVertices(): + if zag_found: break + if prev_p != None: + if zig_started: + zig.append(unrotated_vertex(vertex)) + if math.fabs(vertex.p.y - y) < 0.002 * one_over_units: + zag_found = True + break + elif high_point_found: + if math.fabs(vertex.p.y - y0) < 0.002 * one_over_units: + if zig_started: + zig.append(unrotated_vertex(vertex)) + elif math.fabs(prev_p.y - y0) < 0.002 * one_over_units and vertex.type == 0: + zig.append(area.Vertex(0, unrotated_point(prev_p), area.Point(0, 0))) + zig.append(unrotated_vertex(vertex)) + zig_started = True + elif vertex.p.x == high_point.x and vertex.p.y == high_point.y: + high_point_found = True + prev_p = vertex.p + + if zig_started: + + if zig_unidirectional == True: + # remove the last bit of zig + if math.fabs(zig.LastVertex().p.y - y) < 0.002 * one_over_units: + vertices = zig.getVertices() + while len(vertices) > 0: + v = vertices[len(vertices)-1] + if math.fabs(v.p.y - y0) < 0.002 * one_over_units: + break + else: + vertices.pop() + zig = area.Curve() + for v in vertices: + zig.append(v) + + curve_list_for_zigs.append(zig) + +def make_zig(a, y0, y, zig_unidirectional): + for curve in a.getCurves(): + make_zig_curve(curve, y0, y, zig_unidirectional) + +reorder_zig_list_list = [] + +def add_reorder_zig(curve): + global reorder_zig_list_list + + # look in existing lists + s = curve.FirstVertex().p + for curve_list in reorder_zig_list_list: + last_curve = curve_list[len(curve_list) - 1] + e = last_curve.LastVertex().p + if math.fabs(s.x - e.x) < 0.002 * one_over_units and math.fabs(s.y - e.y) < 0.002 * one_over_units: + curve_list.append(curve) + return + + # else add a new list + curve_list = [] + curve_list.append(curve) + reorder_zig_list_list.append(curve_list) + +def reorder_zigs(): + global curve_list_for_zigs + global reorder_zig_list_list + reorder_zig_list_list = [] + for curve in curve_list_for_zigs: + add_reorder_zig(curve) + + curve_list_for_zigs = [] + for curve_list in reorder_zig_list_list: + for curve in curve_list: + curve_list_for_zigs.append(curve) + +def rotated_point(p): + return area.Point(p.x * cos_angle_for_zigs - p.y * sin_angle_for_zigs, p.x * sin_angle_for_zigs + p.y * cos_angle_for_zigs) + +def unrotated_point(p): + return area.Point(p.x * cos_minus_angle_for_zigs - p.y * sin_minus_angle_for_zigs, p.x * sin_minus_angle_for_zigs + p.y * cos_minus_angle_for_zigs) + +def rotated_vertex(v): + if v.type: + return area.Vertex(v.type, rotated_point(v.p), rotated_point(v.c)) + return area.Vertex(v.type, rotated_point(v.p), area.Point(0, 0)) + +def unrotated_vertex(v): + if v.type: + return area.Vertex(v.type, unrotated_point(v.p), unrotated_point(v.c)) + return area.Vertex(v.type, unrotated_point(v.p), area.Point(0, 0)) + +def rotated_area(a): + an = area.Area() + for curve in a.getCurves(): + curve_new = area.Curve() + for v in curve.getVertices(): + curve_new.append(rotated_vertex(v)) + an.append(curve_new) + return an + +def zigzag(a, stepover, zig_unidirectional): + if a.num_curves() == 0: + return + + global rightward_for_zigs + global curve_list_for_zigs + global sin_angle_for_zigs + global cos_angle_for_zigs + global sin_minus_angle_for_zigs + global cos_minus_angle_for_zigs + global one_over_units + + one_over_units = 1 / area.get_units() + + a = rotated_area(a) + + b = area.Box() + a.GetBox(b) + + x0 = b.MinX() - 1.0 + x1 = b.MaxX() + 1.0 + + height = b.MaxY() - b.MinY() + num_steps = int(height / stepover + 1) + y = b.MinY() + 0.1 * one_over_units + null_point = area.Point(0, 0) + rightward_for_zigs = True + curve_list_for_zigs = [] + + for i in range(0, num_steps): + y0 = y + y = y + stepover + p0 = area.Point(x0, y0) + p1 = area.Point(x0, y) + p2 = area.Point(x1, y) + p3 = area.Point(x1, y0) + c = area.Curve() + c.append(area.Vertex(0, p0, null_point, 0)) + c.append(area.Vertex(0, p1, null_point, 0)) + c.append(area.Vertex(0, p2, null_point, 1)) + c.append(area.Vertex(0, p3, null_point, 0)) + c.append(area.Vertex(0, p0, null_point, 1)) + a2 = area.Area() + a2.append(c) + a2.Intersect(a) + make_zig(a2, y0, y, zig_unidirectional) + if zig_unidirectional == False: + rightward_for_zigs = (rightward_for_zigs == False) + + reorder_zigs() + +def pocket(a,tool_radius, extra_offset, stepover, depthparams, from_center, keep_tool_down_if_poss, use_zig_zag, zig_angle, zig_unidirectional = False,start_point=None, cut_mode = 'conventional'): + global tool_radius_for_pocket + global area_for_feed_possible + #if len(a.getCurves()) > 1: + # for crv in a.getCurves(): + # ar = area.Area() + # ar.append(crv) + # pocket(ar, tool_radius, extra_offset, rapid_safety_space, start_depth, final_depth, stepover, stepdown, clearance_height, from_center, keep_tool_down_if_poss, use_zig_zag, zig_angle, zig_unidirectional) + # return + + tool_radius_for_pocket = tool_radius + + if keep_tool_down_if_poss: + area_for_feed_possible = area.Area(a) + area_for_feed_possible.Offset(extra_offset - 0.01) + + use_internal_function = (area.holes_linked() == False) # use internal function, if area module is the Clipper library + + if use_internal_function: + print "using internal. PathAreaUtils:382 " + curve_list = a.MakePocketToolpath(tool_radius, extra_offset, stepover, from_center, use_zig_zag, zig_angle) + + else: + print "not using internal. PathAreaUtils:386 " + + global sin_angle_for_zigs + global cos_angle_for_zigs + global sin_minus_angle_for_zigs + global cos_minus_angle_for_zigs + radians_angle = zig_angle * math.pi / 180 + sin_angle_for_zigs = math.sin(-radians_angle) + cos_angle_for_zigs = math.cos(-radians_angle) + sin_minus_angle_for_zigs = math.sin(radians_angle) + cos_minus_angle_for_zigs = math.cos(radians_angle) + + arealist = list() + + a_offset = area.Area(a) + current_offset = tool_radius + extra_offset + a_offset.Offset(current_offset) + + do_recursive = True + + if use_zig_zag: + zigzag(a_offset, stepover, zig_unidirectional) + curve_list = curve_list_for_zigs + else: + if do_recursive: + recur(arealist, a_offset, stepover, from_center) + else: + while(a_offset.num_curves() > 0): + if from_center: + arealist.insert(0, a_offset) + else: + arealist.append(a_offset) + current_offset = current_offset + stepover + a_offset = area.Area(a) + a_offset.Offset(current_offset) + curve_list = get_curve_list(arealist, cut_mode == 'climb') + + depths = depthparams.get_depths() + current_start_depth = depthparams.start_depth + print "Startpoint: " + str(start_point) + if start_point==None: + for depth in depths: + cut_curvelist1(curve_list, depthparams.rapid_safety_space, current_start_depth, depth, depthparams.clearance_height, keep_tool_down_if_poss) + current_start_depth = depth + + else: + for depth in depths: + cut_curvelist2(curve_list, depthparams.rapid_safety_space, current_start_depth, depth, depthparams.clearance_height, keep_tool_down_if_poss, start_point) + current_start_depth = depth + + diff --git a/src/Mod/Path/PathScripts/PathDrilling.py b/src/Mod/Path/PathScripts/PathDrilling.py index 97ba59c999..d9a4072100 100644 --- a/src/Mod/Path/PathScripts/PathDrilling.py +++ b/src/Mod/Path/PathScripts/PathDrilling.py @@ -46,24 +46,18 @@ class ObjectDrilling: def __init__(self,obj): - #obj.addProperty("App::PropertyVector","StartPoint","Path","The start position of the drilling") - - obj.addProperty("App::PropertyLinkSub","Base","Path","The base geometry of this toolpath") + obj.addProperty("App::PropertyLinkSubList","Base","Path",translate("Parent Object(s)","The base geometry of this toolpath")) obj.addProperty("App::PropertyVectorList","locations","Path","The drilling locations") - obj.addProperty("App::PropertyLength", "PeckDepth", "Drilling","Incremental Drill depth before retracting to clear chips") - #obj.PeckDepth = (0,0,1000,1) - obj.addProperty("App::PropertyDistance", "ClearanceHeight", "Drilling", "The height needed to clear clamps and obstructions") - obj.addProperty("App::PropertyDistance", "FinalDepth", "Drilling","Final Depth of Tool- lowest value in Z") - obj.addProperty("App::PropertyDistance", "RetractHeight", "Drilling","The height where feed starts and height during retract tool when path is finished") - obj.addProperty("App::PropertyLength", "VertFeed", "Feed","Feed rate for vertical moves in Z") - - #obj.addProperty("App::PropertySpeed", "HorizFeed", "Feed","Feed rate for horizontal moves") #not needed for drilling - - obj.addProperty("App::PropertyString","Comment","Path","An optional comment for this profile") - obj.addProperty("App::PropertyBool","Active","Path","Make False, to prevent operation from generating code") - - obj.addProperty("App::PropertyIntegerConstraint","ToolNumber","Tool","The tool number in use") + obj.addProperty("App::PropertyLength", "PeckDepth", "Depth", translate("PeckDepth","Incremental Drill depth before retracting to clear chips")) + #obj.addProperty("App::PropertyFloat", "StartDepth", "Depth", translate("PathProject","Starting Depth of Tool- first cut depth in Z")) + obj.addProperty("App::PropertyDistance", "ClearanceHeight", "Depth", translate("Clearance Height","The height needed to clear clamps and obstructions")) + obj.addProperty("App::PropertyDistance", "FinalDepth", "Depth", translate("Final Depth","Final Depth of Tool- lowest value in Z")) + obj.addProperty("App::PropertyDistance", "RetractHeight", "Depth", translate("Retract Height","The height where feed starts and height during retract tool when path is finished")) + obj.addProperty("App::PropertyLength", "VertFeed", "Feed",translate("Vert Feed","Feed rate for vertical moves in Z")) + obj.addProperty("App::PropertyString","Comment","Path",translate("PathProject","An optional comment for this profile")) + obj.addProperty("App::PropertyBool","Active","Path",translate("Active","Make False, to prevent operation from generating code")) + obj.addProperty("App::PropertyIntegerConstraint","ToolNumber","Tool",translate("PathProfile","The tool number in use")) obj.ToolNumber = (0,0,1000,1) obj.setEditorMode('ToolNumber',1) #make this read only @@ -76,9 +70,38 @@ class ObjectDrilling: return None def execute(self,obj): + locations = [] + for loc in obj.Base: + + if "Face" in loc[1] or "Edge" in loc[1]: + s = getattr(loc[0].Shape,loc[1]) + else: + s = loc[0].Shape + + if s.ShapeType in ['Face', 'Wire', 'Edge' ]: + X = s.Edges[0].Curve.Center.x + Y = s.Edges[0].Curve.Center.y + Z = s.Edges[0].Curve.Center.z + elif s.ShapeType == 'Vertex': + X = s.Point.x + Y = s.Point.y + Z = s.Point.z + + locations.append(FreeCAD.Vector(X,Y,Z)) + + # tie the toolnumber to the PathLoadTool object ToolNumber + if len(obj.InList)>0: #check to see if obj is in the Project group yet + project = obj.InList[0] + tl = int(PathUtils.changeTool(obj,project)) + obj.ToolNumber= tl + + tool = PathUtils.getTool(obj,obj.ToolNumber) + output = "G90 G98\n" + # rapid to clearance height + output += "G0 Z" + str(obj.ClearanceHeight.Value) # rapid to first hole location, with spindle still retracted: - p0 = obj.locations[0] + p0 = locations[0] output += "G0 X"+str(p0.x) + " Y" + str(p0.y)+ "\n" # move tool to clearance plane output += "G0 Z" + str(obj.ClearanceHeight.Value) + "\n" @@ -88,13 +111,12 @@ class ObjectDrilling: else: cmd = "G81" qword = "" - - for p in obj.locations: + for p in locations: + output += cmd + " X" + str(p.x) + " Y" + str(p.y) + " Z" + str(obj.FinalDepth.Value) + qword + " R" + str(obj.RetractHeight.Value) + " F" + str(obj.VertFeed.Value) + "\n" output += "G80\n" - print output path = Path.Path(output) obj.Path = path @@ -104,8 +126,7 @@ class ObjectDrilling: tl = int(PathUtils.changeTool(obj,project)) obj.ToolNumber= tl - -class _ViewProviderDrill: +class ViewProviderDrill: def __init__(self,obj): #mandatory # obj.addProperty("App::PropertyFloat","SomePropertyName","PropertyGroup","Description of this property") obj.Proxy = self @@ -152,45 +173,118 @@ class CommandPathDrilling: def IsActive(self): return not FreeCAD.ActiveDocument is None - + + def Drillable(self, obj): + import Part + drillable = "" + if obj.ShapeType == 'Vertex': + drillable = 'Vertex' + elif obj.ShapeType == 'Edge': + if isinstance(obj.Curve, Part.Circle): + drillable = 'Circle' + elif obj.ShapeType == 'Face': + if isinstance(obj.Edges[0].Curve, Part.Circle): + drillable = 'Circle' + elif obj.ShapeType == 'Wire': + if isinstance(obj.Edges[0].Curve, Part.Circle): + drillable = 'Circle' + else: + drillable = None + return drillable + def Activated(self): import Path import Part from PathScripts import PathUtils,PathDrilling,PathProject prjexists = False - selection = FreeCADGui.Selection.getSelectionEx() + selection = FreeCADGui.Selection.getSelectionEx() if not selection: + FreeCAD.Console.PrintError(translate("PathDrilling","Please select points or cirlces for drilling.\n")) return + + diamcount = 0 #keep track of how many different hole sizes we're asked to deal with + lastradius = 0.0 + locations = [] + vertexcount = 0 #keep track of how many vertices + + for s in selection: + if s.HasSubObjects: + for i in s.SubObjects: + d = self.Drillable (i) + if d == 'Circle': + if i.Edges[0].Curve.Radius != lastradius: + diamcount += 1 + lastradius = i.Edges[0].Curve.Radius + elif d == 'Vertex': + vertexcount += 1 + else: + FreeCAD.Console.PrintError(translate("PathDrilling","No drillable locations were selected.\n")) + return + + #subs = [] + for n in s.SubElementNames: + # subs.append(n) + locations.append((s.ObjectName, s.Object, n)) + + else: + d = self.Drillable (s.Object.Shape) + if d == 'Circle': + if not str(s.Object.Shape.Edges[0].Curve.Radius) == str(lastradius): + diamcount += 1 + lastradius = s.Object.Shape.Edges[0].Curve.Radius + + elif d == 'Vertex': + vertexcount += 1 + else: + FreeCAD.Console.PrintError(translate("PathDrilling","No drillable locations were selected.\n")) + return + + locations.append((s.ObjectName, s.Object, [])) + + if diamcount > 1: + FreeCAD.Console.PrintError(translate("PathDrilling","Circles of different radii found. Select only one size circle at a time.\n")) + return + + if diamcount >= 1 and vertexcount >= 1: + FreeCAD.Console.PrintError(translate("PathDrilling","Please select either points or circles but not both.\n")) + return + + # Take a guess at some reasonable values for Finish depth. + if selection[0].HasSubObjects: + bb = selection[0].Object.Shape.BoundBox #parent boundbox + fbb = selection[0].SubObjects[0].BoundBox #feature boundbox + if fbb.ZMax < bb.ZMax: + zbottom = fbb.ZMax + ztop = bb.ZMax + 1 + else: + zbottom = bb.ZMin + ztop = 5 + else: + zbottom = 0 + ztop = 5 + + # if everything is ok, execute and register the transaction in the undo/redo stack FreeCAD.ActiveDocument.openTransaction(translate("Path_Drilling","Create Drilling")) FreeCADGui.addModule("PathScripts.PathDrilling") - - obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython","Drilling") - PathDrilling.ObjectDrilling(obj) + FreeCADGui.doCommand('obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython","Drilling")') + FreeCADGui.doCommand('PathScripts.PathDrilling.ObjectDrilling(obj)') + FreeCADGui.doCommand('obj.Active = True') + FreeCADGui.doCommand('PathScripts.PathDrilling.ViewProviderDrill(obj.ViewObject)') - myList = obj.locations - for sub in selection: - for point in sub.SubObjects: - if isinstance(point,Part.Vertex): - myList.append(FreeCAD.Vector(point.X, point.Y, point.Z)) - if isinstance(point,Part.Edge): - if isinstance(point.Curve,Part.Circle): - center = point.Curve.Center - myList.append(FreeCAD.Vector(center.x,center.y,center.z)) - - obj.locations = myList + baselist = "[" + for loc in locations: + baselist += "(FreeCAD.ActiveDocument." + str(loc[0]) + ',"' + str(loc[2]) + '"),' + baselist += "]" - PathDrilling._ViewProviderDrill(obj.ViewObject) -# obj.ViewObject.Proxy = 0 - obj.Active = True - - project = PathUtils.addToProject(obj) - - tl = PathUtils.changeTool(obj,project) - if tl: - obj.ToolNumber = tl + FreeCADGui.doCommand('obj.Base = (' + baselist + ')') + FreeCADGui.doCommand('from PathScripts import PathUtils') + FreeCADGui.doCommand('obj.ClearanceHeight = ' + str(ztop)) + FreeCADGui.doCommand('obj.RetractHeight= ' + str(ztop)) + FreeCADGui.doCommand('obj.FinalDepth=' + str(zbottom)) + FreeCADGui.doCommand('PathScripts.PathUtils.addToProject(obj)') FreeCAD.ActiveDocument.commitTransaction() FreeCAD.ActiveDocument.recompute() diff --git a/src/Mod/Path/PathScripts/PathEngrave.py b/src/Mod/Path/PathScripts/PathEngrave.py new file mode 100644 index 0000000000..a39e87e6f3 --- /dev/null +++ b/src/Mod/Path/PathScripts/PathEngrave.py @@ -0,0 +1,175 @@ +# -*- coding: utf-8 -*- + +#*************************************************************************** +#* * +#* Copyright (c) 2014 Yorik van Havre * +#* * +#* This program is free software; you can redistribute it and/or modify * +#* it under the terms of the GNU Lesser General Public License (LGPL) * +#* as published by the Free Software Foundation; either version 2 of * +#* the License, or (at your option) any later version. * +#* for detail see the LICENCE text file. * +#* * +#* This program is distributed in the hope that it will be useful, * +#* but WITHOUT ANY WARRANTY; without even the implied warranty of * +#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +#* GNU Library General Public License for more details. * +#* * +#* You should have received a copy of the GNU Library General Public * +#* License along with this program; if not, write to the Free Software * +#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +#* USA * +#* * +#*************************************************************************** + +import FreeCAD,FreeCADGui,Path,PathGui +from PySide import QtCore,QtGui + +"""Path Engrave object and FreeCAD command""" + +# Qt tanslation handling +try: + _encoding = QtGui.QApplication.UnicodeUTF8 + def translate(context, text, disambig=None): + return QtGui.QApplication.translate(context, text, disambig, _encoding) +except AttributeError: + def translate(context, text, disambig=None): + return QtGui.QApplication.translate(context, text, disambig) + + +class ObjectPathEngrave: + + + def __init__(self,obj): + obj.addProperty("App::PropertyLinkSub","Base","Path","The base geometry of this object") + + obj.addProperty("App::PropertyEnumeration", "Algorithm", "Algorithm",translate("Path", "The library or Algorithm used to generate the path")) + obj.Algorithm = ['OCC Native'] + + #Depth Properties + obj.addProperty("App::PropertyDistance", "ClearanceHeight", "Depth", translate("Path","The height needed to clear clamps and obstructions")) + obj.addProperty("App::PropertyDistance", "SafeHeight", "Depth", translate("Path","Rapid Safety Height between locations.")) + obj.addProperty("App::PropertyDistance", "StartDepth", "Depth", translate("Path","Starting Depth of Tool- first cut depth in Z")) + obj.addProperty("App::PropertyDistance", "FinalDepth", "Depth", translate("Path","Final Depth of Tool- lowest value in Z")) + obj.addProperty("App::PropertyInteger","StartVertex","Path","The vertex index to start the path from") + #Feed Properties + obj.addProperty("App::PropertySpeed", "VertFeed", "Feed",translate("Path","Feed rate for vertical moves in Z")) + obj.addProperty("App::PropertySpeed", "HorizFeed", "Feed",translate("Path","Feed rate for horizontal moves")) + + + obj.Proxy = self + + def __getstate__(self): + return None + + def __setstate__(self,state): + return None + + def execute(self,obj): + if obj.Base: + # we only consider the outer wire if this is a Face + wires = obj.Base[0].Shape.Wires + + output = "" + if obj.Algorithm == "OCC Native": + output += self.buildpathocc(obj, wires) + + #print output + path = Path.Path(output) + obj.Path = path + + def buildpathocc(self, obj, wires): + import Part,DraftGeomUtils + output = "G90\nG21\nG40\n" + output += "G0 Z" + str(obj.ClearanceHeight.Value) + + # absolute coords, millimeters, cancel offsets + + for wire in wires: + offset = wire + + # reorder the wire + offset = DraftGeomUtils.rebaseWire(offset,obj.StartVertex) + + # we create the path from the offset shape + last = None + for edge in offset.Edges: + if not last: + # we set the first move to our first point + last = edge.Vertexes[0].Point + output += "G0" + " X" + str("%f" % last.x) + " Y" + str("%f" % last.y) #Rapid sto starting position + output += "G1" + " Z" + str("%f" % last.z) +"F " + str(obj.VertFeed.Value)+ "\n" #Vertical feed to depth + if isinstance(edge.Curve,Part.Circle): + point = edge.Vertexes[-1].Point + if point == last: # edges can come flipped + point = edge.Vertexes[0].Point + center = edge.Curve.Center + relcenter = center.sub(last) + v1 = last.sub(center) + v2 = point.sub(center) + if v1.cross(v2).z < 0: + output += "G2" + else: + output += "G3" + output += " X" + str("%f" % point.x) + " Y" + str("%f" % point.y) + " Z" + str("%f" % point.z) + output += " I" + str("%f" % relcenter.x) + " J" + str("%f" % relcenter.y) + " K" + str("%f" % relcenter.z) + output += " F " + str(obj.HorizFeed.Value) + output += "\n" + last = point + else: + point = edge.Vertexes[-1].Point + if point == last: # edges can come flipped + point = edge.Vertexes[0].Point + output += "G1 X" + str("%f" % point.x) + " Y" + str("%f" % point.y) + " Z" + str("%f" % point.z) + output += " F " + str(obj.HorizFeed.Value) + output += "\n" + last = point + output += "G0 Z " + str(obj.SafeHeight.Value) + return output + +class CommandPathEngrave: + + + def GetResources(self): + return {'Pixmap' : 'Path-Engrave', + 'MenuText': QtCore.QT_TRANSLATE_NOOP("Path_Engrave","ShapeString Engrave"), + 'ToolTip': QtCore.QT_TRANSLATE_NOOP("Path_Engrave","Creates an Engraving Path around a Draft ShapeString")} + + def IsActive(self): + return not FreeCAD.ActiveDocument is None + + def Activated(self): + import Draft + # check that the selection contains exactly what we want + selection = FreeCADGui.Selection.getSelectionEx() + if len(selection) != 1: + FreeCAD.Console.PrintError(translate("Path_Engrave","Please select one ShapeString\n")) + return + if len(selection[0].SubObjects) != 0: + FreeCAD.Console.PrintError(translate("Path_Engrave","Please select one ShapeString\n")) + return + if not Draft.getType(selection[0].Object) == "ShapeString": + FreeCAD.Console.PrintError(translate("Path_Engrave","Please select one ShapeString\n")) + return + + # if everything is ok, execute and register the transaction in the undo/redo stack + FreeCAD.ActiveDocument.openTransaction("Create Engrave Path") + FreeCADGui.addModule("PathScripts.PathFaceProfile") + FreeCADGui.addModule("PathScripts.PathUtils") + FreeCADGui.doCommand('obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython","PathEngrave")') + FreeCADGui.doCommand('PathScripts.PathEngrave.ObjectPathEngrave(obj)') + FreeCADGui.doCommand('obj.Base = (FreeCAD.ActiveDocument.'+selection[0].ObjectName+',[])') + FreeCADGui.doCommand('obj.ClearanceHeight = 10') + FreeCADGui.doCommand('obj.StartDepth= 0') + FreeCADGui.doCommand('obj.FinalDepth= -0.1' ) + FreeCADGui.doCommand('obj.SafeHeight= 5.0' ) + + FreeCADGui.doCommand('obj.ViewObject.Proxy = 0') + FreeCADGui.doCommand('PathScripts.PathUtils.addToProject(obj)') + FreeCAD.ActiveDocument.commitTransaction() + FreeCAD.ActiveDocument.recompute() + + +if FreeCAD.GuiUp: + # register the FreeCAD command + FreeCADGui.addCommand('Path_Engrave',CommandPathEngrave()) diff --git a/src/Mod/Path/PathScripts/PathHop.py b/src/Mod/Path/PathScripts/PathHop.py index f22e9a9076..e8848bc36c 100644 --- a/src/Mod/Path/PathScripts/PathHop.py +++ b/src/Mod/Path/PathScripts/PathHop.py @@ -119,7 +119,7 @@ class CommandPathHop: FreeCAD.Console.PrintError(translate("Path_Hop","The selected object is not a path\n")) return - FreeCAD.ActiveDocument.openTransaction(translate("Path_Hop","Create Hop")) + FreeCAD.ActiveDocument.openTransaction(translate("Pat_hHop","Create Hop")) FreeCADGui.addModule("PathScripts.PathHop") FreeCADGui.addModule("PathScripts.PathUtils") FreeCADGui.doCommand('obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython","Hop")') diff --git a/src/Mod/Path/PathScripts/PathKurve.py b/src/Mod/Path/PathScripts/PathKurve.py deleted file mode 100644 index 3ef371e4c8..0000000000 --- a/src/Mod/Path/PathScripts/PathKurve.py +++ /dev/null @@ -1,290 +0,0 @@ -# -*- coding: utf-8 -*- - -#*************************************************************************** -#* * -#* Copyright (c) 2015 Dan Falck * -#* * -#* This program is free software; you can redistribute it and/or modify * -#* it under the terms of the GNU Lesser General Public License (LGPL) * -#* as published by the Free Software Foundation; either version 2 of * -#* the License, or (at your option) any later version. * -#* for detail see the LICENCE text file. * -#* * -#* This program is distributed in the hope that it will be useful, * -#* but WITHOUT ANY WARRANTY; without even the implied warranty of * -#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * -#* GNU Library General Public License for more details. * -#* * -#* You should have received a copy of the GNU Library General Public * -#* License along with this program; if not, write to the Free Software * -#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * -#* USA * -#* * -#*************************************************************************** -'''PathKurve - Path Profile operation using libarea (created by Dan Heeks) for making simple CNC paths. -libarea, originally from HeeksCNC project must be present for this to work.''' - - -import FreeCAD,FreeCADGui,Path,PathGui -from PathScripts import PathProject,PathUtils,PathKurveUtils,PathSelection -from PySide import QtCore,QtGui - -# Qt tanslation handling -try: - _encoding = QtGui.QApplication.UnicodeUTF8 - def translate(context, text, disambig=None): - return QtGui.QApplication.translate(context, text, disambig, _encoding) -except AttributeError: - def translate(context, text, disambig=None): - return QtGui.QApplication.translate(context, text, disambig) - -class PathProfile: - def __init__(self,obj): - obj.addProperty("App::PropertyLinkSub","Base","Path","The base geometry of this toolpath") - obj.addProperty("App::PropertyLinkSub","StartPoint", "Path","Linked Start Point of Profile") - obj.addProperty("App::PropertyLinkSub","EndPoint", "Path","Linked End Point of Profile") - obj.addProperty("App::PropertyBool","Active","Path","Make False, to prevent operation from generating code") - obj.addProperty("App::PropertyString","Comment","Path","An optional comment for this profile") - - obj.addProperty("App::PropertyIntegerConstraint","ToolNumber","Tool","The tool number in use") - obj.ToolNumber = (0,0,1000,1) - obj.setEditorMode('ToolNumber',1) #make this read only - #Depth Properties - obj.addProperty("App::PropertyDistance", "ClearanceHeight", "Depth","The height needed to clear clamps and obstructions") - obj.addProperty("App::PropertyLength", "StepDown", "Depth","Incremental Step Down of Tool") -# obj.addProperty("App::PropertyBool","UseStartDepth","Depth","make True, if manually specifying a Start Start Depth") - obj.addProperty("App::PropertyDistance", "StartDepth", "Depth","Starting Depth of Tool- first cut depth in Z") - obj.addProperty("App::PropertyDistance", "FinalDepth", "Depth","Final Depth of Tool- lowest value in Z") - obj.addProperty("App::PropertyDistance", "RetractHeight", "Depth","The height desired to retract tool when path is finished") - - #Feed Properties - obj.addProperty("App::PropertyLength", "VertFeed", "Feed","Feed rate (in units per minute) for vertical moves in Z") - obj.addProperty("App::PropertyLength", "HorizFeed", "Feed","Feed rate (in units per minute) for horizontal moves") - - #Profile Properties - obj.addProperty("App::PropertyEnumeration", "Side", "Profile","Side of edge that tool should cut") - obj.Side = ['left','right','on'] #side of profile that cutter is on in relation to direction of profile - obj.addProperty("App::PropertyEnumeration", "Direction", "Profile","The direction that the toolpath should go around the part ClockWise CW or CounterClockWise CCW") - obj.Direction = ['CW','CCW'] #this is the direction that the profile runs - obj.addProperty("App::PropertyBool","UseComp","Profile","make True, if using Cutter Radius Compensation") - obj.addProperty("App::PropertyIntegerList","Edgelist","Profile","List of edges selected") - obj.addProperty("App::PropertyDistance", "OffsetExtra", "Profile","Extra value to stay away from final profile- good for roughing toolpath") -# obj.addProperty("App::PropertyLength", "SegLen", "Profile","Tesselation value for tool paths made from beziers, bsplines, and ellipses") - -# #Start Point Properties - obj.addProperty("App::PropertyString","StartPtName","Profile","The name of the start point of this path") - obj.addProperty("App::PropertyBool","UseStartPt","Profile","Make True, if specifying a Start Point") -# obj.addProperty("App::PropertyLength", "ExtendAtStart", "Profile", "extra length of tool path before start of part edge") -# obj.addProperty("App::PropertyLength", "LeadInLineLen", "Profile","length of straight segment of toolpath that comes in at angle to first part edge") - -# #End Point Properties - obj.addProperty("App::PropertyString","EndPtName","Profile","The name of the end point of this path") - obj.addProperty("App::PropertyBool","UseEndPt","Profile","Make True, if specifying an End Point") -# obj.addProperty("App::PropertyLength", "ExtendAtEnd", "Profile","extra length of tool path after end of part edge") -# obj.addProperty("App::PropertyLength", "LeadOutLineLen", "Profile","length of straight segment of toolpath that comes in at angle to last edge selected") - -# obj.addProperty("App::PropertyDistance", "RollRadius", "Profile", "Radius at start and end") - - obj.Proxy = self - - - - - def __getstate__(self): - return None - - def __setstate__(self,state): - return None - - def execute(self,obj): - - if obj.Base: - - # tie the toolnumber to the PathLoadTool object ToolNumber - if len(obj.InList)>0: #check to see if obj is in the Project group yet - project = obj.InList[0] - tl = int(PathUtils.changeTool(obj,project)) - obj.ToolNumber= tl - - tool = PathUtils.getTool(obj,obj.ToolNumber) - if tool: - self.radius = tool.Diameter/2 - else: - # temporary value,in case we don't have any tools defined already - self.radius = 0.25 -# self.radius = 0.25 - self.clearance = obj.ClearanceHeight.Value - self.step_down=obj.StepDown.Value - self.start_depth=obj.StartDepth.Value - self.final_depth=obj.FinalDepth.Value - self.rapid_safety_space=obj.RetractHeight.Value - self.side=obj.Side - self.offset_extra=obj.OffsetExtra.Value - self.use_CRC=obj.UseComp - self.vf=obj.VertFeed.Value - self.hf=obj.HorizFeed.Value - - - edgelist = [] - - if obj.StartPtName and obj.UseStartPt: - self.startpt = FreeCAD.ActiveDocument.getObject(obj.StartPtName).Shape - else: - self.startpt = None - - if obj.EndPtName and obj.UseEndPt: - self.endpt = FreeCAD.ActiveDocument.getObject(obj.EndPtName).Shape - else: - self.endpt = None - - for e in obj.Edgelist: - edgelist.append(FreeCAD.ActiveDocument.getObject(obj.Base[0].Name).Shape.Edges[e-1]) - - output=PathKurveUtils.makePath(edgelist,self.side,self.radius,self.vf,self.hf,self.offset_extra, \ - self.rapid_safety_space,self.clearance,self.start_depth,self.step_down, \ - self.final_depth,self.use_CRC,obj.Direction,self.startpt,self.endpt) - - if obj.Active: - path = Path.Path(output) - obj.Path = path - obj.ViewObject.Visibility = True - - else: - path = Path.Path("(inactive operation)") - obj.Path = path - obj.ViewObject.Visibility = False - - - -class _ViewProviderKurve: - - def __init__(self,vobj): #mandatory - vobj.Proxy = self - def __getstate__(self): #mandatory - return None - - def __setstate__(self,state): #mandatory - return None - - def getIcon(self): #optional - return ":/icons/Path-Kurve.svg" - - -class CommandPathKurve: - def GetResources(self): - return {'Pixmap' : 'Path-Kurve', - 'MenuText': QtCore.QT_TRANSLATE_NOOP("Path_Kurve","Profile"), - 'Accel': "P, P", - 'ToolTip': QtCore.QT_TRANSLATE_NOOP("Path_Kurve","Creates a Path Profile object from selected edges, using libarea for offset algorithm")} - - def IsActive(self): - return not FreeCAD.ActiveDocument is None - - def Activated(self): - FreeCAD.ActiveDocument.openTransaction(translate("Path_Kurve","Create a Profile operation using libarea")) - FreeCADGui.addModule("PathScripts.PathKurve") - snippet = ''' -import Path -from PathScripts import PathSelection,PathProject,PathUtils -import area - -def profileop(): - selection = PathSelection.multiSelect() - - if not selection: - FreeCAD.Console.PrintError('please select some edges\\n') - - obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython","Profile") - PathScripts.PathKurve.PathProfile(obj) - - obj.Active = True - PathScripts.PathKurve._ViewProviderKurve(obj.ViewObject) - - obj.Base = (FreeCAD.ActiveDocument.getObject(selection['objname'])) - - elist = [] - for e in selection['edgenames']: - elist.append(eval(e.lstrip('Edge'))) - - obj.Edgelist = elist - if selection['pointnames']: - FreeCAD.Console.PrintMessage('There are points selected.\\n') - if len(selection['pointnames'])>1: - obj.StartPtName = selection['pointnames'][0] - obj.StartPoint= FreeCAD.ActiveDocument.getObject(obj.StartPtName) - - obj.EndPtName = selection['pointnames'][-1] - obj.EndPoint=FreeCAD.ActiveDocument.getObject(obj.EndPtName) - - else: - obj.StartPtName = selection['pointnames'][0] - obj.StartPoint= FreeCAD.ActiveDocument.getObject(obj.StartPtName) - - obj.ClearanceHeight = 2.0 - obj.StepDown = 1.0 - obj.StartDepth=0.0 - obj.FinalDepth=-1.0 - obj.RetractHeight = 5.0 - obj.Side = 'left' - obj.OffsetExtra = 0.0 - if selection['clockwise']: - obj.Direction = 'CW' - else: - obj.Direction = 'CCW' - obj.UseComp = False - - project = PathUtils.addToProject(obj) - - tl = PathUtils.changeTool(obj,project) - - if tl: - obj.ToolNumber = tl - - -from PathScripts import PathProject,PathUtils,PathKurve, PathKurveUtils,PathSelection -try: - import area - -except: - FreeCAD.Console.PrintError('libarea needs to be installed for this command to work\\n') -profileop() -''' - - FreeCADGui.doCommand(snippet) - FreeCAD.ActiveDocument.commitTransaction() - FreeCAD.ActiveDocument.recompute() - -if FreeCAD.GuiUp: - # register the FreeCAD command - FreeCADGui.addCommand('Path_Kurve',CommandPathKurve()) - -FreeCAD.Console.PrintLog("Loading PathKurve... done\n") - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/Mod/Path/PathScripts/PathKurveUtils.py b/src/Mod/Path/PathScripts/PathKurveUtils.py index 9acc77ebda..455da6dd5c 100644 --- a/src/Mod/Path/PathScripts/PathKurveUtils.py +++ b/src/Mod/Path/PathScripts/PathKurveUtils.py @@ -32,7 +32,15 @@ import math import area import Path from PathScripts import PathUtils -import PathSelection +# import PathSelection +from nc.nc import * +import PathScripts.nc.iso + + + + + + def makeAreaVertex(seg): if seg.ShapeType =='Edge': @@ -52,31 +60,32 @@ def makeAreaCurve(edges,direction,startpt=None,endpt=None): curveobj = area.Curve() cleanededges = PathUtils.cleanedges(edges, 0.01) - - #sort the edges - vlist,edgestart,common = PathSelection.Sort2Edges([cleanededges[0],cleanededges[1]]) - - if cleanededges[0].valueAt(cleanededges[0].FirstParameter)<>edgestart: - firstedge=PathUtils.reverseEdge(cleanededges[0]) - else: - firstedge=cleanededges[0] - edgelist=[] - edgelist.append(firstedge) + + if len(cleanededges) == 1: #user selected a single edge. + edgelist = cleanededges + else: + edgelist = [] #Multiple edges. Need to sequence the vetexes. + #First get the first segment oriented correctly. - #get start and end points of each edge aligned - for e in cleanededges[1:]: - if DraftVecUtils.equals(common,e.valueAt(e.FirstParameter)): - edgelist.append(e) - common= e.valueAt(e.LastParameter) + #We first compare the last parameter of the first segment to see if it matches either end of the second segment. If not, it must need flipping. + if cleanededges[0].valueAt(cleanededges[0].LastParameter) in [cleanededges[1].valueAt(cleanededges[1].FirstParameter), cleanededges[1].valueAt(cleanededges[1].LastParameter)]: + edge0 = cleanededges[0] else: - newedge = PathUtils.reverseEdge(e) - common= newedge.valueAt(newedge.LastParameter) - edgelist.append(newedge) - - curveobj.append(area.Point(edgestart.x,edgestart.y)) + edge0 = PathUtils.reverseEdge(cleanededges[0]) + + edgelist.append(edge0) + #Now iterate the rest of the edges matching the last parameter of the previous segment. + for edge in cleanededges[1:]: + if edge.valueAt(edge.FirstParameter) == edgelist[-1].valueAt(edgelist[-1].LastParameter): + nextedge = edge + else: + nextedge = PathUtils.reverseEdge(edge) + edgelist.append(nextedge) + #print (str(area.Point(edgelist[0].Vertexes[0].X) + ", " + str(edgelist[0].Vertexes[0].Y))) + curveobj.append(area.Point(edgelist[0].Vertexes[0].X,edgelist[0].Vertexes[0].Y)) # seglist =[] # if direction=='CW': # edgelist.reverse() @@ -85,7 +94,7 @@ def makeAreaCurve(edges,direction,startpt=None,endpt=None): # else: # for e in edgelist: # seglist.append(e) - + for s in edgelist: curveobj.append(makeAreaVertex(s)) @@ -98,7 +107,7 @@ def makeAreaCurve(edges,direction,startpt=None,endpt=None): # curveobj.ChangeStart(area.Point(perppoint1[0].x,perppoint1[0].y)) # else: # curveobj.ChangeStart(area.Point(startpt.X,startpt.Y)) - curveobj.ChangeStart(area.Point(startpt.X,startpt.Y)) + curveobj.ChangeStart(area.Point(startpt.x,startpt.y)) if endpt: # future nearest point code yet to be worked out -fixme # v2 = Vector(endpt.X,endpt.Y,endpt.Z) @@ -107,16 +116,19 @@ def makeAreaCurve(edges,direction,startpt=None,endpt=None): # curveobj.ChangeEnd(area.Point(perppoint2[0].x,perppoint2[0].y)) # else: # curveobj.ChangeEnd(area.Point(endpt.X,endpt.Y)) - curveobj.ChangeEnd(area.Point(endpt.X,endpt.Y)) - - if direction == 'CW': - curveobj.Reverse() + curveobj.ChangeEnd(area.Point(endpt.x,endpt.y)) + if curveobj.IsClockwise() and direction == 'CCW': + curveobj.Reverse() + elif not curveobj.IsClockwise() and direction == 'CW': + curveobj.Reverse() + print "direction is clockwise: " + str(curveobj.IsClockwise()) + print "numvertices: " + str(curveobj.getNumVertices()) return curveobj # profile command, -# side_of_line should be 'left' or 'right' or 'on' +# side_of_line should be 'Left' or 'Right' or 'On' def profile(curve,side_of_line,radius=1.0,vertfeed=0.0,horizfeed=0.0,offset_extra=0.0, \ rapid_safety_space=None,clearance=None,start_depth=None,stepdown=None, \ final_depth=None,use_CRC=False, \ @@ -124,16 +136,18 @@ def profile(curve,side_of_line,radius=1.0,vertfeed=0.0,horizfeed=0.0,offset_extr roll_start_pt=None,roll_end_pt=None): output = "" + output += "G0 Z" + str(clearance)+"\n" + offset_curve = area.Curve(curve) if offset_curve.getNumVertices() <= 1: raise Exception,"Sketch has no elements!" - if side_of_line == "on": + if side_of_line == "On": use_CRC =False - elif (side_of_line == "left") or (side_of_line == "right"): + elif (side_of_line == "Left") or (side_of_line == "Right"): # get tool radius plus little bit of extra offset, if needed to clean up profile a little more offset = radius + offset_extra - if side_of_line == 'left': + if side_of_line == 'Left': offset_curve.Offset(offset) else: @@ -142,7 +156,7 @@ def profile(curve,side_of_line,radius=1.0,vertfeed=0.0,horizfeed=0.0,offset_extr if offset_curve == False: raise Exception, "couldn't offset kurve " + str(offset_curve) else: - raise Exception,"Side must be 'left','right', or 'on'" + raise Exception,"Side must be 'Left','Right', or 'On'" #=============================================================================== # #roll_on roll_off section @@ -244,15 +258,412 @@ def profile(curve,side_of_line,radius=1.0,vertfeed=0.0,horizfeed=0.0,offset_extr return output -def makePath(edges,side,radius,vertfeed,horizfeed,offset_extra,rapid_safety_space,clearance,start_depth,step_down,final_depth,use_CRC,direction,startpt=None,endpt=None): +# def makePath(edges,side,radius,vertfeed,horizfeed,offset_extra,rapid_safety_space,clearance,start_depth,step_down,final_depth,use_CRC,direction,startpt=None,endpt=None): - curve = makeAreaCurve(edges,direction,startpt, endpt) - if direction == 'CW': - curve.Reverse() - path = profile(curve,side,radius,vertfeed,horizfeed,offset_extra,rapid_safety_space,clearance,start_depth,step_down,final_depth,use_CRC) - del curve - return path +# curve = makeAreaCurve(edges,direction,startpt, endpt) +# path = profile(curve,side,radius,vertfeed,horizfeed,offset_extra,rapid_safety_space,clearance,start_depth,step_down,final_depth,use_CRC) +# del curve +# return path + +# def edgedumper (mylist): +# import Part, PathUtils +# print "The amazing edgedumper" +# print "mylist: " + str(mylist) +# edgs = PathUtils.cleanedges(mylist, 0.01) +# edgs = Part.Wire(edgs) +# for i in edgs.Edges: +# mystring = str(i) +# for v in i.Vertexes: +# mystring = mystring + " : " + str(v.Point) +# print mystring + +'''The following procedures are copied almost directly from heekscnc kurve_funcs.py. They depend on nc directory existing below PathScripts and have not been +throughly optimized, understood, or tested for FreeCAD.''' +def profile2(curve, direction = "on", radius = 1.0, vertfeed=0.0,horizfeed=0.0, offset_extra = 0.0, roll_radius = 2.0, roll_on = None, roll_off = None, depthparams = None, extend_at_start = 0.0, extend_at_end = 0.0, lead_in_line_len=0.0,lead_out_line_len= 0.0): + print "we're in" + from PathScripts.nc.nc import * + global tags + direction = direction.lower() + offset_curve = area.Curve(curve) + if direction == "on": + use_CRC() == False + + if direction != "on": + if direction != "left" and direction != "right": + raise "direction must be left or right", direction + + # get tool diameter + offset = radius + offset_extra + if use_CRC() == False or (use_CRC()==True and CRC_nominal_path()==True): + if math.fabs(offset) > 0.00005: + if direction == "right": + offset = -offset + offset_success = offset_curve.Offset(offset) + if offset_success == False: + global using_area_for_offset + if curve.IsClosed() and (using_area_for_offset == False): + cw = curve.IsClockwise() + using_area_for_offset = True + a = area.Area() + a.append(curve) + a.Offset(-offset) + for curve in a.getCurves(): + curve_cw = curve.IsClockwise() + if cw != curve_cw: + curve.Reverse() + set_good_start_point(curve, False) + profile(curve, direction, 0.0, 0.0, roll_radius, roll_on, roll_off, depthparams, extend_at_start, extend_at_end, lead_in_line_len, lead_out_line_len) + using_area_for_offset = False + return + else: + raise Exception, "couldn't offset kurve " + str(offset_curve) + + # extend curve + if extend_at_start > 0.0: + span = offset_curve.GetFirstSpan() + new_start = span.p + span.GetVector(0.0) * ( -extend_at_start) + new_curve = area.Curve() + new_curve.append(new_start) + for vertex in offset_curve.getVertices(): + new_curve.append(vertex) + offset_curve = new_curve + + if extend_at_end > 0.0: + span = offset_curve.GetLastSpan() + new_end = span.v.p + span.GetVector(1.0) * extend_at_end + offset_curve.append(new_end) + + # remove tags further than radius from the offset kurve + new_tags = [] + for tag in tags: + if tag.dist(offset_curve) <= radius + 0.001: + new_tags.append(tag) + tags = new_tags + + if offset_curve.getNumVertices() <= 1: + raise "sketch has no spans!" + + # do multiple depths + depths = depthparams.get_depths() + + current_start_depth = depthparams.start_depth + + # tags + if len(tags) > 0: + # make a copy to restore to after each level + copy_of_offset_curve = area.Curve(offset_curve) + + prev_depth = depthparams.start_depth + + endpoint = None + + for depth in depths: + mat_depth = prev_depth + + if len(tags) > 0: + split_for_tags(offset_curve, radius, depthparams.start_depth, depth, depthparams.final_depth) + + # make the roll on and roll off kurves + roll_on_curve = area.Curve() + add_roll_on(offset_curve, roll_on_curve, direction, roll_radius, offset_extra, roll_on) + roll_off_curve = area.Curve() + add_roll_off(offset_curve, roll_off_curve, direction, roll_radius, offset_extra, roll_off) + if use_CRC(): + crc_start_point = area.Point() + add_CRC_start_line(offset_curve,roll_on_curve,roll_off_curve,radius,direction,crc_start_point,lead_in_line_len) + + # get the tag depth at the start + start_z = get_tag_z_for_span(0, offset_curve, radius, depthparams.start_depth, depth, depthparams.final_depth) + if start_z > mat_depth: mat_depth = start_z + + # rapid across to the start + s = roll_on_curve.FirstVertex().p + + # start point + if (endpoint == None) or (endpoint != s): + if use_CRC(): + rapid(crc_start_point.x,crc_start_point.y) + else: + rapid(s.x, s.y) + + # rapid down to just above the material + if endpoint == None: + rapid(z = mat_depth + depthparams.rapid_safety_space) + else: + rapid(z = mat_depth) + + # feed down to depth + mat_depth = depth + if start_z > mat_depth: mat_depth = start_z + feed(z = mat_depth) + + if use_CRC(): + start_CRC(direction == "left", radius) + # move to the startpoint + feed(s.x, s.y) + + # cut the roll on arc + cut_curve(roll_on_curve) + + # cut the main kurve + current_perim = 0.0 + + for span in offset_curve.GetSpans(): + # height for tags + current_perim += span.Length() + ez = get_tag_z_for_span(current_perim, offset_curve, radius, depthparams.start_depth, depth, depthparams.final_depth) + + if span.v.type == 0:#line + feed(span.v.p.x, span.v.p.y, ez) + else: + if span.v.type == 1:# anti-clockwise arc + arc_ccw(span.v.p.x, span.v.p.y, ez, i = span.v.c.x, j = span.v.c.y) + else: + arc_cw(span.v.p.x, span.v.p.y, ez, i = span.v.c.x, j = span.v.c.y) + + + # cut the roll off arc + cut_curve(roll_off_curve) + + endpoint = offset_curve.LastVertex().p + if roll_off_curve.getNumVertices() > 0: + endpoint = roll_off_curve.LastVertex().p + + #add CRC end_line + if use_CRC(): + crc_end_point = area.Point() + add_CRC_end_line(offset_curve,roll_on_curve,roll_off_curve,radius,direction,crc_end_point,lead_out_line_len) + if direction == "on": + rapid(z = depthparams.clearance_height) + else: + feed(crc_end_point.x, crc_end_point.y) + + + # restore the unsplit kurve + if len(tags) > 0: + offset_curve = area.Curve(copy_of_offset_curve) + if use_CRC(): + end_CRC() + + if endpoint != s: + # rapid up to the clearance height + rapid(z = depthparams.clearance_height) + + prev_depth = depth + + rapid(z = depthparams.clearance_height) + + del offset_curve + + if len(tags) > 0: + del copy_of_offset_curve + +class Tag: + def __init__(self, p, width, angle, height): + self.p = p + self.width = width # measured at the top of the tag. In the toolpath, the tag width will be this with plus the tool diameter, so that the finished tag has this "width" at it's smallest + self.angle = angle # the angle of the ramp in radians. Between 0 and Pi/2; 0 is horizontal, Pi/2 is vertical + self.height = height # the height of the tag, always measured above "final_depth" + self.ramp_width = self.height / math.tan(self.angle) + + def split_curve(self, curve, radius, start_depth, depth, final_depth): + tag_top_depth = final_depth + self.height + + if depth > tag_top_depth - 0.0000001: + return # kurve is above this tag, so doesn't need splitting + + height_above_depth = tag_top_depth - depth + ramp_width_at_depth = height_above_depth / math.tan(self.angle) + cut_depth = start_depth - depth + half_flat_top = radius + self.width / 2 + + d = curve.PointToPerim(self.p) + d0 = d - half_flat_top + perim = curve.Perim() + if curve.IsClosed(): + while d0 < 0: d0 += perim + while d0 > perim: d0 -= perim + p = curve.PerimToPoint(d0) + curve.Break(p) + d1 = d + half_flat_top + if curve.IsClosed(): + while d1 < 0: d1 += perim + while d1 > perim: d1 -= perim + p = curve.PerimToPoint(d1) + curve.Break(p) + + d0 = d - half_flat_top - ramp_width_at_depth + if curve.IsClosed(): + while d0 < 0: d0 += perim + while d0 > perim: d0 -= perim + p = curve.PerimToPoint(d0) + curve.Break(p) + d1 = d + half_flat_top + ramp_width_at_depth + if curve.IsClosed(): + while d1 < 0: d1 += perim + while d1 > perim: d1 -= perim + p = curve.PerimToPoint(d1) + curve.Break(p) + + def get_z_at_perim(self, current_perim, curve, radius, start_depth, depth, final_depth): + # return the z for this position on the kurve ( specified by current_perim ), for this tag + # if the position is not within the tag, then depth is returned + cut_depth = start_depth - depth + half_flat_top = radius + self.width / 2 + + z = depth + d = curve.PointToPerim(self.p) + dist_from_d = math.fabs(current_perim - d) + if dist_from_d < half_flat_top: + # on flat top of tag + z = final_depth + self.height + elif dist_from_d < half_flat_top + self.ramp_width: + # on ramp + dist_up_ramp = (half_flat_top + self.ramp_width) - dist_from_d + z = final_depth + dist_up_ramp * math.tan(self.angle) + if z < depth: z = depth + return z + + def dist(self, curve): + # return the distance from the tag point to the given kurve + d = curve.PointToPerim(self.p) + p = curve.PerimToPoint(d) + v = self.p - p + return v.length() + +tags = [] + +def add_roll_on(curve, roll_on_curve, direction, roll_radius, offset_extra, roll_on): + if direction == "on": roll_on = None + if curve.getNumVertices() <= 1: return + first_span = curve.GetFirstSpan() + + if roll_on == None: + rollstart = first_span.p + elif roll_on == 'auto': + if roll_radius < 0.0000000001: + rollstart = first_span.p + v = first_span.GetVector(0.0) + if direction == 'right': + off_v = area.Point(v.y, -v.x) + else: + off_v = area.Point(-v.y, v.x) + rollstart = first_span.p + off_v * roll_radius + else: + rollstart = roll_on + + rvertex = area.Vertex(first_span.p) + + if first_span.p == rollstart: + rvertex.type = 0 + else: + v = first_span.GetVector(0.0) # get start direction + rvertex.c, rvertex.type = area.TangentialArc(first_span.p, rollstart, -v) + rvertex.type = -rvertex.type # because TangentialArc was used in reverse + # add a start roll on point + roll_on_curve.append(rollstart) + + # add the roll on arc + roll_on_curve.append(rvertex) + +def add_roll_off(curve, roll_off_curve, direction, roll_radius, offset_extra, roll_off): + if direction == "on": return + if roll_off == None: return + if curve.getNumVertices() <= 1: return + + last_span = curve.GetLastSpan() + + if roll_off == 'auto': + if roll_radius < 0.0000000001: return + v = last_span.GetVector(1.0) # get end direction + if direction == 'right': + off_v = area.Point(v.y, -v.x) + else: + off_v = area.Point(-v.y, v.x) + + rollend = last_span.v.p + off_v * roll_radius; + else: + rollend = roll_off + + # add the end of the original kurve + roll_off_curve.append(last_span.v.p) + if rollend == last_span.v.p: return + rvertex = area.Vertex(rollend) + v = last_span.GetVector(1.0) # get end direction + rvertex.c, rvertex.type = area.TangentialArc(last_span.v.p, rollend, v) + + # add the roll off arc + roll_off_curve.append(rvertex) + +def clear_tags(): + global tags + tags = [] + +def add_tag(p, width, angle, height): + global tags + tag = Tag(p, width, angle, height) + tags.append(tag) + +def split_for_tags( curve, radius, start_depth, depth, final_depth ): + global tags + for tag in tags: + tag.split_curve(curve, radius, start_depth, depth, final_depth) + +def get_tag_z_for_span(current_perim, curve, radius, start_depth, depth, final_depth): + global tags + max_z = None + perim = curve.Perim() + for tag in tags: + z = tag.get_z_at_perim(current_perim, curve, radius, start_depth, depth, final_depth) + if max_z == None or z > max_z: + max_z = z + if curve.IsClosed(): + # do the same test, wrapped around the closed kurve + z = tag.get_z_at_perim(current_perim - perim, curve, radius, start_depth, depth, final_depth) + if max_z == None or z > max_z: + max_z = z + z = tag.get_z_at_perim(current_perim + perim, curve, radius, start_depth, depth, final_depth) + if max_z == None or z > max_z: + max_z = z + + return max_z + +def cut_curve(curve): + for span in curve.GetSpans(): + if span.v.type == 0:#line + feed(span.v.p.x, span.v.p.y) + else: + if span.v.type == 1:# anti-clockwise arc + arc_ccw(span.v.p.x, span.v.p.y, i = span.v.c.x, j = span.v.c.y) + else: + arc_cw(span.v.p.x, span.v.p.y, i = span.v.c.x, j = span.v.c.y) + +def add_CRC_start_line(curve,roll_on_curve,roll_off_curve,radius,direction,crc_start_point,lead_in_line_len): + first_span = curve.GetFirstSpan() + v = first_span.GetVector(0.0) + if direction == 'right': + off_v = area.Point(v.y, -v.x) + else: + off_v = area.Point(-v.y, v.x) + startpoint_roll_on = roll_on_curve.FirstVertex().p + crc_start = startpoint_roll_on + off_v * lead_in_line_len + crc_start_point.x = crc_start.x + crc_start_point.y = crc_start.y + +def add_CRC_end_line(curve,roll_on_curve,roll_off_curve,radius,direction,crc_end_point,lead_out_line_len): + last_span = curve.GetLastSpan() + v = last_span.GetVector(1.0) + if direction == 'right': + off_v = area.Point(v.y, -v.x) + else: + off_v = area.Point(-v.y, v.x) + endpoint_roll_off = roll_off_curve.LastVertex().p + crc_end = endpoint_roll_off + off_v * lead_out_line_len + crc_end_point.x = crc_end.x + crc_end_point.y = crc_end.y + +using_area_for_offset = False diff --git a/src/Mod/Path/PathScripts/PathMachine.py b/src/Mod/Path/PathScripts/PathMachine.py index cb877c3184..fcca0cf7d2 100644 --- a/src/Mod/Path/PathScripts/PathMachine.py +++ b/src/Mod/Path/PathScripts/PathMachine.py @@ -41,26 +41,26 @@ except AttributeError: class Machine: def __init__(self,obj): - obj.addProperty("App::PropertyString", "MachineName","Base","Name of the Machine that will use the CNC program") + obj.addProperty("App::PropertyString", "MachineName","Base",translate("Machine Name","Name of the Machine that will use the CNC program")) - obj.addProperty("App::PropertyFile", "PostProcessor", "CodeOutput", "Select the Post Processor file for this machine") - obj.setEditorMode("PostProcessor",1) #set to read only - obj.addProperty("App::PropertyEnumeration", "MachineUnits","CodeOutput", "Units that the machine works in, ie Metric or Inch") + obj.addProperty("App::PropertyFile", "PostProcessor", "CodeOutput", translate("Post Processor","Select the Post Processor file for this machine")) + #obj.setEditorMode("PostProcessor",1) #set to read only + obj.addProperty("App::PropertyEnumeration", "MachineUnits","CodeOutput", translate( "Machine Units", "Units that the machine works in, ie Metric or Inch")) obj.MachineUnits=['Metric', 'Inch'] - obj.addProperty("Path::PropertyTooltable","Tooltable", "Base","The tooltable used for this CNC program") + obj.addProperty("Path::PropertyTooltable","Tooltable", "Base",translate("Tool Table","The tooltable used for this CNC program")) - obj.addProperty("App::PropertyDistance", "X_Max", "Limits", "The Maximum distance in X the machine can travel") - obj.addProperty("App::PropertyDistance", "Y_Max", "Limits", "The Maximum distance in X the machine can travel") - obj.addProperty("App::PropertyDistance", "Z_Max", "Limits", "The Maximum distance in X the machine can travel") + obj.addProperty("App::PropertyDistance", "X_Max", "Limits", translate("X Maximum Limit","The Maximum distance in X the machine can travel")) + obj.addProperty("App::PropertyDistance", "Y_Max", "Limits", translate("Y Maximum Limit","The Maximum distance in X the machine can travel")) + obj.addProperty("App::PropertyDistance", "Z_Max", "Limits", translate("Y Maximum Limit","The Maximum distance in X the machine can travel")) - obj.addProperty("App::PropertyDistance", "X_Min", "Limits", "The Minimum distance in X the machine can travel") - obj.addProperty("App::PropertyDistance", "Y_Min", "Limits", "The Minimum distance in X the machine can travel") - obj.addProperty("App::PropertyDistance", "Z_Min", "Limits", "The Minimum distance in X the machine can travel") + obj.addProperty("App::PropertyDistance", "X_Min", "Limits", translate("X Minimum Limit","The Minimum distance in X the machine can travel")) + obj.addProperty("App::PropertyDistance", "Y_Min", "Limits", translate("Y Minimum Limit","The Minimum distance in X the machine can travel")) + obj.addProperty("App::PropertyDistance", "Z_Min", "Limits", translate("Y Minimum Limit","The Minimum distance in X the machine can travel")) - obj.addProperty("App::PropertyDistance", "X", "HomePosition", "Home position of machine, in X (mainly for visualization)") - obj.addProperty("App::PropertyDistance", "Y", "HomePosition", "Home position of machine, in Y (mainly for visualization)") - obj.addProperty("App::PropertyDistance", "Z", "HomePosition", "Home position of machine, in Z (mainly for visualization)") + obj.addProperty("App::PropertyDistance", "X", "HomePosition", translate("X Home Position","Home position of machine, in X (mainly for visualization)")) + obj.addProperty("App::PropertyDistance", "Y", "HomePosition", translate("Y Home Position","Home position of machine, in Y (mainly for visualization)")) + obj.addProperty("App::PropertyDistance", "Z", "HomePosition", translate("Z Home Position","Home position of machine, in Z (mainly for visualization)")) obj.Proxy = self mode = 2 @@ -199,15 +199,15 @@ class _ViewProviderMachine: class CommandPathMachine: def GetResources(self): return {'Pixmap' : 'Path-Machine', - 'MenuText': QtCore.QT_TRANSLATE_NOOP("Path_Machine","Machine Object"), + 'MenuText': QtCore.QT_TRANSLATE_NOOP("PathMachine","Machine Object"), 'Accel': "P, M", - 'ToolTip': QtCore.QT_TRANSLATE_NOOP("Path_Machine","Create a Machine object")} + 'ToolTip': QtCore.QT_TRANSLATE_NOOP("PathMachine","Create a Machine object")} def IsActive(self): return not FreeCAD.ActiveDocument is None def Activated(self): - FreeCAD.ActiveDocument.openTransaction(translate("Path_Machine","Create a Machine object")) + FreeCAD.ActiveDocument.openTransaction(translate("PathMachine","Create a Machine object")) CommandPathMachine.Create() FreeCAD.ActiveDocument.commitTransaction() FreeCAD.ActiveDocument.recompute() diff --git a/src/Mod/Path/PathScripts/PathProfile.py b/src/Mod/Path/PathScripts/PathProfile.py index 4b75a66a0e..fe0ccf9b12 100644 --- a/src/Mod/Path/PathScripts/PathProfile.py +++ b/src/Mod/Path/PathScripts/PathProfile.py @@ -22,11 +22,23 @@ #* * #*************************************************************************** -import FreeCAD,FreeCADGui,Path,PathGui +import FreeCAD,Path from FreeCAD import Vector -from PySide import QtCore,QtGui from PathScripts import PathUtils,PathSelection,PathProject +if FreeCAD.GuiUp: + import FreeCADGui, PathGui + from PySide import QtCore, QtGui + from DraftTools import translate + from pivy import coin +else: + def translate(ctxt,txt): + return txt + +__title__="Path Profile Operation" +__author__ = "sliptonic (Brad Collette)" +__url__ = "http://www.freecadweb.org" + """Path Profile object and FreeCAD command""" # Qt tanslation handling @@ -38,59 +50,84 @@ except AttributeError: def translate(context, text, disambig=None): return QtGui.QApplication.translate(context, text, disambig) +def makeProfile(self, name="Profile"): + '''creates a Profile operation''' + + #obj = FreeCAD.ActiveDocument.addObject("App::FeaturePython",name) + #obj.Label = translate("Path",name) + obj=self + ObjectProfile(obj) + if FreeCAD.GuiUp: + _ViewProviderProfile(obj.ViewObject) + + locations = [] + angles = [] + lengths = [] + heights = [] + + obj.locs = locations + obj.angles = angles + obj.lengths = lengths + obj.heights = heights + FreeCAD.ActiveDocument.recompute() + return obj + class ObjectProfile: def __init__(self,obj): - obj.addProperty("App::PropertyLinkSub","Base","Path","The base geometry of this toolpath") - obj.addProperty("App::PropertyLinkSub","Face1","Path","First Selected Face to help determine where final depth of tool path is") - obj.addProperty("App::PropertyLinkSub","Face2","Path","Second Selected Face to help determine where the upper level of tool path is") - obj.addProperty("App::PropertyBool","PathClosed","Path","If the toolpath is a closed polyline this is True") - obj.addProperty("App::PropertyLinkSub","Edge1","Path","First Selected Edge to help determine which geometry to make a toolpath around") - obj.addProperty("App::PropertyLinkSub","Edge2","Path","Second Selected Edge to help determine which geometry to make a toolpath around") - obj.addProperty("App::PropertyBool","Active","Path","Make False, to prevent operation from generating code") - obj.addProperty("App::PropertyBool","UsePlacements","Path","make True, if using the profile operation placement properties to transform toolpath in post processor") + obj.addProperty("App::PropertyLinkSub","Base","Path",translate("Parent Object","The base geometry of this toolpath")) + obj.addProperty("App::PropertyBool","Active","Path",translate("Path","Make False, to prevent operation from generating code")) + obj.addProperty("App::PropertyString","Comment","Path",translate("Path","An optional comment for this profile")) - obj.addProperty("App::PropertyIntegerConstraint","ToolNumber","Tool","The tool number in use") + obj.addProperty("App::PropertyEnumeration", "Algorithm", "Algorithm",translate("Path", "The library or algorithm used to generate the path")) + obj.Algorithm = ['OCC Native','libarea'] + + obj.addProperty("App::PropertyIntegerConstraint","ToolNumber","Tool",translate("Path","The tool number in use")) obj.ToolNumber = (0,0,1000,1) obj.setEditorMode('ToolNumber',1) #make this read only #Depth Properties - obj.addProperty("App::PropertyDistance", "ClearanceHeight", "Depth","The height needed to clear clamps and obstructions") - obj.addProperty("App::PropertyLength", "StepDown", "Depth", "Incremental Step Down of Tool") - obj.addProperty("App::PropertyBool","UseStartDepth","Depth","make True, if manually specifying a Start Start Depth") - obj.addProperty("App::PropertyDistance", "StartDepth", "Depth", "Starting Depth of Tool- first cut depth in Z") - obj.addProperty("App::PropertyDistance", "FinalDepth", "Depth", "Final Depth of Tool- lowest value in Z") - obj.addProperty("App::PropertyDistance", "RetractHeight", "Depth","The height desired to retract tool when path is finished") - obj.addProperty("App::PropertyString","Comment","Path","An optional comment for this profile") + obj.addProperty("App::PropertyDistance", "ClearanceHeight", "Depth", translate("Path","The height needed to clear clamps and obstructions")) + obj.addProperty("App::PropertyDistance", "SafeHeight", "Depth", translate("Path","Rapid Safety Height between locations.")) + obj.addProperty("App::PropertyFloatConstraint", "StepDown", "Depth", translate("Path","Incremental Step Down of Tool")) + obj.StepDown = (1,0.01,1000,0.5) + obj.addProperty("App::PropertyDistance", "StartDepth", "Depth", translate("Path","Starting Depth of Tool- first cut depth in Z")) + obj.addProperty("App::PropertyDistance", "FinalDepth", "Depth", translate("Path","Final Depth of Tool- lowest value in Z")) + #obj.addProperty("App::PropertyDistance", "RetractHeight", "Depth", translate("Retract Height","The height desired to retract tool when path is finished")) #Feed Properties - obj.addProperty("App::PropertySpeed", "VertFeed", "Feed","Feed rate for vertical moves in Z") - obj.addProperty("App::PropertySpeed", "HorizFeed", "Feed","Feed rate for horizontal moves") + obj.addProperty("App::PropertySpeed", "VertFeed", "Feed",translate("Path","Feed rate for vertical moves in Z")) + obj.addProperty("App::PropertySpeed", "HorizFeed", "Feed",translate("Path","Feed rate for horizontal moves")) #Start Point Properties - obj.addProperty("App::PropertyVector","StartPoint","Start Point","The start point of this path") - obj.addProperty("App::PropertyBool","UseStartPoint","Start Point","make True, if specifying a Start Point") - obj.addProperty("App::PropertyLength", "ExtendAtStart", "Start Point","extra length of tool path before start of part edge") - obj.addProperty("App::PropertyLength", "LeadInLineLen", "Start Point","length of straight segment of toolpath that comes in at angle to first part edge") + obj.addProperty("App::PropertyVector","StartPoint","Start Point",translate("Path_Profile","The start point of this path")) + obj.addProperty("App::PropertyBool","UseStartPoint","Start Point",translate("Path_Profile","make True, if specifying a Start Point")) + obj.addProperty("App::PropertyLength", "ExtendAtStart", "Start Point", translate("Path_Profile", "extra length of tool path before start of part edge")) + obj.addProperty("App::PropertyLength", "LeadInLineLen", "Start Point", translate("Path_Profile","length of straight segment of toolpath that comes in at angle to first part edge")) #End Point Properties - obj.addProperty("App::PropertyBool","UseEndPoint","End Point","make True, if specifying an End Point") - obj.addProperty("App::PropertyLength", "ExtendAtEnd", "End Point","extra length of tool path after end of part edge") - obj.addProperty("App::PropertyLength", "LeadOutLineLen", "End Point","length of straight segment of toolpath that comes in at angle to last part edge") - obj.addProperty("App::PropertyVector","EndPoint","End Point","The end point of this path") + obj.addProperty("App::PropertyBool","UseEndPoint","End Point",translate("Path_Profile","make True, if specifying an End Point")) + obj.addProperty("App::PropertyLength", "ExtendAtEnd", "End Point", translate("Path_Profile","extra length of tool path after end of part edge")) + obj.addProperty("App::PropertyLength", "LeadOutLineLen", "End Point", translate("Path_Profile","length of straight segment of toolpath that comes in at angle to last part edge")) + obj.addProperty("App::PropertyVector","EndPoint","End Point",translate("Path_Profile","The end point of this path")) #Profile Properties - obj.addProperty("App::PropertyEnumeration", "Side", "Profile","Side of edge that tool should cut") + obj.addProperty("App::PropertyEnumeration", "Side", "Profile", translate("Path_Profile","Side of edge that tool should cut")) obj.Side = ['Left','Right','On'] #side of profile that cutter is on in relation to direction of profile - obj.addProperty("App::PropertyEnumeration", "Direction", "Profile","The direction that the toolpath should go around the part ClockWise CW or CounterClockWise CCW") + obj.addProperty("App::PropertyEnumeration", "Direction", "Profile",translate("Path_Profile", "The direction that the toolpath should go around the part ClockWise CW or CounterClockWise CCW")) obj.Direction = ['CW','CCW'] #this is the direction that the profile runs + obj.addProperty("App::PropertyBool","UseComp","Profile",translate("Path_Profile","make True, if using Cutter Radius Compensation")) - obj.addProperty("App::PropertyDistance", "RollRadius", "Profile","Radius at start and end") - obj.addProperty("App::PropertyDistance", "OffsetExtra", "Profile","Extra value to stay away from final profile- good for roughing toolpath") - obj.addProperty("App::PropertyLength", "SegLen", "Profile","Tesselation value for tool paths made from beziers, bsplines, and ellipses") + obj.addProperty("App::PropertyDistance", "RollRadius", "Profile", translate("Path_Profile","Radius at start and end")) + obj.addProperty("App::PropertyDistance", "OffsetExtra", "Profile",translate("Path_Profile","Extra value to stay away from final profile- good for roughing toolpath")) + obj.addProperty("App::PropertyLength", "SegLen", "Profile",translate("Path_Profile","Tesselation value for tool paths made from beziers, bsplines, and ellipses")) + obj.addProperty("App::PropertyVectorList", "locs", "Tags", translate("Path_Profile", "List of holding tag locations")) + obj.addProperty("App::PropertyFloatList","angles","Tags", translate("Path_Profile", "List of angles for the holding tags")) + obj.addProperty("App::PropertyFloatList","heights","Tags", translate("Path_Profile", "List of angles for the holding tags")) + obj.addProperty("App::PropertyFloatList","lengths","Tags", translate("Path_Profile", "List of angles for the holding tags")) obj.Proxy = self @@ -101,68 +138,125 @@ class ObjectProfile: return None def execute(self,obj): + import Part, DraftGeomUtils, math + import PathScripts.PathKurveUtils as PathKurveUtils + from PathScripts.PathUtils import depth_params + if obj.Base: - # tie the toolnumber to the PathLoadTool object ToolNumber if len(obj.InList)>0: #check to see if obj is in the Project group yet project = obj.InList[0] tl = int(PathUtils.changeTool(obj,project)) obj.ToolNumber= tl - tool = PathUtils.getTool(obj,obj.ToolNumber) if tool: radius = tool.Diameter/2 else: - # temporary value, to be taken from the properties later on - radius = 0.001 - if obj.Base[0].Shape.ShapeType == "Wire": #a pure wire was picked - wire = obj.Base[0].Shape - else: #we are dealing with a face and it's edges or just a face - if obj.Edge1: - e1 = FreeCAD.ActiveDocument.getObject(obj.Base[0].Name).Shape.Edges[eval(obj.Edge1[1][0].lstrip('Edge'))-1] - if e1.BoundBox.ZMax <> e1.BoundBox.ZMin: - FreeCAD.Console.PrintError('vertical edges not valid yet\n') - return - if obj.Base[0].Shape.ShapeType =='Wire': - wire = obj.Base[0].Shape - if obj.Base[0].Shape.ShapeType =='Solid' or obj.Base[0].Shape.ShapeType =='Compound': - shape = obj.Base[0].Shape - for fw in shape.Wires: - if (fw.BoundBox.ZMax == e1.BoundBox.ZMax) and (fw.BoundBox.ZMin == e1.BoundBox.ZMin): - for e in fw.Edges: - if e.isSame(e1): - #FreeCAD.Console.PrintMessage('found the same objects\n') - wire = fw - elif obj.Face1: # we are only dealing with a face or faces - f1 = FreeCAD.ActiveDocument.getObject(obj.Base[0].Name).Shape.Faces[eval(obj.Face1[1][0].lstrip('Face'))-1] - # make the side Left and direction CW for normal cnc milling - obj.Direction = 'CW' - obj.Side = "Left" - # we only consider the outer wire if this is a single Face - wire = f1.OuterWire + # temporary value,in case we don't have any tools defined already + radius = 0.25 + + depthparams = depth_params (obj.ClearanceHeight.Value, obj.SafeHeight.Value, obj.StartDepth.Value, obj.StepDown, 0.0, obj.FinalDepth.Value, None) + + clearance = obj.ClearanceHeight.Value + step_down=obj.StepDown + start_depth=obj.StartDepth.Value + final_depth=obj.FinalDepth.Value + rapid_safety_space=obj.SafeHeight.Value + + side=obj.Side + offset_extra=obj.OffsetExtra.Value + use_CRC=obj.UseComp + vf=obj.VertFeed.Value + hf=obj.HorizFeed.Value + seglen=obj.SegLen.Value + direction = obj.Direction + + + # we only consider the outer wire if this is a Face + shape = getattr(obj.Base[0].Shape,obj.Base[1][0]) + if shape.ShapeType in ["Edge"]: + edges = [getattr(obj.Base[0].Shape,sub) for sub in obj.Base[1]] + wire = Part.Wire(edges) + + if not wire.Edges[0].isSame(shape): + wire.Edges.reverse() - if obj.Direction == 'CCW': - clockwise=False else: - clockwise=True - output ="" - output += '('+ str(obj.Comment)+')\n' + wire = shape.OuterWire - FirstEdge= None - if obj.Edge1: - ename = obj.Edge1[1][0] - edgeNumber = int(ename[4:])-1 - FirstEdge = obj.Base[0].Shape.Edges[edgeNumber] - ZMax = obj.Base[0].Shape.BoundBox.ZMax + edgelist = wire.Edges + #edgelist = Part.__sortEdges__(wire.Edges) - ZCurrent = obj.ClearanceHeight.Value - - if obj.UseStartDepth: - output += PathUtils.MakePath(wire,obj.Side,radius,clockwise,obj.ClearanceHeight.Value,obj.StepDown.Value,obj.StartDepth.Value, obj.FinalDepth.Value,FirstEdge,obj.PathClosed,obj.SegLen.Value,obj.VertFeed.Value,obj.HorizFeed.Value) + if obj.StartPoint and obj.UseStartPoint: + startpoint = obj.StartPoint else: - output += PathUtils.MakePath(wire,obj.Side,radius,clockwise,obj.ClearanceHeight.Value,obj.StepDown.Value,ZMax, obj.FinalDepth.Value,FirstEdge,obj.PathClosed,obj.SegLen.Value,obj.VertFeed.Value,obj.HorizFeed.Value) + startpoint = None + if obj.EndPoint and obj.UseEndPoint: + endpoint = obj.EndPoint + else: + endpoint = None + + edgelist = Part.__sortEdges__(edgelist) + + if obj.Algorithm == "OCC Native": + output = "" + output += '('+ str(obj.Comment)+')\n' + + if obj.Direction == 'CCW': + clockwise=False + else: + clockwise=True + + FirstEdge= None + PathClosed = DraftGeomUtils.isReallyClosed(wire) + + output += PathUtils.MakePath(wire, side, radius, clockwise, clearance, step_down, start_depth, final_depth, FirstEdge, PathClosed, seglen, vf, hf) + + else: + try: + import area + except: + FreeCAD.Console.PrintError(translate("Path","libarea needs to be installed for this command to work.\n")) + return + + PathKurveUtils.output('mem') + output = "" + curve = PathKurveUtils.makeAreaCurve(edgelist,direction,startpoint, endpoint) + + '''The following line uses a profile function written for use with FreeCAD. It's clean but incomplete. It doesn't handle + holding tags + start location + CRC + or probably other features in heekscnc''' + #output += PathKurveUtils.profile(curve, side, radius, vf, hf, offset_extra, rapid_safety_space, clearance, start_depth, step_down, final_depth, use_CRC) + + '''The following calls the original procedure from heekscnc profile function. This, in turn, calls many other procedures to modify the profile. + This procedure is hacked together from heekscnc and has not been thoroughly reviewed or understood for FreeCAD. It can probably be + thoroughly optimized and improved but it'll take a smarter mind than mine to do it. -sliptonic Feb16''' + roll_radius = 2.0 + extend_at_start = 0.0 + extend_at_end = 0.0 + lead_in_line_len=0.0 + lead_out_line_len= 0.0 + + ''' + + Right here, I need to know the Holding Tags group from the tree that refers to this profile operation and build up the tags for PathKurve Utils. + I need to access the location vector, length, angle in radians and height. + + ''' + PathKurveUtils.clear_tags() + for i in range(len(obj.locs)): + tag = obj.locs[i] + h = obj.heights[i] + l = obj.lengths[i] + a = math.radians(obj.angles[i]) + PathKurveUtils.add_tag(area.Point(tag.x,tag.y), l, a, h) + + PathKurveUtils.profile2(curve, side, radius, vf, hf, offset_extra, roll_radius, None,None, depthparams, extend_at_start, extend_at_end, lead_in_line_len,lead_out_line_len) + output += PathKurveUtils.retrieve_gcode() if obj.Active: path = Path.Path(output) @@ -175,19 +269,141 @@ class ObjectProfile: obj.ViewObject.Visibility = False +# class ViewProviderProfile: + +# def __init__(self,vobj): +# vobj.Proxy = self + +# def attach(self,vobj): +# self.Object = vobj.Object +# return + +# def getIcon(self): +# return ":/icons/Path-Profile.svg" + +# def __getstate__(self): +# return None + +# def __setstate__(self,state): +# return None -class ViewProviderProfile: +class CommandPathProfile: + def GetResources(self): + return {'Pixmap' : 'Path-Profile', + 'MenuText': QtCore.QT_TRANSLATE_NOOP("PathProfile","Profile"), + 'Accel': "P, P", + 'ToolTip': QtCore.QT_TRANSLATE_NOOP("PathProfile","Creates a Path Profile object from selected faces")} + + def IsActive(self): + return not FreeCAD.ActiveDocument is None + + def Activated(self): + import Path + from PathScripts import PathProject, PathUtils, PathKurve, PathKurveUtils + + # check that the selection contains exactly what we want + selection = FreeCADGui.Selection.getSelectionEx() + if len(selection) != 1: + FreeCAD.Console.PrintError(translate("Path","Select one or more edges or a face from one Document object.\n")) + return + if len(selection[0].SubObjects) == 0: + FreeCAD.Console.PrintError(translate("Path","Select one or more edges or a face from one Document object.\n")) + return + for s in selection[0].SubObjects: + if s.ShapeType != "Edge": + if (s.ShapeType != "Face") or (len(selection[0].SubObjects) != 1): + FreeCAD.Console.PrintError(translate("Path","Please select only edges or a single face\n")) + return + if selection[0].SubObjects[0].ShapeType == "Edge": + try: + import Part + w = Part.Wire(selection[0].SubObjects) + except: + FreeCAD.Console.PrintError(translate("Path","The selected edges don't form a loop\n")) + return + + # if everything is ok, execute and register the transaction in the undo/redo stack + + # Take a guess at some reasonable values for Finish depth. + bb = selection[0].Object.Shape.BoundBox #parent boundbox + fbb = selection[0].SubObjects[0].BoundBox #feature boundbox + if fbb.ZMax < bb.ZMax: + zbottom = fbb.ZMax + else: + zbottom = bb.ZMin + + FreeCAD.ActiveDocument.openTransaction(translate("Path","Create a Profile operation using libarea")) + FreeCADGui.addModule("PathScripts.PathProfile") + FreeCADGui.doCommand('obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython","Profile")') + FreeCADGui.doCommand('PathScripts.PathProfile.makeProfile(obj)') + #FreeCADGui.doCommand('PathScripts.PathProfile.ObjectProfile(obj)') + + FreeCADGui.doCommand('obj.Active = True') + subs ="[" + for s in selection[0].SubElementNames: + subs += '"' + s + '",' + subs += "]" + FreeCADGui.doCommand('obj.Base = (FreeCAD.ActiveDocument.' + selection[0].ObjectName + ',' + subs + ')') + + #FreeCADGui.doCommand('obj.ViewObject.Proxy = 0') + FreeCADGui.doCommand('obj.ClearanceHeight = ' + str(bb.ZMax + 10.0)) + FreeCADGui.doCommand('obj.StepDown = 1.0') + FreeCADGui.doCommand('obj.StartDepth= ' + str(bb.ZMax)) + FreeCADGui.doCommand('obj.FinalDepth=' + str(zbottom)) + + FreeCADGui.doCommand('obj.SafeHeight = '+ str(bb.ZMax + 2.0)) + FreeCADGui.doCommand('obj.Side = "Left"') + FreeCADGui.doCommand('obj.OffsetExtra = 0.0') + FreeCADGui.doCommand('obj.Direction = "CW"') + FreeCADGui.doCommand('obj.UseComp = False') + FreeCADGui.doCommand('PathScripts.PathUtils.addToProject(obj)') + + FreeCAD.ActiveDocument.commitTransaction() + FreeCAD.ActiveDocument.recompute() + +class _ViewProviderProfile: + "A View Provider for the Holding object" def __init__(self,vobj): vobj.Proxy = self - def attach(self,vobj): - self.Object = vobj.Object - return - def getIcon(self): return ":/icons/Path-Profile.svg" + def claimChildren(self): + return [] + + def attach(self, vobj): + return + + def getDisplayModes(self,vobj): + return ["Default"] + + def getDefaultDisplayMode(self): + return "Default" + + def setDisplayMode(self,mode): + return mode + + def updateData(self,obj,prop): + return + def onChanged(self, vobj, prop): + return + + def setEdit(self,vobj,mode=0): + FreeCADGui.Control.closeDialog() + taskd = _EditPanel() + taskd.obj = vobj.Object + taskd.update() + FreeCADGui.Control.showDialog(taskd) + return True + + def unsetEdit(self,vobj,mode): + FreeCADGui.Control.closeDialog() + return + + def doubleClicked(self,vobj): + self.setEdit(vobj) def __getstate__(self): return None @@ -195,88 +411,160 @@ class ViewProviderProfile: def __setstate__(self,state): return None +class _EditPanel: + '''The editmode TaskPanel for profile tags''' + def __init__(self): + # the panel has a tree widget that contains categories + # for the subcomponents, such as additions, subtractions. + # the categories are shown only if they are not empty. -class CommandPathProfile: - def GetResources(self): - return {'Pixmap' : 'Path-Profile', - 'MenuText': QtCore.QT_TRANSLATE_NOOP("Path_Profile","Profile"), - 'Accel': "P, P", - 'ToolTip': QtCore.QT_TRANSLATE_NOOP("Path_Profile","Creates a Path Profile object from selected faces")} + self.updating = False - def IsActive(self): - return not FreeCAD.ActiveDocument is None + self.obj = None + self.form = QtGui.QWidget() + self.form.setObjectName("TaskPanel") + self.grid = QtGui.QGridLayout(self.form) + self.grid.setObjectName("grid") + self.title = QtGui.QLabel(self.form) + self.grid.addWidget(self.title, 0, 0, 1, 2) + + # tree + self.tree = QtGui.QTreeWidget(self.form) + self.grid.addWidget(self.tree, 1, 0, 1, 2) + self.tree.setColumnCount(4) + self.tree.header().resizeSection(0,50) + self.tree.header().resizeSection(1,80) + self.tree.header().resizeSection(2,60) + self.tree.header().resizeSection(3,60) + + # buttons + self.addButton = QtGui.QPushButton(self.form) + self.addButton.setObjectName("addButton") + self.addButton.setIcon(QtGui.QIcon(":/icons/Arch_Add.svg")) + self.grid.addWidget(self.addButton, 3, 0, 1, 1) + self.addButton.setEnabled(True) + + self.delButton = QtGui.QPushButton(self.form) + self.delButton.setObjectName("delButton") + self.delButton.setIcon(QtGui.QIcon(":/icons/Arch_Remove.svg")) + self.grid.addWidget(self.delButton, 3, 1, 1, 1) + self.delButton.setEnabled(True) + + QtCore.QObject.connect(self.addButton, QtCore.SIGNAL("clicked()"), self.addElement) + QtCore.QObject.connect(self.delButton, QtCore.SIGNAL("clicked()"), self.removeElement) + QtCore.QObject.connect(self.tree, QtCore.SIGNAL("itemChanged(QTreeWidgetItem *, int)"), self.edit) + self.update() + self.retranslateUi(self.form) + + def isAllowedAlterSelection(self): + return False + + def isAllowedAlterView(self): + return True + + def getStandardButtons(self): + return int(QtGui.QDialogButtonBox.Close) + + def update(self): + 'fills the treewidget' + self.updating = True + self.tree.clear() + if self.obj: + for i in range(len(self.obj.locs)): + item = QtGui.QTreeWidgetItem(self.tree) + item.setText(0,str(i+1)) + l = self.obj.locs[i] + item.setText(1,str(l.x)+", " + str(l.y) +", " + str(l.z)) + item.setText(2,str(self.obj.heights[i])) + item.setText(3,str(self.obj.lengths[i])) + item.setText(4,str(self.obj.angles[i])) + item.setFlags(item.flags() | QtCore.Qt.ItemIsEditable) + item.setTextAlignment(0,QtCore.Qt.AlignLeft) + self.retranslateUi(self.form) + self.updating = False + return + + def addElement(self): + self.updating = True + + item = QtGui.QTreeWidgetItem(self.tree) + item.setText(0,str(self.tree.topLevelItemCount())) + item.setText(1,"0.0, 0.0, 0.0") + item.setText(2, str(float(4.0))) + item.setText(3, str(float(10.0))) + item.setText(4, str(float(45.0))) + item.setFlags(item.flags() | QtCore.Qt.ItemIsEditable) + self.updating = False + + self.resetObject() + + def removeElement(self): + it = self.tree.currentItem() + if it: + nr = int(it.text(0))-1 + self.resetObject(remove=nr) + self.update() + + def edit(self,item,column): + if not self.updating: + self.resetObject() + + def resetObject(self,remove=None): + "transfers the values from the widget to the object" + loc = [] + h = [] + l = [] + a = [] - def Activated(self): - import Path - from PathScripts import PathUtils,PathProfile,PathProject - prjexists = False - selection = PathSelection.multiSelect() + for i in range(self.tree.topLevelItemCount()): + it = self.tree.findItems(str(i+1),QtCore.Qt.MatchExactly,0)[0] + if (remove == None) or (remove != i): + if it.text(1): + x = float(it.text(1).split()[0].rstrip(",")) + y = float(it.text(1).split()[1].rstrip(",")) + z = float(it.text(1).split()[2].rstrip(",")) + loc.append(Vector(x,y,z)) - if not selection: - return + else: + loc.append(0.0) + if it.text(2): + h.append(float(it.text(2))) + else: + h.append(4.0) + if it.text(3): + l.append(float(it.text(3))) + else: + l.append(5.0) + if it.text(4): + a.append(float(it.text(4))) + else: + a.append(45.0) - # if everything is ok, execute and register the transaction in the undo/redo stack - FreeCAD.ActiveDocument.openTransaction(translate("Path_Profile","Create Profile")) - FreeCADGui.addModule("PathScripts.PathProfile") + self.obj.locs = loc + self.obj.heights = h + self.obj.lengths = l + self.obj.angles = a - obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython","Profile") - PathProfile.ObjectProfile(obj) - PathProfile.ViewProviderProfile(obj.ViewObject) - - obj.Base = (FreeCAD.ActiveDocument.getObject(selection['objname'])) - - if selection['facenames']: - #FreeCAD.Console.PrintMessage('There are edges selected\n') - obj.Face1 = (FreeCAD.ActiveDocument.getObject(selection['objname']),selection['facenames'][0]) - if len(selection['facenames'])>1: - obj.Face2 = (FreeCAD.ActiveDocument.getObject(selection['objname']),selection['facenames'][-1]) - - if selection['edgenames']: - #FreeCAD.Console.PrintMessage('There are edges selected\n') - - obj.Edge1 =(FreeCAD.ActiveDocument.getObject(selection['objname']),(selection['edgenames'][0])) - if len(selection['edgenames'])>1: - obj.Edge2 =(FreeCAD.ActiveDocument.getObject(selection['objname']),(selection['edgenames'][-1])) - - if selection['pointlist']: - FreeCADGui.doCommand('from FreeCAD import Vector') - stptX, stptY, stptZ = selection['pointlist'][0].X, selection['pointlist'][0].Y, selection['pointlist'][0].Z - obj.StartPoint = Vector((stptX),(stptY),(stptZ)) - if len(selection['pointlist'])>1: # we have more than one point so we have an end point - endptX, endptY, endptZ = selection['pointlist'][-1].X, selection['pointlist'][-1].Y, selection['pointlist'][-1].Z - obj.EndPoint = Vector(endptX,endptY,endptZ) - if selection['pathwire'].isClosed(): - obj.PathClosed = True - if selection['clockwise']: - obj.Side = "Left" - obj.Direction = "CW" - elif selection['clockwise'] == False: - obj.Side = "Right" - obj.Direction = "CCW" - else: - obj.Side = "On" - obj.Direction = "CCW" - obj.PathClosed = False - - ZMax = obj.Base[0].Shape.BoundBox.ZMax - ZMin = obj.Base[0].Shape.BoundBox.ZMin - obj.StepDown.Value = 1.0 - obj.StartDepth.Value = ZMax- obj.StepDown.Value - obj.FinalDepth.Value = ZMin-1.0 - obj.ClearanceHeight.Value = ZMax + 5.0 - obj.SegLen.Value = 0.5 - obj.Active = True - obj.ViewObject.ShowFirstRapid = False - - project = PathUtils.addToProject(obj) - - tl = PathUtils.changeTool(obj,project) - if tl: - obj.ToolNumber = tl - - FreeCAD.ActiveDocument.commitTransaction() + self.obj.touch() FreeCAD.ActiveDocument.recompute() + def reject(self): + FreeCAD.ActiveDocument.recompute() + FreeCADGui.ActiveDocument.resetEdit() + return True + + def retranslateUi(self, TaskPanel=None): + TaskPanel.setWindowTitle(QtGui.QApplication.translate("Path", "Holding Tags", None, QtGui.QApplication.UnicodeUTF8)) + self.delButton.setText(QtGui.QApplication.translate("Path", "Remove", None, QtGui.QApplication.UnicodeUTF8)) + self.addButton.setText(QtGui.QApplication.translate("Path", "Add", None, QtGui.QApplication.UnicodeUTF8)) + self.title.setText(QtGui.QApplication.translate("Path", "Tag Locations and Properties", None, QtGui.QApplication.UnicodeUTF8)) + self.tree.setHeaderLabels([QtGui.QApplication.translate("Path", "", None, QtGui.QApplication.UnicodeUTF8), + QtGui.QApplication.translate("Path", "Location", None, QtGui.QApplication.UnicodeUTF8), + QtGui.QApplication.translate("Path", "Height", None, QtGui.QApplication.UnicodeUTF8), + QtGui.QApplication.translate("Path", "Length", None, QtGui.QApplication.UnicodeUTF8), + QtGui.QApplication.translate("Path", "Angle", None, QtGui.QApplication.UnicodeUTF8)]) + + if FreeCAD.GuiUp: # register the FreeCAD command FreeCADGui.addCommand('Path_Profile',CommandPathProfile()) diff --git a/src/Mod/Path/PathScripts/PathSelection.py b/src/Mod/Path/PathScripts/PathSelection.py index 4458a56399..3e566f2d28 100644 --- a/src/Mod/Path/PathScripts/PathSelection.py +++ b/src/Mod/Path/PathScripts/PathSelection.py @@ -39,43 +39,6 @@ def equals(p1,p2): -def Sort2Edges(edgelist): - '''Sort2Edges(edgelist) simple function to reorder the start and end pts of two edges - based on their selection order. Returns the list, the start point, - and their common point, => edgelist, vertex, vertex''' - if len(edgelist)>=2: - vlist = [] - e0 = edgelist[0] - e1=edgelist[1] - a0 = e0.Vertexes[0] - a1 = e0.Vertexes[1] - b0 = e1.Vertexes[0] - b1 = e1.Vertexes[1] - # comparison routine to order two edges: - if equals(a1,b0): - vlist.append((a0.Point.x,a0.Point.y)) - vlist.append((a1.Point.x,a1.Point.y)) - vlist.append((b1.Point.x,b1.Point.y)) - - if equals(a0,b0): - vlist.append((a1.Point.x,a1.Point.y)) - vlist.append((a0.Point.x,a0.Point.y)) - vlist.append((b1.Point.x,b1.Point.y)) - - if equals(a0,b1): - vlist.append((a1.Point.x,a1.Point.y)) - vlist.append((a0.Point.x,a0.Point.y)) - vlist.append((b0.Point.x,b0.Point.y)) - - if equals(a1,b1): - vlist.append((a0.Point.x,a0.Point.y)) - vlist.append((a1.Point.x,a1.Point.y)) - vlist.append((b0.Point.x,b0.Point.y)) - - edgestart = Vector(vlist[0][0],vlist[0][1],e0.Vertexes[1].Z) - edgecommon = Vector(vlist[1][0],vlist[1][1],e0.Vertexes[1].Z) - - return vlist,edgestart,edgecommon def segments(poly): ''' A sequence of (x,y) numeric coordinates pairs ''' @@ -92,135 +55,5 @@ def check_clockwise(poly): return clockwise -def multiSelect(): - ''' - multiSelect() A function for selecting elements of an object for CNC path operations. - Select just a face, an edge,or two edges to indicate direction, a vertex on the object, a point not on the object, - or some combination. Returns a dictionary. - ''' - sel = FreeCADGui.Selection.getSelectionEx() - numobjs = len([selobj.Object for selobj in sel]) - if numobjs == 0: - FreeCAD.Console.PrintError('Please select some objects and try again.\n') - return - goodselect = False - for s in sel: - for i in s.SubObjects: - if i.ShapeType == 'Face': - goodselect = True - if i.ShapeType == 'Edge': - goodselect = True - if i.ShapeType == 'Vertex': - goodselect = True - if not goodselect: - FreeCAD.Console.PrintError('Please select a face and/or edges along with points (optional) and try again.\n') - return - selItems = {} - selItems['objname']=None #the parent object name - a 3D solid - selItems['pointlist']=None #start and end points - selItems['pointnames']=None #names of points for document object - selItems['facenames']=None # the selected face name - selItems['facelist']=None #list of faces selected - selItems['edgelist']=None #some edges that could be selected along with points and faces - selItems['edgenames']=None - selItems['pathwire']=None #the whole wire around edges of the face - selItems['clockwise']=None - selItems['circles']=None - facenames = [] - edgelist =[] - edgenames=[] - ptlist=[] - ptnames=[] - circlelist=[] - face = False - edges = False - points = False - wireobj = False - circles = False - facelist= [] - for s in sel: - if s.Object.Shape.ShapeType in ['Solid','Compound','Wire','Vertex']: - if not (s.Object.Shape.ShapeType =='Vertex'): - objname = s.ObjectName - selItems['objname'] =objname - if s.Object.Shape.ShapeType == 'Wire': - wireobj = True - if s.Object.Shape.ShapeType == 'Vertex': - ptnames.append(s.ObjectName) -# ptlist.append(s.Object) - points = True - for sub in s.SubObjects: - if sub.ShapeType =='Face': - facelist.append(sub) - face = True - if sub.ShapeType =='Edge': - edge = sub - edgelist.append(edge) - edges = True - if isinstance(sub.Curve,Part.Circle): - circlelist.append(edge) - circles = True - if sub.ShapeType =='Vertex': - ptlist.append(sub) - points = True - - for sub in s.SubElementNames: - if 'Face' in sub: - facename = sub - facenames.append(facename) - if 'Edge' in sub: - edgenames.append(sub) - # now indicate which wire is going to be processed, based on which edges are selected - if facelist: - selItems['facelist']=facelist - - if edges: - if face: - selItems['edgelist'] =edgelist - for fw in facelist[0].Wires: - for e in fw.Edges: - if e.isSame(edge): - pathwire = fw - selItems['pathwire'] =pathwire - elif wireobj: - selItems['pathwire'] =s.Object.Shape - selItems['edgelist'] =edgelist - else: - for w in s.Object.Shape.Wires: - for e in w.Edges: - if e.BoundBox.ZMax == e.BoundBox.ZMin: #if they are on same plane in Z as sel edge - if e.isSame(edge): - pathwire = w - selItems['pathwire'] =pathwire - selItems['edgelist'] =edgelist - - if not edges: - if face: - selItems['pathwire'] =facelist[0].OuterWire - - if edges and (len(edgelist)>=2): - vlist,edgestart,edgecommon=Sort2Edges(edgelist) - edgepts ={} - edgepts['vlist'] = vlist - edgepts['edgestart']=edgestart # start point of edges selected - edgepts['edgecommon']=edgecommon # point where two edges join- will be last point in in first gcode line - selItems['edgepts']=edgepts - - if check_clockwise(vlist): - selItems['clockwise']=True - elif check_clockwise(vlist) == False: - selItems['clockwise']=False - - if points: - selItems['pointlist'] = ptlist - selItems['pointnames'] = ptnames - if edges: - selItems['edgenames']=edgenames - if face: - selItems['facenames'] = facenames - if circles: - selItems['circles'] = circlelist - - return selItems diff --git a/src/Mod/Path/PathScripts/PathSurface.py b/src/Mod/Path/PathScripts/PathSurface.py new file mode 100644 index 0000000000..ac85ee72a0 --- /dev/null +++ b/src/Mod/Path/PathScripts/PathSurface.py @@ -0,0 +1,237 @@ +# -*- coding: utf-8 -*- + +#*************************************************************************** +#* * +#* Copyright (c) 2016 sliptonic * +#* * +#* This program is free software; you can redistribute it and/or modify * +#* it under the terms of the GNU Lesser General Public License (LGPL) * +#* as published by the Free Software Foundation; either version 2 of * +#* the License, or (at your option) any later version. * +#* for detail see the LICENCE text file. * +#* * +#* This program is distributed in the hope that it will be useful, * +#* but WITHOUT ANY WARRANTY; without even the implied warranty of * +#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +#* GNU Library General Public License for more details. * +#* * +#* You should have received a copy of the GNU Library General Public * +#* License along with this program; if not, write to the Free Software * +#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +#* USA * +#* * +#*************************************************************************** + +import FreeCAD,FreeCADGui,Path,PathGui +from FreeCAD import Vector +from PySide import QtCore,QtGui +from PathScripts import PathUtils,PathSelection,PathProject + +"""Path Surface object and FreeCAD command""" +'''Surface operation is used for 3D sculpting, roughing, and finishing +Possible algorithms include waterline, zigzag, and adaptive roughing. +Libraries to consider: opencamlib and libactp''' + +# Qt tanslation handling +try: + _encoding = QtGui.QApplication.UnicodeUTF8 + def translate(context, text, disambig=None): + return QtGui.QApplication.translate(context, text, disambig, _encoding) +except AttributeError: + def translate(context, text, disambig=None): + return QtGui.QApplication.translate(context, text, disambig) + + +class ObjectProfile: + + def __init__(self,obj): + obj.addProperty("App::PropertyLinkSub","Base","Path",translate("Parent Object","The base geometry of this toolpath")) + obj.addProperty("App::PropertyBool","Active","Path",translate("Active","Make False, to prevent operation from generating code")) + obj.addProperty("App::PropertyString","Comment","Path",translate("Comment","An optional comment for this profile")) + + obj.addProperty("App::PropertyEnumeration", "Algorithm", "Algorithm",translate("PathProject", "The library to use to generate the path")) + obj.Algorithm = ['OCL Waterline'] + + obj.addProperty("App::PropertyIntegerConstraint","ToolNumber","Tool",translate("PathProfile","The tool number in use")) + obj.ToolNumber = (0,0,1000,1) + obj.setEditorMode('ToolNumber',1) #make this read only + + #Depth Properties + obj.addProperty("App::PropertyDistance", "ClearanceHeight", "Depth", translate("Clearance Height","The height needed to clear clamps and obstructions")) + obj.addProperty("App::PropertyDistance", "SafeHeight", "Depth", translate("PathProject","Rapid Safety Height between locations.")) + obj.addProperty("App::PropertyFloatConstraint", "StepDown", "Depth", translate("StepDown","Incremental Step Down of Tool")) + obj.StepDown = (1,0.01,1000,0.5) + obj.addProperty("App::PropertyDistance", "StartDepth", "Depth", translate("Start Depth","Starting Depth of Tool- first cut depth in Z")) + obj.addProperty("App::PropertyDistance", "FinalDepth", "Depth", translate("Final Depth","Final Depth of Tool- lowest value in Z")) + + #Feed Properties + obj.addProperty("App::PropertySpeed", "VertFeed", "Feed",translate("Vert Feed","Feed rate for vertical moves in Z")) + obj.addProperty("App::PropertySpeed", "HorizFeed", "Feed",translate("Horiz Feed","Feed rate for horizontal moves")) + + #Start Point Properties + obj.addProperty("App::PropertyVector","StartPoint","Start Point",translate("Start Point","The start point of this path")) + obj.addProperty("App::PropertyBool","UseStartPoint","Start Point",translate("Use Start Point","make True, if specifying a Start Point")) + obj.addProperty("App::PropertyLength", "ExtendAtStart", "Start Point", translate("extend at start", "extra length of tool path before start of part edge")) + obj.addProperty("App::PropertyLength", "LeadInLineLen", "Start Point", translate("lead in length","length of straight segment of toolpath that comes in at angle to first part edge")) + + #End Point Properties + obj.addProperty("App::PropertyBool","UseEndPoint","End Point",translate("Use End Point","make True, if specifying an End Point")) + obj.addProperty("App::PropertyLength", "ExtendAtEnd", "End Point", translate("extend at end","extra length of tool path after end of part edge")) + obj.addProperty("App::PropertyLength", "LeadOutLineLen", "End Point", translate("lead_out_line_len","length of straight segment of toolpath that comes in at angle to last part edge")) + obj.addProperty("App::PropertyVector","EndPoint","End Point",translate("End Point","The end point of this path")) + + #Surface Properties + + obj.Proxy = self + + def __getstate__(self): + return None + + def __setstate__(self,state): + return None + + def execute(self,obj): + import Part, DraftGeomUtils + from PathScripts.PathUtils import depth_params + + if obj.Base: + # tie the toolnumber to the PathLoadTool object ToolNumber + if len(obj.InList)>0: #check to see if obj is in the Project group yet + project = obj.InList[0] + tl = int(PathUtils.changeTool(obj,project)) + obj.ToolNumber= tl + + tool = PathUtils.getTool(obj,obj.ToolNumber) + if tool: + radius = tool.Diameter/2 + else: + # temporary value,in case we don't have any tools defined already + radius = 0.25 + + depthparams = depth_params (obj.ClearanceHeight.Value, obj.SafeHeight.Value, obj.StartDepth.Value, obj.StepDown, 0.0, obj.FinalDepth.Value, None) + + clearance = obj.ClearanceHeight.Value + step_down=obj.StepDown + start_depth=obj.StartDepth.Value + final_depth=obj.FinalDepth.Value + rapid_safety_space=obj.SafeHeight.Value + + vf=obj.VertFeed.Value + hf=obj.HorizFeed.Value + + '''Parse the base and get the necessary geometry here''' + + + if obj.Algorithm == "OCL Waterline": + try: + import ocl + except: + FreeCAD.Console.PrintError(translate("Path","OpenCAMLib needs to be installed for this command to work.\n")) + return + + output = "" + output += '('+ str(obj.Comment)+')\n' + + output += self.buildoclwaterline() + + else: + return + + + + if obj.Active: + path = Path.Path(output) + obj.Path = path + obj.ViewObject.Visibility = True + + else: + path = Path.Path("(inactive operation)") + obj.Path = path + obj.ViewObject.Visibility = False + + def buildoclwaterline(self): + output = "Some text" + return output + +class ViewProviderProfile: + + def __init__(self,vobj): + vobj.Proxy = self + + def attach(self,vobj): + self.Object = vobj.Object + return + + def getIcon(self): + return ":/icons/Path-Surface.svg" + + def __getstate__(self): + return None + + def __setstate__(self,state): + return None + + +class CommandPathProfile: + def GetResources(self): + return {'Pixmap' : 'Path-Surface', + 'MenuText': QtCore.QT_TRANSLATE_NOOP("PathSurface","Surface"), + 'Accel': "P, P", + 'ToolTip': QtCore.QT_TRANSLATE_NOOP("PathSurface","Creates a Path Surface object from selected solid")} + + def IsActive(self): + return not FreeCAD.ActiveDocument is None + + def Activated(self): + import Path + from PathScripts import PathProject, PathUtils + + # check that the selection contains exactly what we want + selection = FreeCADGui.Selection.getSelectionEx() + if len(selection) != 1: + FreeCAD.Console.PrintError(translate("Path","Select a Solid.\n")) + return + if len(selection[0].SubObjects) == 0: + FreeCAD.Console.PrintError(translate("Path","Select a Solid.\n")) + return + + # if everything is ok, execute and register the transaction in the undo/redo stack + + # Take a guess at some reasonable values for Finish depth. + bb = selection[0].Object.Shape.BoundBox #parent boundbox + fbb = selection[0].SubObjects[0].BoundBox #feature boundbox + if fbb.ZMax < bb.ZMax: + zbottom = fbb.ZMax + else: + zbottom = bb.ZMin + + FreeCAD.ActiveDocument.openTransaction(translate("Path","Create a Surfacing Operation")) + FreeCADGui.addModule("PathScripts.PathSurface") + FreeCADGui.doCommand('obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython","Surface")') + FreeCADGui.doCommand('PathScripts.PathSurface.ObjectProfile(obj)') + + FreeCADGui.doCommand('obj.Active = True') + # subs ="[" + # for s in selection[0].SubElementNames: + # subs += '"' + s + '",' + # subs += "]" + # FreeCADGui.doCommand('obj.Base = (FreeCAD.ActiveDocument.' + selection[0].ObjectName + ',' + subs + ')') + + FreeCADGui.doCommand('obj.ViewObject.Proxy = 0') + FreeCADGui.doCommand('obj.ClearanceHeight = ' + str(bb.ZMax + 10.0)) + FreeCADGui.doCommand('obj.StepDown = 1.0') + FreeCADGui.doCommand('obj.StartDepth= ' + str(bb.ZMax)) + FreeCADGui.doCommand('obj.FinalDepth=' + str(zbottom)) + + FreeCADGui.doCommand('obj.SafeHeight = '+ str(bb.ZMax + 2.0)) + FreeCADGui.doCommand('PathScripts.PathUtils.addToProject(obj)') + + FreeCAD.ActiveDocument.commitTransaction() + FreeCAD.ActiveDocument.recompute() + + +if FreeCAD.GuiUp: + # register the FreeCAD command + FreeCADGui.addCommand('Path_Surface',CommandPathSurface()) + +FreeCAD.Console.PrintLog("Loading PathSurface... done\n") diff --git a/src/Mod/Path/PathScripts/PathUtils.py b/src/Mod/Path/PathScripts/PathUtils.py index 0e5464da35..1a7caf058e 100644 --- a/src/Mod/Path/PathScripts/PathUtils.py +++ b/src/Mod/Path/PathScripts/PathUtils.py @@ -53,8 +53,9 @@ def cleanedges(splines,precision): edges = curvetowire(spline, 1.0) #fixme hardcoded value elif geomType(spline)=="Circle": - #arcs=filterArcs(spline) - edges.append(spline) + arcs=filterArcs(spline) + for i in arcs: + edges.append(Part.Edge(i)) elif geomType(spline)=="Line": edges.append(spline) @@ -285,7 +286,8 @@ def MakePath(wire,Side,radius,clockwise,ZClearance,StepDown,ZStart,ZFinalDepth,f ''' makes the path - just a simple profile for now ''' offset = SortPath(wire,Side,radius,clockwise,firstedge,SegLen=0.5) toolpath = offset.Edges[:] - paths = "" + paths = "" + paths += "G0 Z" + str(ZClearance)+"\n" first = toolpath[0].Vertexes[0].Point paths += "G0 X"+str(fmt(first.x))+"Y"+str(fmt(first.y))+"\n" ZCurrent = ZStart- StepDown @@ -382,3 +384,79 @@ def getLastZ(obj): lastZ= c.Parameters['Z'] return lastZ +def frange(start, stop, step, finish): + x = [] + curdepth = start + if step == 0: + return x + # do the base cuts until finishing round + while curdepth >= stop + step + finish: + curdepth = curdepth - step + if curdepth <= stop + finish: + curdepth = stop + finish + x.append(curdepth) + + # we might have to do a last pass or else finish round might be too far away + if curdepth - stop > finish: + x.append(stop + finish) + + # do the the finishing round + if curdepth >= stop: + curdepth = stop + x.append(curdepth) + + # Why this? +# if start >= stop: +# start = stop +# x.append (start) + + return x + + + +class depth_params: + def __init__(self, clearance_height, rapid_safety_space, start_depth, step_down, z_finish_depth, final_depth, user_depths=None): + self.clearance_height = clearance_height + self.rapid_safety_space = math.fabs(rapid_safety_space) + self.start_depth = start_depth + self.step_down = math.fabs(step_down) + self.z_finish_depth = math.fabs(z_finish_depth) + self.final_depth = final_depth + self.user_depths = user_depths + + def get_depths(self): + depths = [] + if self.user_depths != None: + depths = self.user_depths + else: + depth = self.final_depth + depths = [depth] + depth += self.z_finish_depth + if depth + 0.0000001 < self.start_depth: + if self.z_finish_depth > 0.0000001: depths.insert(0, depth) + layer_count = int((self.start_depth - depth) / self.step_down - 0.0000001) + 1 + if layer_count > 0: + layer_depth = (self.start_depth - depth)/layer_count + for i in range(1, layer_count): + depth += layer_depth + depths.append(depth) + return depths + +# def get_depths(start_depth, final_depth, step_down, z_finish_depth): +# '''get_depths returns a list of z heights for pocket clearing. First value is Z depth of the first pass, etc. +# start_depth: starting depth of pocket +# step_down: max amount removed per pocket pass +# z_finish_depth: amount to remove on last (finishing) pass +# final_depth: bottom of pocket''' +# depths = [depth] +# depth += z_finish_depth +# if depth + 0.0000001 < start_depth: +# if z_finish_depth > 0.0000001: depths.insert(0, depth) +# layer_count = int((start_depth - depth) / step_down - 0.0000001) + 1 +# if layer_count > 0: +# layer_depth = (start_depth - depth)/layer_count +# for i in range(1, layer_count): +# depth += layer_depth +# depths.insert(0, depth) + +# return depths diff --git a/src/Mod/Path/PathScripts/depth_params.py b/src/Mod/Path/PathScripts/depth_params.py new file mode 100644 index 0000000000..918c66757c --- /dev/null +++ b/src/Mod/Path/PathScripts/depth_params.py @@ -0,0 +1,31 @@ +import math + +class depth_params: + def __init__(self, clearance_height, rapid_safety_space, start_depth, step_down, z_finish_depth, z_thru_depth, final_depth, user_depths): + self.clearance_height = clearance_height + self.rapid_safety_space = math.fabs(rapid_safety_space) + self.start_depth = start_depth + self.step_down = math.fabs(step_down) + self.z_finish_depth = math.fabs(z_finish_depth) + self.z_thru_depth = math.fabs(z_thru_depth) + self.final_depth = final_depth + self.user_depths = user_depths + + def get_depths(self): + if self.user_depths != None: + depths = self.user_depths + else: + depth = self.final_depth - self.z_thru_depth + depths = [depth] + depth += self.z_finish_depth + if depth + 0.0000001 < self.start_depth: + if self.z_finish_depth > 0.0000001: depths.insert(0, depth) + depth += self.z_thru_depth + layer_count = int((self.start_depth - depth) / self.step_down - 0.0000001) + 1 + if layer_count > 0: + layer_depth = (self.start_depth - depth)/layer_count + for i in range(1, layer_count): + depth += layer_depth + depths.insert(0, depth) + + return depths diff --git a/src/Mod/Path/PathScripts/nc/DeckelFP4Ma.py b/src/Mod/Path/PathScripts/nc/DeckelFP4Ma.py new file mode 100644 index 0000000000..70d840cb70 --- /dev/null +++ b/src/Mod/Path/PathScripts/nc/DeckelFP4Ma.py @@ -0,0 +1,30 @@ +import nc +import iso +import math +import datetime +import time +from format import Format + +now = datetime.datetime.now() + +class Creator(iso.Creator): + def __init__(self): + iso.Creator.__init__(self) + self.output_tool_definitions = False + self.fmt = Format(dp_wanted = False, add_trailing_zeros = True, add_plus = True) + + def SPACE_STR(self): return ' ' + def PROGRAM(self): return None + def PROGRAM_END(self): return( 'T0' + self.SPACE() + 'M06' + self.SPACE() + 'M02') + +############################################################################ +## Begin Program + + + def program_begin(self, id, comment): + self.write( ('(Created with Deckel FP4Ma post processor ' + str(now.strftime("%Y/%m/%d %H:%M")) + ')' + '\n') ) + iso.Creator.program_begin(self, id, comment) + + +nc.creator = Creator() + diff --git a/src/Mod/Path/PathScripts/nc/__init__.py b/src/Mod/Path/PathScripts/nc/__init__.py new file mode 100644 index 0000000000..8d1730026a --- /dev/null +++ b/src/Mod/Path/PathScripts/nc/__init__.py @@ -0,0 +1,6 @@ +################################################################################ +# __init__.py +# +# This is here to make python see NC folder +# +# Hirutso Enni, 2009-01-13 diff --git a/src/Mod/Path/PathScripts/nc/anilam_crusader_m.py b/src/Mod/Path/PathScripts/nc/anilam_crusader_m.py new file mode 100644 index 0000000000..65afac5743 --- /dev/null +++ b/src/Mod/Path/PathScripts/nc/anilam_crusader_m.py @@ -0,0 +1,111 @@ +# Preliminary postprocessor support for Anilam Crusader M CNC controller +# This code modified from iso.py and emc2.py distriuted with HeeksCAD as of Sep 2010 +# Kurt Jensen 6 Sep 2010 +# Use at your own risk. +import nc +import iso + +class Creator(iso.Creator): + def init(self): + iso.Creator.init(self) + self.arc_centre_absolute = True + + def SPACE(self): return(' ') + + # This version of COMMENT removes comments from the resultant GCode + # Note: The Anilam hates comments when importing code. + + def COMMENT(self,comment): return('') + + def program_begin(self, id, comment): + self.write('%\n'); # Start of file token that Anilam Crusader M likes + # No Comments for the Anilam crusaher M, please...... + #self.write( ('(' + comment + ')' + '\n') ) + + def program_end(self): + self.write_blocknum() + self.write('G29E\n') # End of code signal for Anilam Crusader M + self.write('%\n') # EOF signal for Anilam Crusader M + + ############################################################################ + ## Settings + + def imperial(self): + self.write_blocknum() + self.write( self.IMPERIAL() + '\n') + self.fmt.number_of_decimal_places = 4 + + def metric(self): + self.write_blocknum() + self.write( self.METRIC() + '\n' ) + self.fmt.number_of_decimal_places = 3 + + def absolute(self): + self.write_blocknum() + self.write( self.ABSOLUTE() + '\n') + + def incremental(self): + self.write_blocknum() + self.write( self.INCREMENTAL() + '\n' ) + + def polar(self, on=True): + if (on) : + self.write_blocknum() + self.write(self.POLAR_ON() + '\n' ) + else : + self.write_blocknum() + self.write(self.POLAR_OFF() + '\n' ) + + def set_plane(self, plane): + if (plane == 0) : + self.write_blocknum() + self.write('G17\n') + elif (plane == 1) : + self.write_blocknum() + self.write('G18\n') + elif (plane == 2) : + self.write_blocknum() + self.write('G19\n') + + def comment(self, text): + self.write_blocknum() + + ############################################################################ + ## Tools + + def tool_change(self, id): + self.write_blocknum() + self.write(('T%i' % id) + '\n') + self.t = id + + def tool_defn(self, id, name='', params=None): + self.write_blocknum() + self.write(('T10%.2d' % id) + ' ') + + if (radius != None): + self.write(('X%.3f' % radius) + ' ') + + if (length != None): + self.write('Z%.3f' % length) + + self.write('\n') + + # This is the coordinate system we're using. G54->G59, G59.1, G59.2, G59.3 + # These are selected by values from 1 to 9 inclusive. + def workplane(self, id): + if ((id >= 1) and (id <= 6)): + self.write_blocknum() + self.write( (self.WORKPLANE() % (id + self.WORKPLANE_BASE())) + '\n') + if ((id >= 7) and (id <= 9)): + self.write_blocknum() + self.write( ((self.WORKPLANE() % (6 + self.WORKPLANE_BASE())) + ('.%i' % (id - 6))) + '\n') + + # inhibit N codes being generated for line numbers: + def write_blocknum(self): + pass + + def drill(self, x=None, y=None, dwell=None, depthparams = None, retract_mode=None, spindle_mode=None, internal_coolant_on=None, rapid_to_clearance = None): + self.write('(Canned drill cycle ops are not yet supported here on this Anilam Crusader M postprocessor)') + +nc.creator = Creator() + diff --git a/src/Mod/Path/PathScripts/nc/anilam_crusader_m_read.py b/src/Mod/Path/PathScripts/nc/anilam_crusader_m_read.py new file mode 100644 index 0000000000..c29969fa5b --- /dev/null +++ b/src/Mod/Path/PathScripts/nc/anilam_crusader_m_read.py @@ -0,0 +1,13 @@ +# Preliminary backplot support for Anilam Crusader M CNC controller +# This code modified from iso_read.py and emc2_read.py distriuted with HeeksCAD as of Sep 2010 +# Kurt Jensen 6 Sep 2010 +# Use at your own risk. +import iso_read as iso +import sys + +# Override some iso parser methods to interpret arc centers as relative to origin, not relative to start of arc. + +class Parser(iso.Parser): + def __init__(self, writer): + iso.Parser.__init__(self, writer) + self.arc_centre_absolute = True diff --git a/src/Mod/Path/PathScripts/nc/attach.py b/src/Mod/Path/PathScripts/nc/attach.py new file mode 100644 index 0000000000..d6636438b9 --- /dev/null +++ b/src/Mod/Path/PathScripts/nc/attach.py @@ -0,0 +1,134 @@ +################################################################################ +# attach.py +# +# NC code creator for attaching Z coordinates to a surface +# + +import recreator +import ocl +import ocl_funcs +import nc + +attached = False +units = 1.0 + +################################################################################ +class Creator(recreator.Redirector): + + def __init__(self, original): + recreator.Redirector.__init__(self, original) + + self.stl = None + self.cutter = None + self.minz = None + self.path = None + self.pdcf = None + self.material_allowance = 0.0 + + ############################################################################ + ## Shift in Z + + def setPdcfIfNotSet(self): + if self.pdcf == None: + self.pdcf = ocl.PathDropCutter() + self.pdcf.setSTL(self.stl) + self.pdcf.setCutter(self.cutter) + self.pdcf.setSampling(0.1) + self.pdcf.setZ(self.minz/units) + + def z2(self, z): + path = ocl.Path() + # use a line with no length + path.append(ocl.Line(ocl.Point(self.x, self.y, self.z), ocl.Point(self.x, self.y, self.z))) + self.setPdcfIfNotSet() + if (self.z>self.minz): + self.pdcf.setZ(self.z) # Adjust Z if we have gotten a higher limit (Fix pocketing loosing steps when using attach?) + else: + self.pdcf.setZ(self.minz/units) # Else use minz + self.pdcf.setPath(path) + self.pdcf.run() + plist = self.pdcf.getCLPoints() + p = plist[0] + return p.z + self.material_allowance/units + + def cut_path(self): + if self.path == None: return + self.setPdcfIfNotSet() + + if (self.z>self.minz): + self.pdcf.setZ(self.z) # Adjust Z if we have gotten a higher limit (Fix pocketing loosing steps when using attach?) + else: + self.pdcf.setZ(self.minz/units) # Else use minz + + # get the points on the surface + self.pdcf.setPath(self.path) + + self.pdcf.run() + plist = self.pdcf.getCLPoints() + + #refine the points + f = ocl.LineCLFilter() + f.setTolerance(0.005) + for p in plist: + f.addCLPoint(p) + f.run() + plist = f.getCLPoints() + + i = 0 + for p in plist: + if i > 0: + self.original.feed(p.x/units, p.y/units, p.z/units + self.material_allowance/units) + i = i + 1 + + self.path = ocl.Path() + + def rapid(self, x=None, y=None, z=None, a=None, b=None, c=None ): + if z != None: + if z < self.z: + return + recreator.Redirector.rapid(self, x, y, z, a, b, c) + + def feed(self, x=None, y=None, z=None, a=None, b=None, c=None): + px = self.x + py = self.y + pz = self.z + recreator.Redirector.feed(self, x, y, z, a, b, c) + if self.x == None or self.y == None or self.z == None: + return + if px == self.x and py == self.y: + return + + # add a line to the path + if self.path == None: self.path = ocl.Path() + self.path.append(ocl.Line(ocl.Point(px, py, pz), ocl.Point(self.x, self.y, self.z))) + + def arc(self, x=None, y=None, z=None, i=None, j=None, k=None, r=None, ccw = True): + px = self.x + py = self.y + pz = self.z + recreator.Redirector.arc(self, x, y, z, i, j, k, r, ccw) + + # add an arc to the path + if self.path == None: self.path = ocl.Path() + self.path.append(ocl.Arc(ocl.Point(px, py, pz), ocl.Point(self.x, self.y, self.z), ocl.Point(i, j, pz), ccw)) + + def set_ocl_cutter(self, cutter): + self.cutter = cutter + +################################################################################ + +def attach_begin(): + global attached + if attached == True: + attach_end() + nc.creator = Creator(nc.creator) + recreator.units = units + attached = True + nc.creator.pdcf = None + nc.creator.path = None + +def attach_end(): + global attached + nc.creator.cut_path() + nc.creator = nc.creator.original + attached = False diff --git a/src/Mod/Path/PathScripts/nc/cad_iso_read.py b/src/Mod/Path/PathScripts/nc/cad_iso_read.py new file mode 100644 index 0000000000..5894147873 --- /dev/null +++ b/src/Mod/Path/PathScripts/nc/cad_iso_read.py @@ -0,0 +1,229 @@ +################################################################################ +# iso_read.py +# +# Simple ISO NC code parsing +# +# Hirutso Enni, 2009-01-13 + +""" use this script to backplot nc files to *.scr file for autocad,bricscad, + draftsight,progecad,ares commander, etc.... + usage: python cad_iso_read.py temp.nc temp.scr +""" + + +import cad_nc_read as nc +import re +import sys + +################################################################################ +class Parser(nc.Parser): + + + + def __init__(self, writer): + nc.Parser.__init__(self, writer) + + self.pattern_main = re.compile('([(!;].*|\s+|[a-zA-Z0-9_:](?:[+-])?\d*(?:\.\d*)?|\w\#\d+|\(.*?\)|\#\d+\=(?:[+-])?\d*(?:\.\d*)?)') + + #if ( or ! or ; at least one space or a letter followed by some character or not followed by a +/- followed by decimal, with a possible decimal point + # followed by a possible deimcal, or a letter followed by # with a decimal . deimcal + # add your character here > [(!;] for comments char + # then look for the 'comment' function towards the end of the file and add another elif + + def ParseWord(self, word): + if (word[0] == 'A' or word[0] == 'a'): + self.col = "axis" + self.a = eval(word[1:]) + self.move = True + elif (word[0] == 'B' or word[0] == 'b'): + self.col = "axis" + self.b = eval(word[1:]) + self.move = True + elif (word[0] == 'C' or word[0] == 'c'): + self.col = "axis" + self.c = eval(word[1:]) + self.move = True + elif (word[0] == 'F' or word[0] == 'f'): + self.col = "axis" + self.f = eval(word[1:]) + self.move = True + elif (word == 'G0' or word == 'G00' or word == 'g0' or word == 'g00'): + self.path_col = "rapid" + self.col = "rapid" + self.arc = 0 + elif (word == 'G1' or word == 'G01' or word == 'g1' or word == 'g01'): + self.path_col = "feed" + self.col = "feed" + self.arc = 0 + elif (word == 'G2' or word == 'G02' or word == 'g2' or word == 'g02' or word == 'G12' or word == 'g12'): + self.path_col = "feed" + self.col = "feed" + self.arc = -1 + elif (word == 'G3' or word == 'G03' or word == 'g3' or word == 'g03' or word == 'G13' or word == 'g13'): + self.path_col = "feed" + self.col = "feed" + self.arc = +1 + elif (word == 'G10' or word == 'g10'): + self.no_move = True + elif (word == 'L1' or word == 'l1'): + self.no_move = True + elif (word == 'G61.1' or word == 'g61.1' or word == 'G61' or word == 'g61' or word == 'G64' or word == 'g64'): + self.no_move = True + elif (word == 'G20' or word == 'G70'): + self.col = "prep" + self.set_mode(units=25.4) + elif (word == 'G21' or word == 'G71'): + self.col = "prep" + self.set_mode(units=1.0) + elif (word == 'G81' or word == 'g81'): + self.drill = True + self.no_move = True + self.path_col = "feed" + self.col = "feed" + elif (word == 'G82' or word == 'g82'): + self.drill = True; + self.no_move = True + self.path_col = "feed" + self.col = "feed" + elif (word == 'G83' or word == 'g83'): + self.drill = True + self.no_move = True + self.path_col = "feed" + self.col = "feed" + elif (word == 'G90' or word == 'g90'): + self.absolute() + elif (word == 'G91' or word == 'g91'): + self.incremental() + elif (word[0] == 'G') : col = "prep" + elif (word[0] == 'I' or word[0] == 'i'): + self.col = "axis" + self.i = eval(word[1:]) + self.move = True + elif (word[0] == 'J' or word[0] == 'j'): + self.col = "axis" + self.j = eval(word[1:]) + self.move = True + elif (word[0] == 'K' or word[0] == 'k'): + self.col = "axis" + self.k = eval(word[1:]) + self.move = True + elif (word[0] == 'M') : self.col = "misc" + elif (word[0] == 'N') : self.col = "blocknum" + elif (word[0] == 'O') : self.col = "program" + elif (word[0] == 'P' or word[0] == 'p'): + if (self.no_move != True): + self.col = "axis" + self.p = eval(word[1:]) + self.move = True + elif (word[0] == 'Q' or word[0] == 'q'): + if (self.no_move != True): + self.col = "axis" + self.q = eval(word[1:]) + self.move = True + elif (word[0] == 'R' or word[0] == 'r'): + self.col = "axis" + self.r = eval(word[1:]) + self.move = True + elif (word[0] == 'S' or word[0] == 's'): + self.col = "axis" + self.s = eval(word[1:]) + self.move = True + elif (word[0] == 'T') : + self.col = "tool" + self.set_tool( eval(word[1:]) ) + elif (word[0] == 'X' or word[0] == 'x'): + self.col = "axis" + self.x = eval(word[1:]) + self.move = True + elif (word[0] == 'Y' or word[0] == 'y'): + self.col = "axis" + self.y = eval(word[1:]) + self.move = True + elif (word[0] == 'Z' or word[0] == 'z'): + self.col = "axis" + self.z = eval(word[1:]) + self.move = True + elif (word[0] == '(') : (self.col, self.cdata) = ("comment", True) + elif (word[0] == '!') : (self.col, self.cdata) = ("comment", True) + elif (word[0] == ';') : (self.col, self.cdata) = ("comment", True) + elif (word[0] == '#') : self.col = "variable" + elif (word[0] == ':') : self.col = "blocknum" + elif (ord(word[0]) <= 32) : self.cdata = True + + def Parse(self, name, oname=None): + self.files_open(name,oname) + + #self.begin_ncblock() + #self.begin_path(None) + #self.add_line(z=500) + #self.end_path() + #self.end_ncblock() + + self.path_col = None + self.f = None + self.arc = 0 + + while (self.readline()): + + self.a = None + self.b = None + self.c = None + self.i = None + self.j = None + self.k = None + self.p = None + self.q = None + self.r = None + self.s = None + self.x = None + self.y = None + self.z = None + + #self.begin_ncblock() + + self.move = False + self.drill = False + self.no_move = False + + words = self.pattern_main.findall(self.line) + for word in words: + self.col = None + self.cdata = False + self.ParseWord(word) + self.add_text(word, self.col, self.cdata) + + if (self.drill): + self.begin_path("rapid") + self.add_line(self.x, self.y, self.r) + self.end_path() + + self.begin_path("feed") + self.add_line(self.x, self.y, self.z) + self.end_path() + + self.begin_path("feed") + self.add_line(self.x, self.y, self.r) + self.end_path() + else: + if (self.move and not self.no_move): + self.begin_path(self.path_col) + if (self.arc==-1): + self.add_arc(self.x, self.y, self.z, self.i, self.j, self.k, self.r, self.arc) + elif (self.arc==1): + #self.add_arc(x, y, z, i, j, k, -r, arc) #if you want to use arcs with R values uncomment the first part of this line and comment the next one + self.add_arc(self.x, self.y, self.z, self.i, self.j, self.k, self.r, self.arc) + else : self.add_line(self.x, self.y, self.z, self.a, self.b, self.c) + self.end_path() + + self.end_ncblock() + + self.files_close() + +################################################################################ + +if __name__ == '__main__': + parser = ParserIso() + if len(sys.argv)>2: + parser.Parse(sys.argv[1],sys.argv[2]) + else: + parser.Parse(sys.argv[1]) diff --git a/src/Mod/Path/PathScripts/nc/cad_nc_read.py b/src/Mod/Path/PathScripts/nc/cad_nc_read.py new file mode 100644 index 0000000000..af6a50aa76 --- /dev/null +++ b/src/Mod/Path/PathScripts/nc/cad_nc_read.py @@ -0,0 +1,113 @@ +################################################################################ +# cad_nc_read.py +# +# Base class for NC code parsing and backplotting +# +# Dan Falck 2011/01/06 + +################################################################################ +class Parser: + + def __init__(self): + self.currentx = -1.0 + self.currenty = 0.0 + self.currentz = 0.0 + x,y,z = 0.0,0.0,0.0 + self.absolute_flag = True + + ############################################################################ + ## Internals + + def files_open(self, name, oname=None): + if (oname == None ): + oname = (name+'.scr') + self.file_in = open(name, 'r') + self.file_out = open(oname, 'w') + + def files_close(self): + self.file_in.close() + self.file_out.write('-linetype set continuous\n') + self.file_out.write('\n') + self.file_out.close() + + def readline(self): + self.line = self.file_in.readline().rstrip() + if (len(self.line)): + return True + else: + return False + + def write(self, s): + self.file_out.write(s) + + ############################################################################ + def end_ncblock(self): + self.file_out.write('Delay 0\n') + + def add_text(self, s, col=None, cdata=False): + return + + def set_mode(self, units=None): + #self.file_out.write(' units="'+str(units)+'"') + return + + def set_tool(self, number=None): + if (number != None): + self.file_out.write('-LAYER New T'+str(number)+'\n') + self.file_out.write('-LAYER Set T'+str(number)+'\n') + + def begin_path(self, col=None): + if (col != None): + if col == 'rapid': + self.file_out.write('-color Red\n') + #self.file_out.write('') + self.file_out.write('-linetype set dashed\n') + self.file_out.write('\n') + else: + self.file_out.write('-color Green\n') + #self.file_out.write('') + self.file_out.write('-linetype set continuous\n') + self.file_out.write('\n') + else : self.file_out.write('\n') + + def end_path(self): + self.file_out.write('\n') + + def add_line(self, x=None, y=None, z=None, a=None, b=None, c=None): + if (x == None and y == None and z == None and a == None and b == None and c == None) : return + #self.file_out.write('line %s,%s %s,%s' %(self.currentx,self.currenty,x,y)) + if (x == None) : x = self.currentx + if (y == None) : y = self.currenty + if (z == None) : z = self.currentz + self.file_out.write('line %s,%s,%s %s,%s,%s\n' %(self.currentx,self.currenty,self.currentz,x,y,z)) + self.currentx = x + self.currenty = y + self.currentz = z + + def add_arc(self, x=None, y=None, z=None, i=None, j=None, k=None, r=None, d=None): + if (x == None and y == None and z == None and i == None and j == None and k == None and r == None and d == None) : return + + z = self.currentz + if (x == None) : x = self.currentx + if (y == None) : y = self.currenty + if (z == None) : z = self.currentz + if (d == 1): + self.file_out.write('arc %s,%s,%s\n' %(self.currentx,self.currenty,self.currentz)) + self.file_out.write('c\n') + self.file_out.write('%s,%s,%s\n' %(self.currentx+i,self.currenty+j,self.currentz)) + self.file_out.write('%s,%s,%s' %(x,y,z)) + else: + self.file_out.write('arc %s,%s,%s\n' %(x,y,z)) + self.file_out.write('c\n') + self.file_out.write('%s,%s,%s\n' %(self.currentx+i,self.currenty+j,self.currentz)) + self.file_out.write('%s,%s,%s' %(self.currentx,self.currenty,self.currentz)) + self.currentx = x + self.currenty = y + self.currentz = z + + + def incremental(self): + self.absolute_flag = False + + def absolute(self): + self.absolute_flag = True diff --git a/src/Mod/Path/PathScripts/nc/cad_read.py b/src/Mod/Path/PathScripts/nc/cad_read.py new file mode 100644 index 0000000000..a8cdc4435c --- /dev/null +++ b/src/Mod/Path/PathScripts/nc/cad_read.py @@ -0,0 +1,275 @@ +# Preliminary backplot support for autocad clone applications +# This code modified from iso_read.py and emc2_read.py distriuted with HeeksCAD as of Sep 2010 +# Dan Falck 2011/01/06 +# + +""" use this script to backplot nc files to *.scr file for autocad,bricscad, + draftsight,progecad,ares commander, etc.... + usage: python cad_read.py temp.nc temp.scr +""" + +import cad_iso_read as iso +import sys + + + +# Override some iso parser methods to interpret arc centers as relative to origin, not relative to start of arc. + +#def write_layer(name,number): + #FILE.write('-LAYER New %s%s \n' %(name,number)) + #FILE.write('-LAYER Set %s%s \n' %(name,number)) + + +class CAD_backplot(iso.Parser): + + def __init__(self): + iso.Parser.__init__(self) + + def Parse(self, name, oname=None): + self.files_open(name,oname) + + #self.begin_ncblock() + #self.begin_path(None) + #self.add_line(z=500) + #self.end_path() + #self.end_ncblock() + + path_col = None + f = None + arc = 0 + + # Storage for tool position history of last block processed to properly convert absolute arc centers + + oldx = -1.0 + oldy = 0.0 + oldz = 0.0 + movelist = [] + while (self.readline()): + # self.readline returns false if the line is empty - the parsing stops if the line is empty. + a = None + b = None + c = None + #f = None + i = None + j = None + k = None + p = None + q = None + r = None + s = None + x = None + y = None + z = None + iout = None + jout = None + kout = None + tool = 0 + #self.begin_ncblock() + + move = False + #arc = 0 + #path_col = None + drill = False + no_move = False + + words = self.pattern_main.findall(self.line) + for word in words: + col = None + cdata = False + if (word[0] == 'A' or word[0] == 'a'): + col = "axis" + a = eval(word[1:]) + move = True + elif (word[0] == 'B' or word[0] == 'b'): + col = "axis" + b = eval(word[1:]) + move = True + elif (word[0] == 'C' or word[0] == 'c'): + col = "axis" + c = eval(word[1:]) + move = True + elif (word[0] == 'F' or word[0] == 'f'): + col = "axis" + f = eval(word[1:]) + move = True + elif (word == 'G0' or word == 'G00' or word == 'g0' or word == 'g00'): + ##FILE.write('-color Magenta\n') + path_col = "rapid" + col = "rapid" + arc = 0 + elif (word == 'G1' or word == 'G01' or word == 'g1' or word == 'g01'): + ##FILE.write('-color Green\n') + path_col = "feed" + col = "feed" + arc = 0 + elif (word == 'G2' or word == 'G02' or word == 'g2' or word == 'g02' or word == 'G12' or word == 'g12'): + ##FILE.write('-color Green\n') + path_col = "feed" + col = "feed" + arc = -1 + elif (word == 'G3' or word == 'G03' or word == 'g3' or word == 'g03' or word == 'G13' or word == 'g13'): + ##FILE.write('-color Green\n') + path_col = "feed" + col = "feed" + arc = +1 + elif (word == 'G10' or word == 'g10'): + no_move = True + elif (word == 'L1' or word == 'l1'): + no_move = True + elif (word == 'G20' or word == 'G70'): + col = "prep" + self.set_mode(units=25.4) + elif (word == 'G21' or word == 'G71'): + col = "prep" + self.set_mode(units=1.0) + # Note: Anilam has very non standard params for drill cycles. Not Yet implemented! + elif (word == 'G81' or word == 'g81'): + drill = True + no_move = True + path_col = "feed" + col = "feed" + elif (word == 'G82' or word == 'g82'): + drill = True; + no_move = True + path_col = "feed" + col = "feed" + elif (word == 'G83' or word == 'g83'): + drill = True + no_move = True + path_col = "feed" + col = "feed" + elif (word[0] == 'G') : col = "prep" + elif (word[0] == 'I' or word[0] == 'i'): + col = "axis" + i = eval(word[1:]) + move = True + elif (word[0] == 'J' or word[0] == 'j'): + col = "axis" + j = eval(word[1:]) + move = True + elif (word[0] == 'K' or word[0] == 'k'): + col = "axis" + k = eval(word[1:]) + move = True + elif (word[0] == 'M') : col = "misc" + elif (word[0] == 'N') : col = "blocknum" + elif (word[0] == 'O') : col = "program" + elif (word[0] == 'P' or word[0] == 'p'): + col = "axis" + p = eval(word[1:]) + move = True + elif (word[0] == 'Q' or word[0] == 'q'): + col = "axis" + q = eval(word[1:]) + move = True + elif (word[0] == 'R' or word[0] == 'r'): + col = "axis" + r = eval(word[1:]) + move = True + elif (word[0] == 'S' or word[0] == 's'): + col = "axis" + s = eval(word[1:]) + move = True + elif (word[0] == 'T') : + col = "tool" + self.set_tool( eval(word[1:]) ) + tool = eval(word[1:]) + + elif (word[0] == 'X' or word[0] == 'x'): + col = "axis" + x = eval(word[1:]) + move = True + elif (word[0] == 'Y' or word[0] == 'y'): + col = "axis" + y = eval(word[1:]) + move = True + elif (word[0] == 'Z' or word[0] == 'z'): + col = "axis" + z = eval(word[1:]) + move = True + elif (word[0] == '(') : (col, cdata) = ("comment", True) + elif (word[0] == '!') : (col, cdata) = ("comment", True) + elif (word[0] == ';') : (col, cdata) = ("comment", True) + elif (word[0] == '#') : col = "variable" + elif (word[0] == ':') : col = "blocknum" + elif (ord(word[0]) <= 32) : cdata = True + #self.add_text(word, col, cdata) + + if (drill): + self.begin_path("rapid") + self.add_line(x, y, r) + self.end_path() + + self.begin_path("feed") + self.add_line(x, y, z) + self.end_path() + + self.begin_path("feed") + self.add_line(x, y, r) + self.end_path() + #elif (tool): + #write_layer('T',tool) + + + else: + if (move and not no_move): + self.begin_path(path_col) + #use absolute arc centers for IJK params. + # Subtract old XYZ off to get relative centers as expected: + + #if path_col == 'rapid': + #FILE.write('-color Red\n') + #else: + #FILE.write('-color Green\n') + + if (arc) : + + z = oldz + if (x != None) and (oldx != None) and (i != None): iout = i + if (y != None) and (oldy != None) and (j != None): jout = j + if (z != None) and (oldz != None) and (k != None): kout = k + self.add_arc(x, y, z, iout, jout, kout, r, arc) + #if (arc == -1): + ##FILE.write('arc %s,%s,%s\n' %(x,y,z)) + ##FILE.write('c\n') + ##FILE.write('%s,%s,%s\n' %(oldx+i,oldy+j,oldz)) + ##FILE.write('%s,%s,%s\n' %(oldx,oldy,z)) + + #else: + ##FILE.write('arc %s,%s,%s\n' %(oldx,oldy,z)) + ##FILE.write('c\n') + ##FILE.write('%s,%s,%s\n' %(oldx+i,oldy+j,oldz)) + ##FILE.write('%s,%s,%s\n' %(x,y,z)) + + else: + + self.add_line(x, y, z, a, b, c) + if (x == None) : x = oldx + if (y == None) : y = oldy + if (z == None) : z = oldz + scr_line = ('line %s,%s,%s %s,%s,%s \n' %(oldx,oldy,oldz,x,y,z)) + #print scr_line + + ##FILE.write(scr_line) + + self.end_path() + if (x != None) : oldx = x + if (y != None) : oldy = y + if (z != None) : oldz = z + + #oldx = x + #oldy = y + #oldz = z + self.end_ncblock() + + self.files_close() + #FILE.write('\n') + #FILE.close() + +################################################################################ +if __name__ == '__main__': + parser = CAD_backplot() + if len(sys.argv)>2: + parser.Parse(sys.argv[1],sys.argv[2]) + else: + parser.Parse(sys.argv[1]) diff --git a/src/Mod/Path/PathScripts/nc/centroid1.py b/src/Mod/Path/PathScripts/nc/centroid1.py new file mode 100644 index 0000000000..c1131e3151 --- /dev/null +++ b/src/Mod/Path/PathScripts/nc/centroid1.py @@ -0,0 +1,168 @@ +################################################################################ +# centroid1.py +# +# Post Processor for the centroid M40 machine +# +# +# Dan Falck, 7th March 2010 + +import nc +import iso_modal +import math + +import datetime + +now = datetime.datetime.now() + + + +################################################################################ +class Creator(iso_modal.Creator): + + def __init__(self): + iso_modal.Creator.__init__(self) + + self.useCrc = True + self.useCrcCenterline = True + self.absolute_flag = True + self.prev_g91 = '' + self.safe_z =None + def SPINDLE(self, format, speed): return(self.SPACE() + 'S' + (format % speed)) +################################################################################ +#cutter comp + + #def crc_on(self): + # self.useCrc = True + # self.useCrcCenterline = True + + #def crc_off(self): + # self.useCrc = False + +################################################################################ +# general + + def comment(self, text): + self.write(';' + text +'\n') + def write_blocknum(self): + pass + +################################################################################ +# settings for absolute or incremental mode + def absolute(self): + self.write(self.ABSOLUTE()+'\n') + self.absolute_flag = True + + def incremental(self): + self.write(self.INCREMENTAL()+'\n') + self.absolute_flag = False + +################################################################################ +# APT style INSERT- insert anything into program + + def insert(self, text): + self.write((text + '\n')) + +################################################################################ +# program begin and end + + def program_begin(self, id, name=''): + self.write(';time:'+str(now)+'\n') + self.write('G17 G20 G80 G40 G90\n') + + + def program_end(self): + self.write('M05\n') + self.write('M25\n') + self.write('G00 X-1.0 Y1.0\n') + self.write('G17 G80 G40 G90\n') + self.write('M99\n') + + def program_stop(self, optional=False): + self.write_blocknum() + if (optional) : + self.write(self.STOP_OPTIONAL() + '\n') + else : + self.write('M05\n') + self.write('M25\n') + self.write(self.STOP() + '\n') + self.prev_g0123 = '' +################################################################################ +# coordinate system ie G54-G59 + + def workplane(self, id): + self.write('M25\n') + if ((id >= 1) and (id <= 6)): + self.g_list.append(self.WORKPLANE() % (id + self.WORKPLANE_BASE())) + if ((id >= 7) and (id <= 9)): + self.g_list.append(((self.WORKPLANE() % (6 + self.WORKPLANE_BASE())) + ('.%i' % (id - 6)))) + self.prev_g0123 = '' + +################################################################################ +# clearance plane + + def clearanceplane(self,z=None): + self.safe_z = z +################################################################################ +# return to home + def rapid_home(self, x=None, y=None, z=None, a=None, b=None, c=None): + """Rapid relative to home position""" + self.write('M05\n') + self.write('M25\n') + self.write(self.RAPID()) + self.write(self.X() + (self.fmt % x)) + self.write(self.Y() + (self.fmt % y)) + self.write('\n') + +################################################################################ +# tool info + def tool_change(self, id): + self.write_blocknum() + self.write((self.TOOL() % id) + '\n') + self.t = id + self.write('M25\n') + if self.safe_z == None: + self.write('G43 H'+ str(id) + ' Z') + self.write('1.0') + self.write ('\n') + else: + self.write('G43 H'+ str(id) + ' Z') + self.write(str(self.safe_z)) + self.write ('\n') + + + def tool_defn(self, id, name='', params=None): + #self.write('G43 \n') + pass + + def write_spindle(self): + pass + + + def spindle(self, s, clockwise): + if s < 0: + clockwise = not clockwise + s = abs(s) + + self.s = self.SPINDLE(self.FORMAT_ANG(), s) + if clockwise: + #self.s = self.SPINDLE_CW() + self.s + self.s = self.SPINDLE_CW() + self.write(self.s + '\n') + self.write('G04 P2.0 \n') + + else: + self.s = self.SPINDLE_CCW() + self.s + + def end_canned_cycle(self): + self.write_blocknum() + self.write(self.SPACE() + self.END_CANNED_CYCLE() + '\n') + self.prev_drill = '' + self.prev_g0123 = '' + self.prev_z = '' + self.prev_f = '' + self.prev_retract = '' + self.write('M05\n') + self.write('M25\n') + self.write('G00 X-1.0 Y1.0\n') + +nc.creator = Creator() diff --git a/src/Mod/Path/PathScripts/nc/centroid1_read.py b/src/Mod/Path/PathScripts/nc/centroid1_read.py new file mode 100644 index 0000000000..31870d14e8 --- /dev/null +++ b/src/Mod/Path/PathScripts/nc/centroid1_read.py @@ -0,0 +1,8 @@ +import iso_read as iso +import sys + +# just use the iso reader + +class Parser(iso.Parser): + def __init__(self, writer): + iso.Parser.__init__(self, writer) diff --git a/src/Mod/Path/PathScripts/nc/cutviewer.py b/src/Mod/Path/PathScripts/nc/cutviewer.py new file mode 100644 index 0000000000..033b592e98 --- /dev/null +++ b/src/Mod/Path/PathScripts/nc/cutviewer.py @@ -0,0 +1,34 @@ +def tool_defn(self, id, params): + self.write('(TOOL/') + type = params['type'] + if type == 0:#eDrill = 0, +#;TOOL/DRILL, Diameter, Point Angle, Height + self.write('DRILL, ' + str(params['diameter'])) + self.write(', ' + str(params['cutting edge angle'] * 2.0)) + self.write(', ' + str(params['cutting edge height'])) + elif type == 1:#eCentreDrill, +#;TOOL/CDRILL, D1, A1, L, D2, A2, H (see Fig. below) + self.write('CDRILL, ' + str(params['flat radius'] * 2)) + self.write(', ' + str(params['cutting edge angle'])) + self.write(', ' + str(params['flat radius'] * 2)) + self.write(', ' + str(params['diameter'])) + self.write(', ' + str(params['cutting edge angle'] * 2.0)) + self.write(', ' + str(params['cutting edge height'])) + elif type == 2 or type == 3 or type == 4:#eSlotCutter,#eEndmill,#eBallEndMill, +#TOOL/MILL, Diameter, Corner radius, Height, Taper Angle + self.write('MILL, ' + str(params['diameter'])) + self.write(', ' + str(params['corner radius'])) + self.write(', ' + str(params['cutting edge height'])) + self.write(', ' + str(params['cutting edge angle'])) + elif type == 5 or type == 6:#eChamfer,#eEngravingTool, +#;TOOL/CHAMFER, Diameter, Point Angle, Height +#;TOOL/CHAMFER, Diameter, Point Angle, Height, Chamfer Length + self.write('CHAMFER, ' + str(params['diameter'])) + self.write(', ' + str(params['cutting edge angle'])) + self.write(', ' + str(params['cutting edge height'])) + else:#eUndefinedToolType + pass + self.write(')\n') + +# to do +#;TOOL/CRMILL, Diameter1, Diameter2, Radius, Height, Length diff --git a/src/Mod/Path/PathScripts/nc/drag_knife.py b/src/Mod/Path/PathScripts/nc/drag_knife.py new file mode 100644 index 0000000000..49a904827a --- /dev/null +++ b/src/Mod/Path/PathScripts/nc/drag_knife.py @@ -0,0 +1,72 @@ +################################################################################ +# drag knife.py +# +# NC code creator for attaching Z coordinates to a surface +# +# Dan Heeks 26th April 2012 + +import recreator +dragging = False +from kurve_funcs import cut_curve as cut_curve +import nc +import area + +################################################################################ +class Creator(recreator.Redirector): + + def __init__(self, original, drag_distance): + recreator.Redirector.__init__(self, original) + + self.drag_distance = drag_distance + self.path = None + + def cut_path(self): + if self.path == None: return + + print self.drag_distance + self.path.OffsetForward(self.drag_distance, False) + + nc.creator = nc.creator.original + + if self.path.getNumVertices() > 0: + v = self.path.FirstVertex() + nc.creator.feed(v.p.x, v.p.y) + + cut_curve(self.path) + nc.creator = self + + self.path = area.Curve() + + def feed(self, x=None, y=None, z=None, a=None, b=None, c=None): + px = self.x + py = self.y + pz = self.z + recreator.Redirector.feed(self, x, y, z, a, b, c) + if self.x == None or self.y == None or self.z == None: + return + if px == self.x and py == self.y: + return + + # add a line to the path + if self.path == None: self.path = area.Curve() + self.path.append(area.Point(self.x, self.y)) + + def arc(self, x=None, y=None, z=None, i=None, j=None, k=None, r=None, ccw = True): + recreator.Redirector.arc(self, x, y, z, i, j, k, r, ccw) + + # add an arc to the path + if self.path == None: self.path = area.Curve() + self.path.append(area.Vertex(1 if ccw else -1, area.Point(self.x, self.y), area.Point(i, j))) + +def drag_begin(drag_distance): + global dragging + if dragging == True: + drag_end() + nc.creator = Creator(nc.creator, drag_distance) + dragging = True + +def drag_end(): + global dragging + nc.creator.cut_path() + nc.creator = nc.creator.original + attached = False diff --git a/src/Mod/Path/PathScripts/nc/dynapath.py b/src/Mod/Path/PathScripts/nc/dynapath.py new file mode 100644 index 0000000000..4b4c51dd2a --- /dev/null +++ b/src/Mod/Path/PathScripts/nc/dynapath.py @@ -0,0 +1,38 @@ +import nc +import iso +import math +import datetime +import time +from format import Format + +now = datetime.datetime.now() + +class Creator(iso.Creator): + def __init__(self): + iso.Creator.__init__(self) + self.output_tool_definitions = False + self.m_codes_on_their_own_line = True + self.output_g98_and_g99 = False + #self.fmt = Format(dp_wanted = False, add_trailing_zeros = True, add_plus = True) + + #def SPACE_STR(self): return ' ' + def PROGRAM(self): return None + def RETRACT(self, height): return('R' + (self.fmt.string(height))) + def PECK_DEPTH(self, depth): return('O' + (self.fmt.string(depth))) + + def program_begin(self, id, name=''): + self.write('(' + name + ')\n') + + def imperial(self): + #self.g_list.append(self.IMPERIAL()) + self.fmt.number_of_decimal_places = 4 + + def metric(self): + #self.g_list.append(self.METRIC()) + self.fmt.number_of_decimal_places = 3 + + def comment(self, text): + pass + +nc.creator = Creator() + diff --git a/src/Mod/Path/PathScripts/nc/emc2.py b/src/Mod/Path/PathScripts/nc/emc2.py new file mode 100755 index 0000000000..a9ff3fa211 --- /dev/null +++ b/src/Mod/Path/PathScripts/nc/emc2.py @@ -0,0 +1,235 @@ +import nc +import iso +import math + +class Creator(iso.Creator): + def init(self): + iso.Creator.init(self) + + def SPACE(self): return('') + def TAP(self): return('G33.1') + def TAP_DEPTH(self, format, depth): return(self.SPACE() + 'K' + (format.string(depth))) + def BORE_FEED_OUT(self): return('G85') + def BORE_SPINDLE_STOP_RAPID_OUT(self): return('G86') + def BORE_DWELL_FEED_OUT(self, format, dwell): return('G89') + self.SPACE() + (format.string(dwell)) + def FEEDRATE(self): return((self.SPACE() + ' F')) + + def program_begin(self, id, comment): + self.write( ('(' + comment + ')' + '\n') ) + + ############################################################################ + ## Settings + + def imperial(self): + self.write( self.IMPERIAL() + '\t (Imperial Values)\n') + self.fmt.number_of_decimal_places = 4 + + def metric(self): + self.fmt.number_of_decimal_places = 3 + self.write( self.METRIC() + '\t (Metric Values)\n' ) + + def absolute(self): + self.write( self.ABSOLUTE() + '\t (Absolute Coordinates)\n') + + def incremental(self): + self.write( self.INCREMENTAL() + '\t (Incremental Coordinates)\n' ) + + def polar(self, on=True): + if (on) : + self.write(self.POLAR_ON() + '\t (Polar ON)\n' ) + else : + self.write(self.POLAR_OFF() + '\t (Polar OFF)\n' ) + + def set_plane(self, plane): + if (plane == 0) : + self.write(self.PLANE_XY() + '\t (Select XY Plane)\n') + elif (plane == 1) : + self.write(self.PLANE_XZ() + '\t (Select XZ Plane)\n') + elif (plane == 2) : + self.write(self.PLANE_YZ() + '\t (Select YZ Plane)\n') + + def comment(self, text): + self.write((self.COMMENT(text) + '\n')) + + # This is the coordinate system we're using. G54->G59, G59.1, G59.2, G59.3 + # These are selected by values from 1 to 9 inclusive. + def workplane(self, id): + if ((id >= 1) and (id <= 6)): + self.write( (self.WORKPLANE() % (id + self.WORKPLANE_BASE())) + '\t (Select Relative Coordinate System)\n') + if ((id >= 7) and (id <= 9)): + self.write( ((self.WORKPLANE() % (6 + self.WORKPLANE_BASE())) + ('.%i' % (id - 6))) + '\t (Select Relative Coordinate System)\n') + + def report_probe_results(self, x1=None, y1=None, z1=None, x2=None, y2=None, z2=None, x3=None, y3=None, z3=None, x4=None, y4=None, z4=None, x5=None, y5=None, z5=None, x6=None, y6=None, z6=None, xml_file_name=None ): + if (xml_file_name != None): + self.comment('Generate an XML document describing the probed coordinates found'); + self.write('(LOGOPEN,') + self.write(xml_file_name) + self.write(')\n') + + self.write('(LOG,)\n') + + if ((x1 != None) or (y1 != None) or (z1 != None)): + self.write('(LOG,)\n') + + if (x1 != None): + self.write('#<_value>=[' + x1 + ']\n') + self.write('(LOG,#<_value>)\n') + + if (y1 != None): + self.write('#<_value>=[' + y1 + ']\n') + self.write('(LOG,#<_value>)\n') + + if (z1 != None): + self.write('#<_value>=[' + z1 + ']\n') + self.write('(LOG,#<_value>)\n') + + if ((x1 != None) or (y1 != None) or (z1 != None)): + self.write('(LOG,)\n') + + if ((x2 != None) or (y2 != None) or (z2 != None)): + self.write('(LOG,)\n') + + if (x2 != None): + self.write('#<_value>=[' + x2 + ']\n') + self.write('(LOG,#<_value>)\n') + + if (y2 != None): + self.write('#<_value>=[' + y2 + ']\n') + self.write('(LOG,#<_value>)\n') + + if (z2 != None): + self.write('#<_value>=[' + z2 + ']\n') + self.write('(LOG,#<_value>)\n') + + if ((x2 != None) or (y2 != None) or (z2 != None)): + self.write('(LOG,)\n') + + if ((x3 != None) or (y3 != None) or (z3 != None)): + self.write('(LOG,)\n') + + if (x3 != None): + self.write('#<_value>=[' + x3 + ']\n') + self.write('(LOG,#<_value>)\n') + + if (y3 != None): + self.write('#<_value>=[' + y3 + ']\n') + self.write('(LOG,#<_value>)\n') + + if (z3 != None): + self.write('#<_value>=[' + z3 + ']\n') + self.write('(LOG,#<_value>)\n') + + if ((x3 != None) or (y3 != None) or (z3 != None)): + self.write('(LOG,)\n') + + if ((x4 != None) or (y4 != None) or (z4 != None)): + self.write('(LOG,)\n') + + if (x4 != None): + self.write('#<_value>=[' + x4 + ']\n') + self.write('(LOG,#<_value>)\n') + + if (y4 != None): + self.write('#<_value>=[' + y4 + ']\n') + self.write('(LOG,#<_value>)\n') + + if (z4 != None): + self.write('#<_value>=[' + z4 + ']\n') + self.write('(LOG,#<_value>)\n') + + if ((x4 != None) or (y4 != None) or (z4 != None)): + self.write('(LOG,)\n') + + if ((x5 != None) or (y5 != None) or (z5 != None)): + self.write('(LOG,)\n') + + if (x5 != None): + self.write('#<_value>=[' + x5 + ']\n') + self.write('(LOG,#<_value>)\n') + + if (y5 != None): + self.write('#<_value>=[' + y5 + ']\n') + self.write('(LOG,#<_value>)\n') + + if (z5 != None): + self.write('#<_value>=[' + z5 + ']\n') + self.write('(LOG,#<_value>)\n') + + if ((x5 != None) or (y5 != None) or (z5 != None)): + self.write('(LOG,)\n') + + if ((x6 != None) or (y6 != None) or (z6 != None)): + self.write('(LOG,)\n') + + if (x6 != None): + self.write('#<_value>=[' + x6 + ']\n') + self.write('(LOG,#<_value>)\n') + + if (y6 != None): + self.write('#<_value>=[' + y6 + ']\n') + self.write('(LOG,#<_value>)\n') + + if (z6 != None): + self.write('#<_value>=[' + z6 + ']\n') + self.write('(LOG,#<_value>)\n') + + if ((x6 != None) or (y6 != None) or (z6 != None)): + self.write('(LOG,)\n') + + self.write('(LOG,)\n') + + if (xml_file_name != None): + self.write('(LOGCLOSE)\n') + + def open_log_file(self, xml_file_name=None ): + self.write('(LOGOPEN,') + self.write(xml_file_name) + self.write(')\n') + + def close_log_file(self): + self.write('(LOGCLOSE)\n') + + def log_coordinate(self, x=None, y=None, z=None): + if ((x != None) or (y != None) or (z != None)): + self.write('(LOG,)\n') + + if (x != None): + self.write('#<_value>=[' + x + ']\n') + self.write('(LOG,#<_value>)\n') + + if (y != None): + self.write('#<_value>=[' + y + ']\n') + self.write('(LOG,#<_value>)\n') + + if (z != None): + self.write('#<_value>=[' + z + ']\n') + self.write('(LOG,#<_value>)\n') + + if ((x != None) or (y != None) or (z != None)): + self.write('(LOG,)\n') + + def log_message(self, message=None ): + self.write('(LOG,' + message + ')\n') + + def start_CRC(self, left = True, radius = 0.0): + if self.t == None: + raise "No tool specified for start_CRC()" + if left: + self.write(('G41' + self.SPACE() + 'D%i') % self.t + '\t (start left cutter radius compensation)\n' ) + else: + self.write(('G42' + self.SPACE() + 'D%i') % self.t + '\t (start right cutter radius compensation)\n' ) + + def end_CRC(self): + self.g = 'G40' + self.write_preps() + self.write_misc() + self.write('\t (end cutter radius compensation)\n') + + + + + def tool_defn(self, id, name='', params=None): + pass + +nc.creator = Creator() + diff --git a/src/Mod/Path/PathScripts/nc/emc2b.py b/src/Mod/Path/PathScripts/nc/emc2b.py new file mode 100644 index 0000000000..8e8cb7daa0 --- /dev/null +++ b/src/Mod/Path/PathScripts/nc/emc2b.py @@ -0,0 +1,39 @@ +import nc +import iso_modal +import math +import datetime +import time + +now = datetime.datetime.now() + +class Creator(iso_modal.Creator): + def __init__(self): + iso_modal.Creator.__init__(self) + self.output_block_numbers = False + self.output_tool_definitions = False + self.output_g43_on_tool_change_line = True + + def SPACE(self): + if self.start_of_line == True: + self.start_of_line = False + return '' + else: + return ' ' + + def PROGRAM(self): return None + def PROGRAM_END(self): return( 'T0' + self.SPACE() + 'M06' + self.SPACE() + 'M02') + +############################################################################ +## Begin Program + + + def program_begin(self, id, comment): + if (self.useCrc == False): + self.write( ('(Created with emc2b post processor ' + str(now.strftime("%Y/%m/%d %H:%M")) + ')' + '\n') ) + else: + self.write( ('(Created with emc2b Cutter Radius Compensation post processor ' + str(now.strftime("%Y/%m/%d %H:%M")) + ')' + '\n') ) + iso_modal.Creator.program_begin(self, id, comment) + + +nc.creator = Creator() + diff --git a/src/Mod/Path/PathScripts/nc/emc2b_crc.py b/src/Mod/Path/PathScripts/nc/emc2b_crc.py new file mode 100644 index 0000000000..ff023c31ce --- /dev/null +++ b/src/Mod/Path/PathScripts/nc/emc2b_crc.py @@ -0,0 +1,21 @@ +################################################################################ +# emc2b_crc.py +# +# a class derived from emc2b machine, with Cutter Radius Compensation turned on. +# +# Dan Heeks, 18th Jan 2011 + +import nc +import emc2b +import math + +################################################################################ +class Creator(emc2b.Creator): + + def __init__(self): + emc2b.Creator.__init__(self) + self.useCrc = True + +################################################################################ + +nc.creator = Creator() diff --git a/src/Mod/Path/PathScripts/nc/emc2tap.py b/src/Mod/Path/PathScripts/nc/emc2tap.py new file mode 100755 index 0000000000..4bf4023dd9 --- /dev/null +++ b/src/Mod/Path/PathScripts/nc/emc2tap.py @@ -0,0 +1,85 @@ +import nc +import iso_codes +import emc2 + + + +class CodesEMC2(iso_codes.Codes): + def SPACE(self): return(' ') + def TAP(self): return('G33.1') + def TAP_DEPTH(self, format, depth): return(self.SPACE() + 'K' + (format % depth)) + + + # This version of COMMENT removes comments from the resultant GCode + #def COMMENT(self,comment): return('') + +iso_codes.codes = CodesEMC2() + + +class CreatorEMC2tap(emc2.CreatorEMC2): + def init(self): + iso.CreatorEMC2.init(self) + + + # G33.1 tapping with EMC for now + # unsynchronized (chuck) taps NIY (tap_mode = 1) + def tap(self, x=None, y=None, z=None, zretract=None, depth=None, standoff=None, dwell_bottom=None, pitch=None, stoppos=None, spin_in=None, spin_out=None, tap_mode=None, direction=None): + # mystery parameters: + # zretract=None, dwell_bottom=None,pitch=None, stoppos=None, spin_in=None, spin_out=None): + # I dont see how to map these to EMC Gcode + + if (standoff == None): + # This is a bad thing. All the drilling cycles need a retraction (and starting) height. + return + if (z == None): + return # We need a Z value as well. This input parameter represents the top of the hole + if (pitch == None): + return # We need a pitch value. + if (direction == None): + return # We need a direction value. + + if (tap_mode != 0): + self.comment('only rigid tapping currently supported') + return + + self.write_preps() + self.write_blocknum() + self.write_spindle() + self.write('\n') + + # rapid to starting point; z first, then x,y iff given + + # Set the retraction point to the 'standoff' distance above the starting z height. + retract_height = z + standoff + + # unsure if this is needed: + if self.z != retract_height: + self.rapid(z = retract_height) + + # then continue to x,y if given + if (x != None) or (y != None): + self.write_blocknum() + self.write(iso_codes.codes.RAPID() ) + + if (x != None): + self.write(iso_codes.codes.X() + (self.fmt % x)) + self.x = x + + if (y != None): + self.write(iso_codes.codes.Y() + (self.fmt % y)) + self.y = y + self.write('\n') + + self.write_blocknum() + self.write( iso_codes.codes.TAP() ) + self.write( iso_codes.codes.TAP_DEPTH(self.ffmt,pitch) + iso_codes.codes.SPACE() ) + self.write(iso_codes.codes.Z() + (self.fmt % (z - depth))) # This is the 'z' value for the bottom of the tap. + self.write_misc() + self.write('\n') + + self.z = retract_height # this cycle returns to the start position, so remember that as z value + + + +nc.creator = CreatorEMC2tap() + diff --git a/src/Mod/Path/PathScripts/nc/format.py b/src/Mod/Path/PathScripts/nc/format.py new file mode 100644 index 0000000000..beaea30132 --- /dev/null +++ b/src/Mod/Path/PathScripts/nc/format.py @@ -0,0 +1,105 @@ +import math + +class Format: + def __init__(self, number_of_decimal_places = 3, add_leading_zeros = 1, add_trailing_zeros = False, dp_wanted = True, add_plus = False, no_minus = False, round_down = False): + self.number_of_decimal_places = number_of_decimal_places + self.add_leading_zeros = add_leading_zeros # fill the start of the number with zeros, so there are at least this number of digits before the decimal point + self.add_trailing_zeros = add_trailing_zeros # fill the end of the number with zeros, as defined by "number_of_decimal_places" + self.dp_wanted = dp_wanted + self.add_plus = add_plus + self.no_minus = no_minus + self.round_down = round_down + + def string(self, number): + if number == None: + return 'None' + f = float(number) * math.pow(10, self.number_of_decimal_places) + s = str(f) + + if self.round_down == False: + if f < 0: f = f - .5 + else: f = f + .5 + s = str(number) + + if math.fabs(f) < 1.0: + s = '0' + + minus = False + if s[0] == '-': + minus = True + if self.no_minus: + s = s[1:] + + dot = s.find('.') + if dot == -1: + before_dp = s + after_dp = '' + else: + before_dp = s[0:dot] + after_dp = s[dot + 1: dot + 1 + self.number_of_decimal_places] + + before_dp = before_dp.zfill(self.add_leading_zeros) + if self.add_trailing_zeros: + for i in range(0, self.number_of_decimal_places - len(after_dp)): + after_dp += '0' + else: + after_dp = after_dp.rstrip('0') + + s = '' + + if minus == False: + if self.add_plus == True: + s += '+' + s += before_dp + if len(after_dp): + if self.dp_wanted: s += '.' + s += after_dp + + return s + +class Address: + def __init__(self, text, fmt = Format(), modal = True): + self.text = text + self.fmt = fmt + self.modal = modal + self.str = None + self.previous = None + + def set(self, number): + self.str = self.text + self.fmt.string(number) + + def write(self, writer): + if self.str == None: return '' + if self.modal: + if self.str != self.previous: + writer.write(self.str) + self.previous = self.str + else: + writer.write(self.str) + self.str = None + +class AddressPlusMinus(Address): + def __init__(self, text, fmt = Format(), modal = True): + Address.__init__(self, text, fmt, modal) + self.str2 = None + self.previous2 = None + + def set(self, number, text_plus, text_minus): + Address.set(self, number) + if float(number) > 0.0: + self.str2 = text_plus + else: + self.str2 = text_minus + + def write(self, writer): + Address.write(self, writer) + if self.str2 == None: return '' + if self.modal: + if self.str2 != self.previous2: + writer.write(writer.SPACE()) + writer.write(self.str2) + self.previous2 = self.str2 + else: + writer.write(writer.SPACE()) + writer.write(self.str2) + self.str2 = None diff --git a/src/Mod/Path/PathScripts/nc/gantry_router.py b/src/Mod/Path/PathScripts/nc/gantry_router.py new file mode 100755 index 0000000000..15d95fe2e9 --- /dev/null +++ b/src/Mod/Path/PathScripts/nc/gantry_router.py @@ -0,0 +1,18 @@ +import nc +import emc2 + +class Creator(emc2.Creator): + def init(self): + emc2.Creator.init(self) + + def program_begin(self, id, comment): + self.write( ('(' + comment + ')' + '\n') ) + + def tool_defn(self, id, name='', params=None): + pass + + def spindle(self, s, clockwise): + pass + +nc.creator = Creator() + diff --git a/src/Mod/Path/PathScripts/nc/heiden.py b/src/Mod/Path/PathScripts/nc/heiden.py new file mode 100644 index 0000000000..aadec66d66 --- /dev/null +++ b/src/Mod/Path/PathScripts/nc/heiden.py @@ -0,0 +1,1156 @@ + +# heiden.py, just copied from iso.py, to start with, but needs to be modified to make this sort of output + +#1 BEGIN PGM 0011 MM +#2 BLK FORM 0.1 Z X-262.532 Y-262.55 Z-75.95 +#3 BLK FORM 0.2 X262.532 Y262.55 Z0.05 +#4 TOOL CALL 3 Z S3263 DL+0.0 DR+0.0 +#5 TOOL CALL 3 Z S3263 DL+0.0 DR+0.0 +#6 L X-80.644 Y-95.2 Z+100.0 R0 F237 M3 +#7 L Z-23.222 F333 +#8 L X-80.627 Y-95.208 Z-23.5 F326 +#49 L X-73.218 Y-88.104 Z-26.747 F229 +#50 L X-73.529 Y-87.795 Z-26.769 F227 +#51 L X-74.09 Y-87.326 Z-25.996 F279 +#52 M30 +#53 END PGM 0011 MM + + +import nc +import math +from format import Format +from format import * + +################################################################################ +class Creator(nc.Creator): + + def __init__(self): + nc.Creator.__init__(self) + + self.a = 0 + self.b = 0 + self.c = 0 + self.f = Address('F', fmt = Format(number_of_decimal_places = 2)) + self.fh = None + self.fv = None + self.fhv = False + self.g_plane = Address('G', fmt = Format(number_of_decimal_places = 0)) + self.g_list = [] + self.i = 0 + self.j = 0 + self.k = 0 + self.m = [] + self.n = 10 + self.r = 0 + self.s = AddressPlusMinus('S', fmt = Format(number_of_decimal_places = 2), modal = False) + self.t = None + self.x = 0 + self.y = 0 + self.z = 500 + self.g0123_modal = False + self.drill_modal = False + self.prev_f = '' + self.prev_g0123 = '' + self.prev_drill = '' + self.prev_retract = '' + self.prev_z = '' + self.useCrc = False + self.useCrcCenterline = False + self.gCRC = '' + self.fmt = Format() + self.absolute_flag = True + self.ffmt = Format(number_of_decimal_places = 2) + self.sfmt = Format(number_of_decimal_places = 1) + self.arc_centre_absolute = False + self.arc_centre_positive = False + self.in_quadrant_splitting = False + self.drillExpanded = False + self.can_do_helical_arcs = True + self.shift_x = 0.0 + self.shift_y = 0.0 + self.shift_z = 0.0 + ############################################################################ + ## Codes + + def SPACE(self): return('') + def FORMAT_FEEDRATE(self): return('%.2f') + def FORMAT_ANG(self): return('%.1f') + def FORMAT_TIME(self): return('%.2f') + def FORMAT_DWELL(self): return('P%f') + + def BLOCK(self): return('%i') + def COMMENT(self,comment): return( ('(%s)' % comment ) ) + def VARIABLE(self): return( '#%i') + def VARIABLE_SET(self): return( '=%.3f') + + def SUBPROG_CALL(self): return( 'M98' + self.SPACE() + 'P%i') + def SUBPROG_END(self): return( 'M99') + + def STOP_OPTIONAL(self): return('M01') + def STOP(self): return('M00') + + def IMPERIAL(self): return('G20') + def METRIC(self): return('G21') + def ABSOLUTE(self): return('G90') + def INCREMENTAL(self): return('G91') + def SET_TEMPORARY_COORDINATE_SYSTEM(self): return('G92') + def REMOVE_TEMPORARY_COORDINATE_SYSTEM(self): return('G92.1') + def POLAR_ON(self): return('G16') + def POLAR_OFF(self): return('G15') + def PLANE_XY(self): return('17') + def PLANE_XZ(self): return('18') + def PLANE_YZ(self): return('19') + + def TOOL(self): return('T%i' + self.SPACE() + 'M06') + def TOOL_DEFINITION(self): return('G10' + self.SPACE() + 'L1') + + def WORKPLANE(self): return('G%i') + def WORKPLANE_BASE(self): return(53) + + def SPINDLE_CW(self): return('M03') + def SPINDLE_CCW(self): return('M04') + def COOLANT_OFF(self): return('M09') + def COOLANT_MIST(self): return('M07') + def COOLANT_FLOOD(self): return('M08') + def GEAR_OFF(self): return('?') + def GEAR(self): return('M%i') + def GEAR_BASE(self): return(37) + + def RAPID(self): return('G00') + def FEED(self): return('G01') + def ARC_CW(self): return('G02') + def ARC_CCW(self): return('G03') + def DWELL(self): return('G04') + def DRILL(self): return('G81') + def DRILL_WITH_DWELL(self, format, dwell): return('G82' + self.SPACE() + (format.string(dwell))) + def PECK_DRILL(self): return('G83') + def PECK_DEPTH(self, format, depth): return(self.SPACE() + 'Q' + (format.string(depth))) + def RETRACT(self, format, height): return(self.SPACE() + 'R' + (format.string(height))) + def END_CANNED_CYCLE(self): return('G80') + def TAP(self): return('G84') + def TAP_DEPTH(self, format, depth): return(self.SPACE() + 'K' + (format.string(depth))) + + def X(self): return('X') + def Y(self): return('Y') + def Z(self): return('Z') + def A(self): return('A') + def B(self): return('B') + def C(self): return('C') + def CENTRE_X(self): return('I') + def CENTRE_Y(self): return('J') + def CENTRE_Z(self): return('K') + def RADIUS(self): return('R') + def TIME(self): return('P') + + def PROBE_TOWARDS_WITH_SIGNAL(self): return('G38.2') + def PROBE_TOWARDS_WITHOUT_SIGNAL(self): return('G38.3') + def PROBE_AWAY_WITH_SIGNAL(self): return('G38.4') + def PROBE_AWAY_WITHOUT_SIGNAL(self): return('G38.5') + + def MACHINE_COORDINATES(self): return('G53') + + def EXACT_PATH_MODE(self): return('G61') + def EXACT_STOP_MODE(self): return('G61.1') + + ############################################################################ + ## Internals + + def write_feedrate(self): + self.f.write(self) + + def write_preps(self): + self.g_plane.write(self) + for g in self.g_list: + self.write(self.SPACE() + g) + self.g_list = [] + + def write_misc(self): + if (len(self.m)) : self.write(self.m.pop()) + + def write_blocknum(self): + self.write(self.BLOCK() % self.n) + self.n += 1 + + def write_spindle(self): + self.s.write(self) + + ############################################################################ + ## Programs + + def program_begin(self, id, name=''): + #1 BEGIN PGM 0011 MM + self.write_blocknum() + self.program_id = id + self.write(self.SPACE() + ('BEGIN PGM %i MM' % id)) + self.write('\n') + + def program_stop(self, optional=False): + self.write_blocknum() + if (optional) : + self.write(self.SPACE() + self.STOP_OPTIONAL() + '\n') + self.prev_g0123 = '' + else : + self.write(self.STOP() + '\n') + self.prev_g0123 = '' + + + def program_end(self): + self.write_blocknum() + self.write(self.SPACE() + ('END PGM %i MM' % self.program_id) + '\n') + + def flush_nc(self): + if len(self.g_list) == 0 and len(self.m) == 0: return + self.write_blocknum() + self.write_preps() + self.write_misc() + self.write('\n') + + ############################################################################ + ## Subprograms + + def sub_begin(self, id, name=''): + self.write((self.PROGRAM() % id) + self.SPACE() + (self.COMMENT(name))) + self.write('\n') + + def sub_call(self, id): + self.write_blocknum() + self.write(self.SPACE() + (self.SUBPROG_CALL() % id) + '\n') + + def sub_end(self): + self.write_blocknum() + self.write(self.SPACE() + self.SUBPROG_END() + '\n') + + ############################################################################ + ## Settings + + def imperial(self): + self.g_list.append(self.IMPERIAL()) + self.fmt.number_of_decimal_places = 4 + + def metric(self): + self.g_list.append(self.METRIC()) + self.fmt.number_of_decimal_places = 3 + + def absolute(self): + self.g_list.append(self.ABSOLUTE()) + self.absolute_flag = True + + def incremental(self): + self.g_list.append(self.INCREMENTAL()) + self.absolute_flag = False + + def polar(self, on=True): + if (on) : self.g_list.append(self.POLAR_ON()) + else : self.g_list.append(self.POLAR_OFF()) + + def set_plane(self, plane): + if (plane == 0) : self.g_plane.set(self.PLANE_XY()) + elif (plane == 1) : self.g_plane.set(self.PLANE_XZ()) + elif (plane == 2) : self.g_plane.set(self.PLANE_YZ()) + + def set_temporary_origin(self, x=None, y=None, z=None, a=None, b=None, c=None): + self.write_blocknum() + self.write(self.SPACE() + (self.SET_TEMPORARY_COORDINATE_SYSTEM())) + if (x != None): self.write( self.SPACE() + 'X ' + (self.fmt.string(x + self.shift_x)) ) + if (y != None): self.write( self.SPACE() + 'Y ' + (self.fmt.string(y + self.shift_y)) ) + if (z != None): self.write( self.SPACE() + 'Z ' + (self.fmt.string(z + self.shift_z)) ) + if (a != None): self.write( self.SPACE() + 'A ' + (self.fmt.string(a)) ) + if (b != None): self.write( self.SPACE() + 'B ' + (self.fmt.string(b)) ) + if (c != None): self.write( self.SPACE() + 'C ' + (self.fmt.string(c)) ) + self.write('\n') + + def remove_temporary_origin(self): + self.write_blocknum() + self.write(self.SPACE() + (self.REMOVE_TEMPORARY_COORDINATE_SYSTEM())) + self.write('\n') + ############################################################################ + ## new graphics origin- make a new coordinate system and snap it onto the geometry + ## the toolpath generated should be translated + def translate(self,x=None, y=None, z=None): + self.shift_x = -x + self.shift_y = -y + self.shift_z = -z + + ############################################################################ + ## Tools + + def tool_change(self, id): + self.write_blocknum() + self.write(self.SPACE() + (self.TOOL() % id) + '\n') + self.t = id + + def tool_defn(self, id, name='', params=None): + self.write_blocknum() + self.write(self.SPACE() + self.TOOL_DEFINITION()) + self.write(self.SPACE() + ('P%i' % id) + ' ') + + if (radius != None): + self.write(self.SPACE() + ('R%.3f' % (float(params['diameter'])/2))) + + if (length != None): + self.write(self.SPACE() + 'Z%.3f' % float(params['cutting edge height'])) + + self.write('\n') + + def offset_radius(self, id, radius=None): + pass + + def offset_length(self, id, length=None): + pass + + def current_tool(self): + return self.t + + ############################################################################ + ## Datums + + def datum_shift(self, x=None, y=None, z=None, a=None, b=None, c=None): + pass + + def datum_set(self, x=None, y=None, z=None, a=None, b=None, c=None): + pass + + # This is the coordinate system we're using. G54->G59, G59.1, G59.2, G59.3 + # These are selected by values from 1 to 9 inclusive. + def workplane(self, id): + if ((id >= 1) and (id <= 6)): + self.g_list.append(self.WORKPLANE() % (id + self.WORKPLANE_BASE())) + if ((id >= 7) and (id <= 9)): + self.g_list.append(((self.WORKPLANE() % (6 + self.WORKPLANE_BASE())) + ('.%i' % (id - 6)))) + + + ############################################################################ + ## Rates + Modes + + def feedrate(self, f): + self.f = self.SPACE() + self.FEEDRATE() + self.ffmt.string(f) + self.fhv = False + + def feedrate_hv(self, fh, fv): + self.fh = fh + self.fv = fv + self.fhv = True + + def calc_feedrate_hv(self, h, v): + if math.fabs(v) > math.fabs(h * 2): + # some horizontal, so it should be fine to use the horizontal feed rate + self.f.set(self.fv) + else: + # not much, if any horizontal component, so use the vertical feed rate + self.f.set(self.fh) + + def spindle(self, s, clockwise): + if clockwise == True: + self.s.set(s, self.SPACE() + self.SPINDLE_CW(), self.SPACE() + self.SPINDLE_CCW()) + else: + self.s.set(s, self.SPACE() + self.SPINDLE_CCW(), self.SPACE() + self.SPINDLE_CW()) + + def coolant(self, mode=0): + if (mode <= 0) : self.m.append(self.SPACE() + self.COOLANT_OFF()) + elif (mode == 1) : self.m.append(self.SPACE() + self.COOLANT_MIST()) + elif (mode == 2) : self.m.append(self.SPACE() + self.COOLANT_FLOOD()) + + def gearrange(self, gear=0): + if (gear <= 0) : self.m.append(self.SPACE() + self.GEAR_OFF()) + elif (gear <= 4) : self.m.append(self.SPACE() + self.GEAR() % (gear + GEAR_BASE())) + + ############################################################################ + ## Moves + + def rapid(self, x=None, y=None, z=None, a=None, b=None, c=None ): + self.write_blocknum() + + if self.g0123_modal: + if self.prev_g0123 != self.RAPID(): + self.write(self.SPACE() + self.RAPID()) + self.prev_g0123 = self.RAPID() + else: + self.write(self.SPACE() + self.RAPID()) + self.write_preps() + if (x != None): + dx = x - self.x + if (self.absolute_flag ): + self.write(self.SPACE() + self.X() + (self.fmt.string(x + self.shift_x))) + else: + self.write(self.SPACE() + self.X() + (self.fmt.string(dx))) + self.x = x + if (y != None): + dy = y - self.y + if (self.absolute_flag ): + self.write(self.SPACE() + self.Y() + (self.fmt.string(y + self.shift_y))) + else: + self.write(self.SPACE() + self.Y() + (self.fmt.string(dy))) + + self.y = y + if (z != None): + dz = z - self.z + if (self.absolute_flag ): + self.write(self.SPACE() + self.Z() + (self.fmt.string(z + self.shift_z))) + else: + self.write(self.SPACE() + self.Z() + (self.fmt.string(dz))) + + self.z = z + + if (a != None): + da = a - self.a + if (self.absolute_flag ): + self.write(self.SPACE() + self.A() + (self.fmt.string(a))) + else: + self.write(self.SPACE() + self.A() + (self.fmt.string(da))) + self.a = a + + if (b != None): + db = b - self.b + if (self.absolute_flag ): + self.write(self.SPACE() + self.B() + (self.fmt.string(b))) + else: + self.write(self.SPACE() + self.B() + (self.fmt.string(db))) + self.b = b + + if (c != None): + dc = c - self.c + if (self.absolute_flag ): + self.write(self.SPACE() + self.C() + (self.fmt.string(c))) + else: + self.write(self.SPACE() + self.C() + (self.fmt.string(dc))) + self.c = c + self.write_spindle() + self.write_misc() + self.write('\n') + + def feed(self, x=None, y=None, z=None, a=None, b=None, c=None): + if self.same_xyz(x, y, z): return + self.write_blocknum() + if self.g0123_modal: + if self.prev_g0123 != self.FEED(): + self.write(self.SPACE() + self.FEED()) + self.prev_g0123 = self.FEED() + else: + self.write(self.FEED()) + self.write_preps() + dx = dy = dz = 0 + if (x != None): + dx = x - self.x + if (self.absolute_flag ): + self.write(self.SPACE() + self.X() + (self.fmt.string(x + self.shift_x))) + else: + self.write(self.SPACE() + self.X() + (self.fmt.string(dx))) + self.x = x + if (y != None): + dy = y - self.y + if (self.absolute_flag ): + self.write(self.SPACE() + self.Y() + (self.fmt.string(y + self.shift_y))) + else: + self.write(self.SPACE() + self.Y() + (self.fmt.string(dy))) + + self.y = y + if (z != None): + dz = z - self.z + if (self.absolute_flag ): + self.write(self.SPACE() + self.Z() + (self.fmt.string(z + self.shift_z))) + else: + self.write(self.SPACE() + self.Z() + (self.fmt.string(dz))) + + self.z = z + if (self.fhv) : self.calc_feedrate_hv(math.sqrt(dx*dx+dy*dy), math.fabs(dz)) + self.write_feedrate() + self.write_spindle() + self.write_misc() + self.write('\n') + + def same_xyz(self, x=None, y=None, z=None): + if (x != None): + if (self.fmt.string(x + self.shift_x)) != (self.fmt.string(self.x)): + return False + if (y != None): + if (self.fmt.string(y + self.shift_y)) != (self.fmt.string(self.y)): + return False + if (z != None): + if (self.fmt.string(z + self.shift_z)) != (self.fmt.string(self.z)): + return False + + return True + + + def get_quadrant(self, dx, dy): + if dx < 0: + if dy < 0: + return 2 + else: + return 1 + + else: + if dy < 0: + return 3 + else: + return 0 + + def quadrant_start(self, q, i, j, rad): + while q > 3: q = q - 4 + if q == 0: + return i + rad, j + if q == 1: + return i, j + rad + if q == 2: + return i - rad, j + return i, j - rad + + def quadrant_end(self, q, i, j, rad): + return self.quadrant_start(q + 1, i, j, rad) + + def get_arc_angle(self, sdx, sdy, edx, edy, cw): + angle_s = math.atan2(sdy, sdx); + angle_e = math.atan2(edy, edx); + if cw: + if angle_s < angle_e: angle_s = angle_s + 2 * math.pi + else: + if angle_e < angle_s: angle_e = angle_e + 2 * math.pi + return angle_e - angle_s + + def arc(self, cw, x=None, y=None, z=None, i=None, j=None, k=None, r=None): + if self.can_do_helical_arcs == False and self.in_quadrant_splitting == False and (z != None) and (math.fabs(z - self.z) > 0.000001) and (self.fmt.string(z) != self.fmt.string(self.z)): + # split the helical arc into little line feed moves + if x == None: x = self.x + if y == None: y = self.y + sdx = self.x - i + sdy = self.y - j + edx = x - i + edy = y - j + radius = math.sqrt(sdx*sdx + sdy*sdy) + arc_angle = self.get_arc_angle(sdx, sdy, edx, edy, cw) + angle_start = math.atan2(sdy, sdx); + tolerance = 0.02 + angle_step = 2.0 * math.atan( math.sqrt ( tolerance /(radius - tolerance) )) + segments = int(math.fabs(arc_angle / angle_step) + 1) + angle_step = arc_angle / segments + angle = angle_start + z_step = float(z - self.z)/segments + next_z = self.z + for p in range(0, segments): + angle = angle + angle_step + next_x = i + radius * math.cos(angle) + next_y = j + radius * math.sin(angle) + next_z = next_z + z_step + self.feed(next_x, next_y, next_z) + return + + if self.arc_centre_positive == True and self.in_quadrant_splitting == False: + # split in to quadrant arcs + self.in_quadrant_splitting = True + + if x == None: x = self.x + if y == None: y = self.y + sdx = self.x - i + sdy = self.y - j + edx = x - i + edy = y - j + + qs = self.get_quadrant(sdx, sdy) + qe = self.get_quadrant(edx, edy) + + if qs == qe: + arc_angle = math.fabs(self.get_arc_angle(sdx, sdy, edx, edy, cw)) + # arc_angle will be either less than pi/2 or greater than 3pi/2 + if arc_angle > 3.14: + if cw: + qs = qs + 4 + else: + qe = qe + 4 + + if qs == qe: + self.arc(cw, x, y, z, i, j, k, r) + else: + rad = math.sqrt(sdx * sdx + sdy * sdy) + if cw: + if qs < qe: qs = qs + 4 + else: + if qe < qs: qe = qe + 4 + + q = qs + while 1: + x1 = x + y1 = y + if q != qe: + if cw: + x1, y1 = self.quadrant_start(q, i, j, rad) + else: + x1, y1 = self.quadrant_end(q, i, j, rad) + + if ((math.fabs(x1 - self.x) > 0.000001) or (math.fabs(y1 - self.y) > 0.000001)) and ((self.fmt.string(x1) != self.fmt.string(self.x)) or (self.fmt.string(y1) != self.fmt.string(self.y))): + self.arc(cw, x1, y1, z, i, j, k, r) + if q == qe: + break + if cw: + q = q - 1 + else: + q = q + 1 + + self.in_quadrant_splitting = False + return + + #if self.same_xyz(x, y, z): return + self.write_blocknum() + arc_g_code = '' + if cw: arc_g_code = self.ARC_CW() + else: arc_g_code = self.ARC_CCW() + if self.g0123_modal: + if self.prev_g0123 != arc_g_code: + self.write(arc_g_code) + self.prev_g0123 = arc_g_code + else: + self.write(arc_g_code) + self.write_preps() + if (x != None): + dx = x - self.x + if (self.absolute_flag ): + self.write(self.SPACE() + self.X() + (self.fmt.string(x + self.shift_x))) + else: + self.write(self.SPACE() + self.X() + (self.fmt.string(dx))) + if (y != None): + dy = y - self.y + if (self.absolute_flag ): + self.write(self.SPACE() + self.Y() + (self.fmt.string(y + self.shift_y))) + else: + self.write(self.SPACE() + self.Y() + (self.fmt.string(dy))) + if (z != None): + dz = z - self.z + if (self.absolute_flag ): + self.write(self.SPACE() + self.Z() + (self.fmt.string(z + self.shift_z))) + else: + self.write(self.SPACE() + self.Z() + (self.fmt.string(dz))) + if (i != None): + if self.arc_centre_absolute == False: + i = i - self.x + s = self.fmt.string(i) + if self.arc_centre_positive == True: + if s[0] == '-': + s = s[1:] + self.write(self.SPACE() + self.CENTRE_X() + s) + if (j != None): + if self.arc_centre_absolute == False: + j = j - self.y + s = self.fmt.string(j) + if self.arc_centre_positive == True: + if s[0] == '-': + s = s[1:] + self.write(self.SPACE() + self.CENTRE_Y() + s) + if (k != None): + if self.arc_centre_absolute == False: + k = k - self.z + s = self.fmt.string(k) + if self.arc_centre_positive == True: + if s[0] == '-': + s = s[1:] + self.write(self.SPACE() + self.CENTRE_Z() + s) + if (r != None): + s = self.fmt.string(r) + if self.arc_centre_positive == True: + if s[0] == '-': + s = s[1:] + self.write(self.SPACE() + self.RADIUS() + s) +# use horizontal feed rate + if (self.fhv) : self.calc_feedrate_hv(1, 0) + self.write_feedrate() + self.write_spindle() + self.write_misc() + self.write('\n') + if (x != None): + self.x = x + if (y != None): + self.y = y + if (z != None): + self.z = z + + def arc_cw(self, x=None, y=None, z=None, i=None, j=None, k=None, r=None): + self.arc(True, x, y, z, i, j, k, r) + + def arc_ccw(self, x=None, y=None, z=None, i=None, j=None, k=None, r=None): + self.arc(False, x, y, z, i, j, k, r) + + def dwell(self, t): + self.write_blocknum() + self.write_preps() + self.write(self.FORMAT_DWELL() % t) + self.write_misc() + self.write('\n') + + def rapid_home(self, x=None, y=None, z=None, a=None, b=None, c=None): + pass + + def rapid_unhome(self): + pass + + def set_machine_coordinates(self): + self.write(self.SPACE() + self.MACHINE_COORDINATES()) + self.prev_g0123 = '' + + ############################################################################ + ## CRC + + def use_CRC(self): + return self.useCrc + + def CRC_nominal_path(self): + return self.useCrcCenterline + + def start_CRC(self, left = True, radius = 0.0): + # set up prep code, to be output on next line + if self.t == None: + raise "No tool specified for start_CRC()" + self.write_blocknum() + if left: + self.write(self.SPACE() + 'G41') + else: + self.write(self.SPACE() + 'G42') + self.write((self.SPACE() + 'D%i\n') % self.t) + + def end_CRC(self): + self.write_blocknum() + self.write(self.SPACE() + 'G40\n') + + ############################################################################ + ## Cycles + + def pattern(self): + pass + + def pocket(self): + pass + + def profile(self): + pass + + # The drill routine supports drilling (G81), drilling with dwell (G82) and peck drilling (G83). + # The x,y,z values are INITIAL locations (above the hole to be made. This is in contrast to + # the Z value used in the G8[1-3] cycles where the Z value is that of the BOTTOM of the hole. + # Instead, this routine combines the Z value and the depth value to determine the bottom of + # the hole. + # + # The standoff value is the distance up from the 'z' value (normally just above the surface) where the bit retracts + # to in order to clear the swarf. This combines with 'z' to form the 'R' value in the G8[1-3] cycles. + # + # The peck_depth value is the incremental depth (Q value) that tells the peck drilling + # cycle how deep to go on each peck until the full depth is achieved. + # + # NOTE: This routine forces the mode to absolute mode so that the values passed into + # the G8[1-3] cycles make sense. I don't know how to find the mode to revert it so I won't + # revert it. I must set the mode so that I can be sure the values I'm passing in make + # sense to the end-machine. + # + def drill(self, x=None, y=None, dwell=None, depthparams = None, retract_mode=None, spindle_mode=None, internal_coolant_on=None, rapid_to_clearance = None): + if (standoff == None): + # This is a bad thing. All the drilling cycles need a retraction (and starting) height. + return + + if (z == None): + return # We need a Z value as well. This input parameter represents the top of the hole + + if self.drillExpanded: + # for machines which don't understand G81, G82 etc. + if peck_depth == None: + peck_depth = depth + current_z = z + self.rapid(x, y) + + first = True + + while True: + next_z = current_z - peck_depth + if next_z < z - depth: + next_z = z - depth + if next_z >= current_z: + break; + if first: + self.rapid(z = z + standoff) + else: + self.rapid(z = current_z) + self.feed(z = next_z) + self.rapid(z = z + standoff) + current_z = next_z + if dwell: + self.dwell(dwell) + first = False + + # we should pass clearance height into here, but my machine is on and I'm in a hurry... 22nd June 2011 danheeks + self.rapid(z = z + 5.0) + + return + + self.write_preps() + self.write_blocknum() + + if (peck_depth != 0): + # We're pecking. Let's find a tree. + if self.drill_modal: + if self.PECK_DRILL() + self.PECK_DEPTH(self.fmt, peck_depth) != self.prev_drill: + self.write(self.SPACE() + self.PECK_DRILL() + self.SPACE() + self.PECK_DEPTH(self.fmt, peck_depth)) + self.prev_drill = self.PECK_DRILL() + self.PECK_DEPTH(self.fmt, peck_depth) + else: + self.write(self.PECK_DRILL() + self.PECK_DEPTH(self.fmt, peck_depth)) + + else: + # We're either just drilling or drilling with dwell. + if (dwell == 0): + # We're just drilling. + if self.drill_modal: + if self.DRILL() != self.prev_drill: + self.write(self.SPACE() + self.DRILL()) + self.prev_drill = self.DRILL() + else: + self.write(self.SPACE() + self.DRILL()) + + else: + # We're drilling with dwell. + + if self.drill_modal: + if self.DRILL_WITH_DWELL(self.FORMAT_DWELL(),dwell) != self.prev_drill: + self.write(self.SPACE() + self.DRILL_WITH_DWELL(self.FORMAT_DWELL(),dwell)) + self.prev_drill = self.DRILL_WITH_DWELL(self.FORMAT_DWELL(),dwell) + else: + self.write(self.SPACE() + self.DRILL_WITH_DWELL(self.FORMAT_DWELL(),dwell)) + + + #self.write(self.DRILL_WITH_DWELL(self.FORMAT_DWELL(),dwell)) + + # Set the retraction point to the 'standoff' distance above the starting z height. + retract_height = z + standoff + if (x != None): + dx = x - self.x + self.write(self.SPACE() + self.X() + (self.fmt.string(x + self.shift_x))) + self.x = x + + if (y != None): + dy = y - self.y + self.write(self.SPACE() + self.Y() + (self.fmt.string(y + self.shift_y))) + self.y = y + + dz = (z + standoff) - self.z # In the end, we will be standoff distance above the z value passed in. + + if self.drill_modal: + if z != self.prev_z: + self.write(self.SPACE() + self.Z() + (self.fmt.string(z - depth))) + self.prev_z=z + else: + self.write(self.SPACE() + self.Z() + (self.fmt.string(z - depth))) # This is the 'z' value for the bottom of the hole. + self.z = (z + standoff) # We want to remember where z is at the end (at the top of the hole) + + if self.drill_modal: + if self.prev_retract != self.RETRACT(self.fmt, retract_height) : + self.write(self.SPACE() + self.RETRACT(self.fmt, retract_height)) + self.prev_retract = self.RETRACT(self.fmt, retract_height) + else: + self.write(self.SPACE() + self.RETRACT(self.fmt, retract_height)) + + if (self.fhv) : + self.calc_feedrate_hv(math.sqrt(dx*dx+dy*dy), math.fabs(dz)) + + self.write_feedrate() + self.write_spindle() + self.write_misc() + self.write('\n') + + # G33.1 tapping with EMC for now + # unsynchronized (chuck) taps NIY (tap_mode = 1) + + def tap(self, x=None, y=None, z=None, zretract=None, depth=None, standoff=None, dwell_bottom=None, pitch=None, stoppos=None, spin_in=None, spin_out=None, tap_mode=None, direction=None): + # mystery parameters: + # zretract=None, dwell_bottom=None,pitch=None, stoppos=None, spin_in=None, spin_out=None): + # I dont see how to map these to EMC Gcode + + if (standoff == None): + # This is a bad thing. All the drilling cycles need a retraction (and starting) height. + return + if (z == None): + return # We need a Z value as well. This input parameter represents the top of the hole + if (pitch == None): + return # We need a pitch value. + if (direction == None): + return # We need a direction value. + + if (tap_mode != 0): + raise "only rigid tapping currently supported" + + self.write_preps() + self.write_blocknum() + self.write_spindle() + self.write('\n') + + # rapid to starting point; z first, then x,y iff given + + # Set the retraction point to the 'standoff' distance above the starting z height. + retract_height = z + standoff + + # unsure if this is needed: + if self.z != retract_height: + self.rapid(z = retract_height) + + # then continue to x,y if given + if (x != None) or (y != None): + self.write_blocknum() + self.write(self.RAPID() ) + + if (x != None): + self.write(self.X() + self.fmt.string(x + self.shift_x)) + self.x = x + + if (y != None): + self.write(self.Y() + self.fmt.string(y + self.shift_y)) + self.y = y + self.write('\n') + + self.write_blocknum() + self.write( self.TAP() ) + self.write( self.TAP_DEPTH(self.ffmt,pitch) + self.SPACE() ) + self.write(self.Z() + self.fmt.string(z - depth))# This is the 'z' value for the bottom of the tap. + self.write_misc() + self.write('\n') + + self.z = retract_height # this cycle returns to the start position, so remember that as z value + + def bore(self, x=None, y=None, z=None, zretract=None, depth=None, standoff=None, dwell_bottom=None, feed_in=None, feed_out=None, stoppos=None, shift_back=None, shift_right=None, backbore=False, stop=False): + pass + + def end_canned_cycle(self): + if self.drillExpanded: + return + self.write_blocknum() + self.write(self.SPACE() + self.END_CANNED_CYCLE() + '\n') + self.prev_drill = '' + self.prev_g0123 = '' + self.prev_z = '' + self.prev_f = '' + self.prev_retract = '' + ############################################################################ + ## Misc + + def comment(self, text): + self.write((self.COMMENT(text) + '\n')) + + def insert(self, text): + pass + + def block_delete(self, on=False): + pass + + def variable(self, id): + return (self.VARIABLE() % id) + + def variable_set(self, id, value): + self.write_blocknum() + self.write(self.SPACE() + (self.VARIABLE() % id) + self.SPACE() + (self.VARIABLE_SET() % value) + '\n') + + # This routine uses the G92 coordinate system offsets to establish a temporary coordinate + # system at the machine's current position. It can then use absolute coordinates relative + # to this position which makes coding easy. It then moves to the 'point along edge' which + # should be above the workpiece but still on one edge. It then backs off from the edge + # to the 'retracted point'. It then plunges down by the depth value specified. It then + # probes back towards the 'destination point'. The probed X,Y location are stored + # into the 'intersection variable' variables. Finally the machine moves back to the + # original location. This is important so that the results of multiple calls to this + # routine may be compared meaningfully. + def probe_single_point(self, point_along_edge_x=None, point_along_edge_y=None, depth=None, retracted_point_x=None, retracted_point_y=None, destination_point_x=None, destination_point_y=None, intersection_variable_x=None, intersection_variable_y=None, probe_offset_x_component=None, probe_offset_y_component=None ): + self.write_blocknum() + self.write(self.SPACE() + (self.SET_TEMPORARY_COORDINATE_SYSTEM() + (' X 0 Y 0 Z 0') + ('\t(Temporarily make this the origin)\n'))) + + if (self.fhv) : self.calc_feedrate_hv(1, 0) + self.write_blocknum() + self.write_feedrate() + self.write('\t(Set the feed rate for probing)\n') + + self.rapid(point_along_edge_x,point_along_edge_y) + self.rapid(retracted_point_x,retracted_point_y) + self.feed(z=depth) + + self.write_blocknum() + self.write((self.PROBE_TOWARDS_WITH_SIGNAL() + (' X ' + (self.fmt.string(destination_point_x)) + ' Y ' + (self.fmt.string(destination_point_y)) ) + ('\t(Probe towards our destination point)\n'))) + + self.comment('Back off the workpiece and re-probe more slowly') + self.write_blocknum() + self.write(self.SPACE() + ('#' + intersection_variable_x + '= [#5061 - [ 0.5 * ' + probe_offset_x_component + ']]\n')) + self.write_blocknum() + self.write(self.SPACE() + ('#' + intersection_variable_y + '= [#5062 - [ 0.5 * ' + probe_offset_y_component + ']]\n')) + self.write_blocknum(); + self.write(self.RAPID()) + self.write(self.SPACE() + ' X #' + intersection_variable_x + ' Y #' + intersection_variable_y + '\n') + + self.write_blocknum() + self.write(self.SPACE() + self.FEEDRATE() + self.ffmt.string(self.fh / 2.0) + '\n') + + self.write_blocknum() + self.write((self.SPACE() + self.PROBE_TOWARDS_WITH_SIGNAL() + (' X ' + (self.fmt.string(destination_point_x)) + ' Y ' + (self.fmt.string(destination_point_y)) ) + ('\t(Probe towards our destination point)\n'))) + + self.comment('Store the probed location somewhere we can get it again later') + self.write_blocknum() + self.write(('#' + intersection_variable_x + '=' + probe_offset_x_component + ' (Portion of probe radius that contributes to the X coordinate)\n')) + self.write_blocknum() + self.write(('#' + intersection_variable_x + '=[#' + intersection_variable_x + ' + #5061]\n')) + self.write_blocknum() + self.write(('#' + intersection_variable_y + '=' + probe_offset_y_component + ' (Portion of probe radius that contributes to the Y coordinate)\n')) + self.write_blocknum() + self.write(('#' + intersection_variable_y + '=[#' + intersection_variable_y + ' + #5062]\n')) + + self.comment('Now move back to the original location') + self.rapid(retracted_point_x,retracted_point_y) + self.rapid(z=0) + self.rapid(point_along_edge_x,point_along_edge_y) + self.rapid(x=0, y=0) + + self.write_blocknum() + self.write((self.REMOVE_TEMPORARY_COORDINATE_SYSTEM() + ('\t(Restore the previous coordinate system)\n'))) + + def probe_downward_point(self, x=None, y=None, depth=None, intersection_variable_z=None): + self.write_blocknum() + self.write((self.SET_TEMPORARY_COORDINATE_SYSTEM() + (' X 0 Y 0 Z 0') + ('\t(Temporarily make this the origin)\n'))) + if (self.fhv) : self.calc_feedrate_hv(1, 0) + self.write_blocknum() + self.write(self.FEEDRATE() + ' [' + self.ffmt.string(self.fh) + ' / 5.0 ]') + self.write('\t(Set the feed rate for probing)\n') + + if x != None and y != None: + self.write_blocknum(); + self.write(self.RAPID()) + self.write(' X ' + x + ' Y ' + y + '\n') + + self.write_blocknum() + self.write((self.PROBE_TOWARDS_WITH_SIGNAL() + ' Z ' + (self.fmt.string(depth)) + ('\t(Probe towards our destination point)\n'))) + + self.comment('Store the probed location somewhere we can get it again later') + self.write_blocknum() + self.write(('#' + intersection_variable_z + '= #5063\n')) + + self.comment('Now move back to the original location') + self.rapid(z=0) + self.rapid(x=0, y=0) + + self.write_blocknum() + self.write((self.REMOVE_TEMPORARY_COORDINATE_SYSTEM() + ('\t(Restore the previous coordinate system)\n'))) + + + def report_probe_results(self, x1=None, y1=None, z1=None, x2=None, y2=None, z2=None, x3=None, y3=None, z3=None, x4=None, y4=None, z4=None, x5=None, y5=None, z5=None, x6=None, y6=None, z6=None, xml_file_name=None ): + pass + + def open_log_file(self, xml_file_name=None ): + pass + + def log_coordinate(self, x=None, y=None, z=None): + pass + + def log_message(self, message=None): + pass + + def close_log_file(self): + pass + + # Rapid movement to the midpoint between the two points specified. + # NOTE: The points are specified either as strings representing numbers or as strings + # representing variable names. This allows the HeeksCNC module to determine which + # variable names are used in these various routines. + def rapid_to_midpoint(self, x1=None, y1=None, z1=None, x2=None, y2=None, z2=None): + self.write_blocknum() + self.write(self.RAPID()) + if ((x1 != None) and (x2 != None)): + self.write((' X ' + '[[[' + x1 + ' - ' + x2 + '] / 2.0] + ' + x2 + ']')) + + if ((y1 != None) and (y2 != None)): + self.write((' Y ' + '[[[' + y1 + ' - ' + y2 + '] / 2.0] + ' + y2 + ']')) + + if ((z1 != None) and (z2 != None)): + self.write((' Z ' + '[[[' + z1 + ' - ' + z2 + '] / 2.0] + ' + z2 + ']')) + + self.write('\n') + + # Rapid movement to the intersection of two lines (in the XY plane only). This routine + # is based on information found in http://local.wasp.uwa.edu.au/~pbourke/geometry/lineline2d/ + # written by Paul Bourke. The ua_numerator, ua_denominator, ua and ub parameters + # represent variable names (with the preceding '#' included in them) for use as temporary + # variables. They're specified here simply so that HeeksCNC can manage which variables + # are used in which GCode calculations. + # + # As per the notes on the web page, the ua_denominator and ub_denominator formulae are + # the same so we don't repeat this. If the two lines are coincident or parallel then + # no movement occurs. + # + # NOTE: The points are specified either as strings representing numbers or as strings + # representing variable names. This allows the HeeksCNC module to determine which + # variable names are used in these various routines. + def rapid_to_intersection(self, x1, y1, x2, y2, x3, y3, x4, y4, intersection_x, intersection_y, ua_numerator, ua_denominator, ua, ub_numerator, ub): + self.comment('Find the intersection of the two lines made up by the four probed points') + self.write_blocknum(); + self.write(ua_numerator + '=[[[' + x4 + ' - ' + x3 + '] * [' + y1 + ' - ' + y3 + ']] - [[' + y4 + ' - ' + y3 + '] * [' + x1 + ' - ' + x3 + ']]]\n') + self.write_blocknum(); + self.write(ua_denominator + '=[[[' + y4 + ' - ' + y3 + '] * [' + x2 + ' - ' + x1 + ']] - [[' + x4 + ' - ' + x3 + '] * [' + y2 + ' - ' + y1 + ']]]\n') + self.write_blocknum(); + self.write(ub_numerator + '=[[[' + x2 + ' - ' + x1 + '] * [' + y1 + ' - ' + y3 + ']] - [[' + y2 + ' - ' + y1 + '] * [' + x1 + ' - ' + x3 + ']]]\n') + + self.comment('If they are not parallel') + self.write('O900 IF [' + ua_denominator + ' NE 0]\n') + self.comment('And if they are not coincident') + self.write('O901 IF [' + ua_numerator + ' NE 0 ]\n') + + self.write_blocknum(); + self.write(' ' + ua + '=[' + ua_numerator + ' / ' + ua_denominator + ']\n') + self.write_blocknum(); + self.write(' ' + ub + '=[' + ub_numerator + ' / ' + ua_denominator + ']\n') # NOTE: ub denominator is the same as ua denominator + self.write_blocknum(); + self.write(' ' + intersection_x + '=[' + x1 + ' + [[' + ua + ' * [' + x2 + ' - ' + x1 + ']]]]\n') + self.write_blocknum(); + self.write(' ' + intersection_y + '=[' + y1 + ' + [[' + ua + ' * [' + y2 + ' - ' + y1 + ']]]]\n') + self.write_blocknum(); + self.write(' ' + self.RAPID()) + self.write(' X ' + intersection_x + ' Y ' + intersection_y + '\n') + + self.write('O901 ENDIF\n') + self.write('O900 ENDIF\n') + + # We need to calculate the rotation angle based on the line formed by the + # x1,y1 and x2,y2 coordinate pair. With that angle, we need to move + # x_offset and y_offset distance from the current (0,0,0) position. + # + # The x1,y1,x2 and y2 parameters are all variable names that contain the actual + # values. + # The x_offset and y_offset are both numeric (floating point) values + def rapid_to_rotated_coordinate(self, x1, y1, x2, y2, ref_x, ref_y, x_current, y_current, x_final, y_final): + self.comment('Rapid to rotated coordinate') + self.write_blocknum(); + self.write( '#1 = [atan[' + y2 + ' - ' + y1 + ']/[' + x2 +' - ' + x1 + ']] (nominal_angle)\n') + self.write_blocknum(); + self.write( '#2 = [atan[' + ref_y + ']/[' + ref_x + ']] (reference angle)\n') + self.write_blocknum(); + self.write( '#3 = [#1 - #2] (angle)\n' ) + self.write_blocknum(); + self.write( '#4 = [[[' + (self.fmt.string(0)) + ' - ' + (self.fmt.string(x_current)) + '] * COS[ #3 ]] - [[' + (self.fmt.string(0)) + ' - ' + (self.fmt.string(y_current)) + '] * SIN[ #3 ]]]\n' ) + self.write_blocknum(); + self.write( '#5 = [[[' + (self.fmt.string(0)) + ' - ' + (self.fmt.string(x_current)) + '] * SIN[ #3 ]] + [[' + (self.fmt.string(0)) + ' - ' + (self.fmt.string(y_current)) + '] * COS[ #3 ]]]\n' ) + + self.write_blocknum(); + self.write( '#6 = [[' + (self.fmt.string(x_final)) + ' * COS[ #3 ]] - [' + (self.fmt.string(y_final)) + ' * SIN[ #3 ]]]\n' ) + self.write_blocknum(); + self.write( '#7 = [[' + (self.fmt.string(y_final)) + ' * SIN[ #3 ]] + [' + (self.fmt.string(y_final)) + ' * COS[ #3 ]]]\n' ) + + self.write_blocknum(); + self.write( self.RAPID() + ' X [ #4 + #6 ] Y [ #5 + #7 ]\n' ) + + def BEST_POSSIBLE_SPEED(self, motion_blending_tolerance, naive_cam_tolerance): + statement = 'G64' + + if (motion_blending_tolerance > 0): + statement += ' P ' + str(motion_blending_tolerance) + + if (naive_cam_tolerance > 0): + statement += ' Q ' + str(naive_cam_tolerance) + + return(statement) + + def set_path_control_mode(self, mode, motion_blending_tolerance, naive_cam_tolerance ): + self.write_blocknum() + if (mode == 0): + self.write( self.EXACT_PATH_MODE() + '\n' ) + if (mode == 1): + self.write( self.EXACT_STOP_MODE() + '\n' ) + if (mode == 2): + self.write( self.BEST_POSSIBLE_SPEED( motion_blending_tolerance, naive_cam_tolerance ) + '\n' ) + + +################################################################################ + +nc.creator = Creator() diff --git a/src/Mod/Path/PathScripts/nc/heiden_read.py b/src/Mod/Path/PathScripts/nc/heiden_read.py new file mode 100644 index 0000000000..fc76cd34e4 --- /dev/null +++ b/src/Mod/Path/PathScripts/nc/heiden_read.py @@ -0,0 +1,152 @@ +################################################################################ +# heiden_read.py +# +# Simple ISO NC code parsing +# + +import nc_read as nc +import re +import sys +import math + +################################################################################ +class Parser(nc.Parser): + + def __init__(self, writer): + nc.Parser.__init__(self, writer) + + self.pattern_main = re.compile('([(!;].*' + '|\s+|[a-zA-Z0-9_:](?:[+-])?\d*(?:\.\d*)?' + '|\w\#\d+|\(.*?\)' + '|\#\d+\=(?:[+-])?\d*(?:\.\d*)? )') + self.pattern_tool = re.compile('([(!;].*' + '|\S+' + '|\s+|\d)') + self.oldx = 0 + self.oldy = 0 + self.oldz = 150 + self.olda = 0 + self.oldb = 0 + self.oldc = 0 + + def ParseTool(self, word): + # parse the first numeric parameter that comes after 'tool call' + try: + if (word[0:] == 'TOOL'): + self.col = "tool" + self.move = False + elif (word[0:] == 'CALL'): + self.col = "tool call" + self.move = False + elif self.col == 'tool call': + if not self.t > 0: + if word[0] >= '0' and word[0] <= '9': + self.t = eval(word[0:]) + self.col = 'tool no' + except: + pass + + def change_tool(self, t): + pass + + def ParseWord(self, word): + try: + if (word[0] == 'A' or word[0] == 'a'): + self.col = "axis" + self.a = eval(word[1:]) + self.move = True + elif (word[0] == 'B' or word[0] == 'b'): + self.col = "axis" + self.b = eval(word[1:]) + self.move = True + elif (word[0] == 'C' or word[0] == 'c'): + self.col = "axis" + self.c = eval(word[1:]) + self.move = True + elif (word[0] == 'F' or word[0] == 'f'): + self.col = "axis" + if word[1:] == 'FMAX': + self.rapid = True + self.path_col = "rapid" + self.col = "rapid" + else: + self.f = eval(word[1:]) + self.rapid = False + self.path_col = "feed" + self.col = "feed" + self.move = True + elif (word == 'L' or word == 'l'): + self.arc = 0 + self.move = True + elif (word[0] == 'X' or word[0] == 'x'): + self.col = "axis" + self.x = eval(word[1:]) + self.move = True + elif (word[0] == 'Y' or word[0] == 'y'): + self.col = "axis" + self.y = eval(word[1:]) + self.move = True + elif (word[0] == 'Z' or word[0] == 'z'): + self.col = "axis" + self.z = eval(word[1:]) + self.move = True + self.t = eval(word[1:]) + except: + pass + + def Parsey(self, name): + self.files_open(name) + + for_full_machine_sim = True # to do, make derived class to do this + #for_full_machine_sim = False + + self.f = None + self.arc = 0 + self.rapid = True + + while (self.readline()): + + self.a = None + self.b = None + self.c = None + self.h = None + self.i = None + self.j = None + self.k = None + self.p = None + self.q = None + self.r = None + self.s = None + self.x = None + self.y = None + self.z = None + self.t = 0 + + self.move = False + self.no_move = False + + words = self.pattern_tool.findall(self.line) + for word in words: + self.ParseTool(word) + + if not self.t: + words = self.pattern_main.findall(self.line) + for word in words: + self.ParseWord(word) + + if (self.move and not self.no_move): + if (self.arc==0): + self.add_line(self.x, self.y, self.z, self.a, self.b, self.rapid) + else: + self.add_arc(self.x, self.y, self.z, self.i, self.j, self.k, self.r, self.arc) + if self.x != None: self.oldx = self.x + if self.y != None: self.oldy = self.y + if self.z != None: self.oldz = self.z + if self.a != None: self.olda = self.a + if self.b != None: self.oldb = self.b + if self.c != None: self.oldc = self.c + + elif (self.t): + self.change_tool(self.t) + + self.files_close() diff --git a/src/Mod/Path/PathScripts/nc/hm50.py b/src/Mod/Path/PathScripts/nc/hm50.py new file mode 100755 index 0000000000..2eb30b7a08 --- /dev/null +++ b/src/Mod/Path/PathScripts/nc/hm50.py @@ -0,0 +1,24 @@ +import nc +import emc2 + +class Creator(emc2.Creator): + def init(self): + iso.Creator.init(self) + + def program_begin(self, id, comment): + self.write( ('(' + comment + ')' + '\n') ) + + def tool_change(self, id): + self.write_blocknum() + self.write('G53 G00 Z30\n') + self.write_blocknum() + self.write((self.TOOL() % id) + '\n') + self.write_blocknum() + self.write('G01 Z100.000 F800.000\n') + self.write_blocknum() + self.write('M0\n') + self.write_blocknum() + self.write('G01 Z10.000 F300.000\n') + + +nc.creator = Creator() diff --git a/src/Mod/Path/PathScripts/nc/hpgl2d.py b/src/Mod/Path/PathScripts/nc/hpgl2d.py new file mode 100644 index 0000000000..cab6fadd44 --- /dev/null +++ b/src/Mod/Path/PathScripts/nc/hpgl2d.py @@ -0,0 +1,109 @@ +# hpgl2d.py +# +# Copyright (c) 2009, Dan Heeks +# This program is released under the BSD license. See the file COPYING for details. +# + +import nc +import math + +class Creator(nc.Creator): + def __init__(self): + nc.Creator.__init__(self) + self.x = int(0) + self.y = int(0) # these are in machine units, like 0.01mm or maybe 0.25mm + self.metric() # set self.units_to_mc_units + + def imperial(self): + self.units_to_mc_units = 2540 # multiplier from inches to machine units + + def metric(self): + self.units_to_mc_units = 100 # multiplier from mm to machine units + + def program_begin(self, id, name=''): + self.write('IN;\n') + self.write('VS32,1;\n') + self.write('VS32,2;\n') + self.write('VS32,3;\n') + self.write('VS32,4;\n') + self.write('VS32,5;\n') + self.write('VS32,6;\n') + self.write('VS32,7;\n') + self.write('VS32,8;\n') + self.write('WU0;\n') + self.write('PW0.349,1;\n') + self.write('PW0.349,2;\n') + self.write('PW0.349,3;\n') + self.write('PW0.349,4;\n') + self.write('PW0.349,5;\n') + self.write('PW0.349,6;\n') + self.write('PW0.349,7;\n') + self.write('PW0.349,8;\n') + self.write('SP1;\n') + + def program_end(self): + self.write('SP0;\n') + + def closest_int(self, f): + if math.fabs(f) < 0.3: + return 0 + elif f > 0: + return int(f + 0.5) + else: + return int(f - 0.5) + + def get_machine_x_y(self, x=None, y=None): + machine_x = self.x + machine_y = self.y + if x != None: + machine_x = self.closest_int(x * self.units_to_mc_units) + if y != None: + machine_y = self.closest_int(y * self.units_to_mc_units) + return machine_x, machine_y + + def rapid(self, x=None, y=None, z=None, a=None, b=None, c=None): + # ignore the z, any rapid will be assumed to be done with the pen up + mx, my = self.get_machine_x_y(x, y) + if mx != self.x or my != self.y: + self.write(('PU%i' % mx) + (' %i;\n' % my)) + self.x = mx + self.y = my + + def feed(self, x=None, y=None, z=None, a=None, b=None, c=None): + # ignore the z, any feed will be assumed to be done with the pen down + mx, my = self.get_machine_x_y(x, y) + if mx != self.x or my != self.y: + self.write(('PD%i' % mx) + (' %i;\n' % my)) + self.x = mx + self.y = my + + def arc(self, cw, x=None, y=None, z=None, i=None, j=None, k=None, r=None): + mx, my = self.get_machine_x_y(x, y) + if mx != self.x or my != self.y: + cx = float(self.x) / self.units_to_mc_units + i + cy = float(self.y) / self.units_to_mc_units + j + sdx = -i + sdy = -j + edx = x - cx + edy = y - cy + start_angle = math.atan2(sdy, sdx) + end_angle = math.atan2(edy, edx) + if cw: + if start_angle < end_angle: start_angle += 2 * math.pi + else: + if end_angle < start_angle: end_angle += 2 * math.pi + + a = math.fabs(end_angle - start_angle) + if cw: a = -a + + mcx, mcy = self.get_machine_x_y(cx, cy) + + self.write(('AA%i' % mcx) + (',%i' % mcy) + (',%d;\n' % (a * 180 / math.pi))) + + def arc_cw(self, x=None, y=None, z=None, i=None, j=None, k=None, r=None): + self.arc(True, x, y, z, i, j, k, r) + + def arc_ccw(self, x=None, y=None, z=None, i=None, j=None, k=None, r=None): + self.arc(False, x, y, z, i, j, k, r) + +nc.creator = Creator() diff --git a/src/Mod/Path/PathScripts/nc/hpgl2d_read.py b/src/Mod/Path/PathScripts/nc/hpgl2d_read.py new file mode 100644 index 0000000000..ef052aba4d --- /dev/null +++ b/src/Mod/Path/PathScripts/nc/hpgl2d_read.py @@ -0,0 +1,90 @@ +import num_reader +import sys +import math + +class Parser(num_reader.NumReader): + + def __init__(self, writer): + num_reader.NumReader.__init__(self, writer) + self.i = 0 + self.j = 0 + self.x = 0 + self.y = 0 + self.down_z = 0 + self.up_z = 20 + self.up = True + self.units_to_mm = 0.01 + + def ParsePuOrPd(self, up): + self.line_index = self.line_index + 1 + x = self.get_number() + if len(x) > 0: + y = self.get_number() + if len(y) > 0: + if up: color = "rapid" + else: color = "feed" + self.add_word(color) + self.writer.begin_path(color) + if up: z = self.up_z + else: z = self.down_z + if self.up != up: + self.writer.add_line(self.x * self.units_to_mm, self.y * self.units_to_mm, z) + self.writer.add_line(int(x) * self.units_to_mm, int(y) * self.units_to_mm, z) + self.writer.end_path() + self.up = up + self.x = int(x) + self.y = int(y) + + def ParseAA(self): + self.line_index = self.line_index + 1 + cx = self.get_number() + if len(cx) > 0: + cy = self.get_number() + if len(cy) > 0: + a = self.get_number() + if len(a) > 0: + self.add_word("feed") + self.writer.begin_path("feed") + z = self.down_z + if self.up: + self.writer.add_line(self.x * self.units_to_mm, self.y * self.units_to_mm, z) + + sdx = self.x - int(cx) + sdy = self.y - int(cy) + + start_angle = math.atan2(sdy, sdx) + + end_angle = start_angle + int(a) * math.pi/180 + + radius = math.sqrt(sdx*sdx + sdy*sdy) + + ex = int(cx) + radius * math.cos(end_angle) + ey = int(cy) + radius * math.sin(end_angle) + + if int(a) > 0: d = 1 + else: d = -1 + + self.writer.add_arc(ex * self.units_to_mm, ey * self.units_to_mm, 0.0, i = int(-sdx) * self.units_to_mm, j = int(-sdy) * self.units_to_mm, d = d) + self.writer.end_path() + self.up = False + self.x = int(ex) + self.y = int(ey) + + def ParseFromFirstLetter(self, c): + if c == 'P': + self.line_index = self.line_index + 1 + if self.line_index < self.line_length: + c1 = self.line[self.line_index] + self.parse_word += c1 + if c1 == 'U': # PU + self.ParsePuOrPd(True) + elif c1 == 'D': # PD + self.ParsePuOrPd(False) + elif c == 'A': + self.line_index = self.line_index + 1 + if self.line_index < self.line_length: + c1 = self.line[self.line_index] + self.parse_word += c1 + if c1 == 'A': # AA, arc absolute + self.ParseAA() + diff --git a/src/Mod/Path/PathScripts/nc/hpgl2dv.py b/src/Mod/Path/PathScripts/nc/hpgl2dv.py new file mode 100644 index 0000000000..79bf022113 --- /dev/null +++ b/src/Mod/Path/PathScripts/nc/hpgl2dv.py @@ -0,0 +1,22 @@ +# hpgl2dv.py +# +# Copyright (c) 2009, Dan Heeks +# This program is released under the BSD license. See the file COPYING for details. +# + +# This is the same as the hpgl2d machine, but uses units of 0.25mm instead of 0.01mm + +import nc +import hpgl2d + +class Creator(hpgl2d.Creator): + def init(self): + hpgl2d.Creator.init(self) + + def imperial(self): + self.units_to_mc_units = 101.6 # multiplier from inches to machine units + + def metric(self): + self.units_to_mc_units = 4 # multiplier from mm to machine units + +nc.creator = Creator() diff --git a/src/Mod/Path/PathScripts/nc/hpgl2dv_read.py b/src/Mod/Path/PathScripts/nc/hpgl2dv_read.py new file mode 100644 index 0000000000..47a1883cd8 --- /dev/null +++ b/src/Mod/Path/PathScripts/nc/hpgl2dv_read.py @@ -0,0 +1,9 @@ +import hpgl2d_read as hpgl +import sys + +# same as hpgl2d, but with 0.25mm units, instead of 0.01mm + +class Parser(hpgl.Parser): + def __init__(self, writer): + hpgl.Parser.__init__(self, writer) + self.units_to_mm = 0.25 diff --git a/src/Mod/Path/PathScripts/nc/hpgl3d.py b/src/Mod/Path/PathScripts/nc/hpgl3d.py new file mode 100644 index 0000000000..d8241b25d9 --- /dev/null +++ b/src/Mod/Path/PathScripts/nc/hpgl3d.py @@ -0,0 +1,64 @@ +# hpgl3d.py +# +# Copyright (c) 2009, Dan Heeks +# This program is released under the BSD license. See the file COPYING for details. +# + +import nc +import hpgl2d +import math + +class Creator(hpgl2d.Creator): + def __init__(self): + hpgl2d.Creator.__init__(self) + self.z = int(0) + self.metric() # set self.units_to_mc_units + self.doing_rapid = True + + def program_begin(self, id, name=''): + self.write(';;^IN;!MC0;\n') + self.write('V50.0;^PR;Z0,0,10500;^PA;\n') + self.write('!RC15;\n') + self.write('!MC1;\n') + + def program_end(self): + self.write('!VZ50.0;!ZM0;\n') + self.write('!MC0;^IN;\n') + + def get_machine_xyz(self, x=None, y=None, z=None): + machine_x = self.x + machine_y = self.y + machine_z = self.z + if x != None: + machine_x = self.closest_int(x * self.units_to_mc_units) + if y != None: + machine_y = self.closest_int(y * self.units_to_mc_units) + if z != None: + machine_z = self.closest_int(z * self.units_to_mc_units) + return machine_x, machine_y, machine_z + + def rapid(self, x=None, y=None, z=None, a=None, b=None, c=None): + # do a rapid move. + # for now, do all rapid moves at V50 ( 50 mm/s ) + mx, my, mz = self.get_machine_xyz(x, y, z) + if mx != self.x or my != self.y or mz != self.z: + if self.doing_rapid == False: self.write('V50.0;') + self.write(('Z%i' % mx) + (',%i' % my) + (',%i;\n' % mz)) + self.x = mx + self.y = my + self.z = mz + self.doing_rapid = True + + def feed(self, x=None, y=None, z=None, a=None, b=None, c=None): + # do a feed move. + # for now, do all feed moves at V10 ( 10 mm/s ) + mx, my, mz = self.get_machine_xyz(x, y, z) + if mx != self.x or my != self.y or mz != self.z: + if self.doing_rapid == True: self.write('V10.0;') + self.write(('Z%i' % mx) + (',%i' % my) + (',%i;\n' % mz)) + self.x = mx + self.y = my + self.z = mz + self.doing_rapid = False + +nc.creator = Creator() diff --git a/src/Mod/Path/PathScripts/nc/hpgl3d_read.py b/src/Mod/Path/PathScripts/nc/hpgl3d_read.py new file mode 100644 index 0000000000..54a21d1a9a --- /dev/null +++ b/src/Mod/Path/PathScripts/nc/hpgl3d_read.py @@ -0,0 +1,45 @@ +import num_reader +import sys +import math + +class Parser(num_reader.NumReader): + + def __init__(self, writer): + num_reader.NumReader.__init__(self, writer) + self.x = 0 + self.y = 0 + self.z = 10000 + self.f = 0 + self.units_to_mm = 0.01 + + def ParseV(self): + self.line_index = self.line_index + 1 + f = self.get_number() + if len(f) > 0: + self.f = float(f) + self.add_word("prep") + + def ParseZ(self): + self.line_index = self.line_index + 1 + x = self.get_number() + if len(x) > 0: + y = self.get_number() + if len(y) > 0: + z = self.get_number() + if len(z) > 0: + if self.f > 40: color = "rapid" + else: color = "feed" + self.add_word(color) + self.writer.begin_path(color) + self.writer.add_line(int(x) * self.units_to_mm, int(y) * self.units_to_mm, int(z) * self.units_to_mm) + self.writer.end_path() + self.x = int(x) + self.y = int(y) + self.z = int(z) + + def ParseFromFirstLetter(self, c): + if c == 'Z': + self.ParseZ() + elif c == 'V': + self.ParseV() + diff --git a/src/Mod/Path/PathScripts/nc/hxml_writer.py b/src/Mod/Path/PathScripts/nc/hxml_writer.py new file mode 100644 index 0000000000..31ae4f2066 --- /dev/null +++ b/src/Mod/Path/PathScripts/nc/hxml_writer.py @@ -0,0 +1,130 @@ +import tempfile + +class HxmlWriter: + def __init__(self): + self.file_out = open(tempfile.gettempdir()+'/backplot.xml', 'w') + self.file_out.write('\n') + self.file_out.write('\n') + self.t = None + self.oldx = None + self.oldy = None + self.oldz = None + + def __del__(self): + self.file_out.write('\n') + self.file_out.close() + + def write(self, s): + self.file_out.write(s) + +############################################ + + def begin_ncblock(self): + self.file_out.write('\t\n') + + def end_ncblock(self): + self.file_out.write('\t\n') + + def add_text(self, s, col, cdata): + s.replace('&', '&') + s.replace('"', '"') + s.replace('<', '<') + s.replace('>', '>') + if (cdata) : (cd1, cd2) = ('') + else : (cd1, cd2) = ('', '') + if (col != None) : self.file_out.write('\t\t'+cd1+s+cd2+'\n') + else : self.file_out.write('\t\t'+cd1+s+cd2+'\n') + + def set_mode(self, units): + self.file_out.write('\t\t\n') + + def metric(self): + self.set_mode(units = 1.0) + + def imperial(self): + self.set_mode(units = 25.4) + + def begin_path(self, col): + if (col != None) : self.file_out.write('\t\t\n') + else : self.file_out.write('\t\t\n') + + def end_path(self): + self.file_out.write('\t\t\n') + + def rapid(self, x=None, y=None, z=None, a=None, b=None, c=None): + self.begin_path("rapid") + self.add_line(x, y, z, a, b, c) + self.end_path() + + def feed(self, x=None, y=None, z=None, a=None, b=None, c=None): + self.begin_path("feed") + self.add_line(x, y, z, a, b, c) + self.end_path() + + def arc_cw(self, x=None, y=None, z=None, i=None, j=None, k=None, r=None): + self.begin_path("feed") + self.add_arc(x, y, z, i, j, k, r, -1) + self.end_path() + + def arc_ccw(self, x=None, y=None, z=None, i=None, j=None, k=None, r=None): + self.begin_path("feed") + self.add_arc(x, y, z, i, j, k, r, 1) + self.end_path() + + def tool_change(self, id): + self.file_out.write('\t\t\n') + self.t = id + + def current_tool(self): + return self.t + + def spindle(self, s, clockwise): + pass + + def feedrate(self, f): + pass + + def add_line(self, x, y, z, a = None, b = None, c = None): + self.file_out.write('\t\t\t\n') + if x != None: self.oldx = x + if y != None: self.oldy = y + if z != None: self.oldz = z + + def add_arc(self, x, y, z, i, j, k, r = None, d = None): + self.file_out.write('\t\t\t\n') + if x != None: self.oldx = x + if y != None: self.oldy = y + if z != None: self.oldz = z diff --git a/src/Mod/Path/PathScripts/nc/iso.py b/src/Mod/Path/PathScripts/nc/iso.py new file mode 100644 index 0000000000..b5bce214f6 --- /dev/null +++ b/src/Mod/Path/PathScripts/nc/iso.py @@ -0,0 +1,1341 @@ +################################################################################ +# iso.py +# +# Simple ISO NC code creator +# +# Hirutso Enni, 2009-01-13 + +import nc +import math +from format import Format +from format import * + +################################################################################ +class Creator(nc.Creator): + + def __init__(self): + nc.Creator.__init__(self) + + # internal variables + self.a = 0 + self.b = 0 + self.c = 0 + self.f = Address('F', fmt = Format(number_of_decimal_places = 2)) + self.fh = None + self.fv = None + self.fhv = False + self.g_plane = Address('G', fmt = Format(number_of_decimal_places = 0)) + self.g_list = [] + self.i = 0 + self.j = 0 + self.k = 0 + self.m = [] + self.r = 0 + self.s = AddressPlusMinus('S', fmt = Format(number_of_decimal_places = 2), modal = False) + self.t = None + self.x = None + self.y = None + self.z = None + self.g0123_modal = False + self.drill_modal = False + self.prev_f = '' + self.prev_g0123 = '' + self.prev_drill = '' + self.prev_retract = '' + self.prev_z = '' + self.useCrc = False + self.useCrcCenterline = False + self.gCRC = '' + self.fmt = Format() + self.absolute_flag = True + self.ffmt = Format(number_of_decimal_places = 2) + self.sfmt = Format(number_of_decimal_places = 1) + self.in_quadrant_splitting = False + self.in_canned_cycle = False + self.first_drill_pos = True + self.shift_x = 0.0 + self.shift_y = 0.0 + self.shift_z = 0.0 + self.start_of_line = False + self.internal_coolant_on = None + self.g98_not_g99 = None # True for G98 ouput, False for G99 output + self.current_fixture = None + self.fixture_wanted = '54' + self.move_done_since_tool_change = False + self.tool_defn_params = {} + self.program_id = None + self.current_sub_id = None + self.subroutine_files = [] + self.program_name = None + self.temp_file_to_append_on_close = None + self.fixture_order = ['54', '55', '56', '57', '58', '59'] + for i in range(1, 50): + self.fixture_order.append('54.' + str(i)) + self.output_disabled = False + self.z_for_g43 = None + + # optional settings + self.arc_centre_absolute = False + self.arc_centre_positive = False + self.drillExpanded = False + self.dwell_allowed_in_G83 = False + self.can_do_helical_arcs = True + self.z_for_g53 = None # set this to a value to output G53 Zvalue in tool change and at program end + self.output_h_and_d_at_tool_change = False + self.output_block_numbers = True + self.start_block_number = 10 + self.block_number_increment = 10 + self.block_number_restart_after = None + self.output_tool_definitions = True + self.output_g43_on_tool_change_line = False + self.output_internal_coolant_commands = False + self.output_g98_and_g99 = True + self.output_g43_z_before_drilling_if_g98 = False + self.output_cutviewer_comments = False + self.output_fixtures = False + self.use_this_program_id = None + self.subroutines_in_own_files = False + self.pattern_done_with_subroutine = False + self.output_comment_before_tool_change = True + self.output_arcs_as_lines = False + self.m_codes_on_their_own_line = False + + ############################################################################ + ## Codes + + def SPACE_STR(self): return '' + def SPACE(self): + if self.start_of_line == True: + self.start_of_line = False + return '' + else: + return self.SPACE_STR() + + def FORMAT_FEEDRATE(self): return('%.2f') + def FORMAT_ANG(self): return('%.1f') + def FORMAT_TIME(self): return self.fmt + + def BLOCK(self): return('N%i') + def COMMENT(self,comment): return( ('(%s)' % comment ) ) + def VARIABLE(self): return( '#%i') + def VARIABLE_SET(self): return( '=%.3f') + + def PROGRAM(self): return( 'O%i') + def PROGRAM_END(self): return( 'M02') + + def SUBPROG_CALL(self): return( 'M98' + self.SPACE() + 'P%i') + def SUBPROG_END(self): return( 'M99') + + def STOP_OPTIONAL(self): return('M01') + def STOP(self): return('M00') + + def IMPERIAL(self): return('G20') + def METRIC(self): return('G21') + def ABSOLUTE(self): return('G90') + def INCREMENTAL(self): return('G91') + def SET_TEMPORARY_COORDINATE_SYSTEM(self): return('G92') + def REMOVE_TEMPORARY_COORDINATE_SYSTEM(self): return('G92.1') + def POLAR_ON(self): return('G16') + def POLAR_OFF(self): return('G15') + def PLANE_XY(self): return('17') + def PLANE_XZ(self): return('18') + def PLANE_YZ(self): return('19') + + def TOOL(self): return('T%i' + self.SPACE() + 'M06') + def TOOL_DEFINITION(self): return('G10' + self.SPACE() + 'L1') + + def WORKPLANE(self): return('G%i') + def WORKPLANE_BASE(self): return(53) + + def SPINDLE_CW(self): return('M03') + def SPINDLE_CCW(self): return('M04') + def COOLANT_OFF(self): return('M09') + def COOLANT_MIST(self): return('M07') + def COOLANT_FLOOD(self): return('M08') + def GEAR_OFF(self): return('?') + def GEAR(self): return('M%i') + def GEAR_BASE(self): return(37) + + def RAPID(self): return('G00') + def FEED(self): return('G01') + def ARC_CW(self): return('G02') + def ARC_CCW(self): return('G03') + def DWELL(self, dwell): return('G04' + self.SPACE() + self.TIME() + (self.FORMAT_TIME().string(dwell))) + def DRILL(self): return('G81') + def DRILL_WITH_DWELL(self, dwell): return('G82' + self.SPACE() + self.TIME() + (self.FORMAT_TIME().string(dwell))) + def PECK_DRILL(self): return('G83') + def PECK_DEPTH(self, depth): return('Q' + (self.fmt.string(depth))) + def RETRACT(self, height): return('R' + (self.fmt.string(height))) + def END_CANNED_CYCLE(self): return('G80') + def TAP(self): return('G84') + def TAP_DEPTH(self, depth): return('K' + (self.fmt.string(depth))) + def INTERNAL_COOLANT_ON(self): return('M18') + def INTERNAL_COOLANT_OFF(self): return('M9') + + def X(self): return('X') + def Y(self): return('Y') + def Z(self): return('Z') + def A(self): return('A') + def B(self): return('B') + def C(self): return('C') + def CENTRE_X(self): return('I') + def CENTRE_Y(self): return('J') + def CENTRE_Z(self): return('K') + def RADIUS(self): return('R') + def TIME(self): return('P') + + def PROBE_TOWARDS_WITH_SIGNAL(self): return('G38.2') + def PROBE_TOWARDS_WITHOUT_SIGNAL(self): return('G38.3') + def PROBE_AWAY_WITH_SIGNAL(self): return('G38.4') + def PROBE_AWAY_WITHOUT_SIGNAL(self): return('G38.5') + + def MACHINE_COORDINATES(self): return('G53') + + def EXACT_PATH_MODE(self): return('G61') + def EXACT_STOP_MODE(self): return('G61.1') + + def RETRACT_TO_CLEARANCE(self): return('G98') + def RETRACT_TO_STANDOFF(self): return('G99') + + ############################################################################ + ## Internals + def write(self, s): + if self.output_disabled == False: + nc.Creator.write(self, s) + if '\n' in s: + self.start_of_line = s[-1] == '\n' + + def write_feedrate(self): + self.write(self.SPACE()) + self.f.write(self) + + def write_preps(self): + i = 0 + if self.g_plane.str: + i += 1 + self.write(self.SPACE()) + self.g_plane.write(self) + for g in self.g_list: + if i > 0: + self.write('\n' if self.m_codes_on_their_own_line else self.SPACE()) + self.write(g) + i += 1 + self.g_list = [] + + def write_misc(self): + if (len(self.m)): + self.write('\n' if self.m_codes_on_their_own_line else self.SPACE()) + self.write(self.m.pop()) + + def write_spindle(self): + if self.s.str: + self.write('\n' if self.m_codes_on_their_own_line else self.SPACE()) + self.s.write(self) + + def output_fixture(self): + if self.current_fixture != self.fixture_wanted: + self.current_fixture = self.fixture_wanted + self.g_list.append('G' + str(self.current_fixture)) + + def increment_fixture(self): + for i in range(0, len(self.fixture_order) - 1): + if self.fixture_order[i] == self.fixture_wanted: + self.fixture_wanted = self.fixture_order[i+1] + return + raise 'too many fixtures wanted!' + + def get_fixture(self): + return self.fixture_wanted + + def set_fixture(self, fixture): + self.fixture_wanted = fixture + + ############################################################################ + ## Programs + + def program_begin(self, id, name=''): + if self.use_this_program_id: + id = self.use_this_program_id + if self.PROGRAM() != None: + self.write((self.PROGRAM() % id) + self.SPACE() + (self.COMMENT(name))) + self.write('\n') + self.program_id = id + self.program_name = name + + def add_stock(self, type_name, params): + if self.output_cutviewer_comments: + self.write("(STOCK/" + type_name) + for param in params: + self.write(",") + self.write(str(param)) + self.write(")\n") + + def program_stop(self, optional=False): + if (optional) : + self.write(self.SPACE() + self.STOP_OPTIONAL() + '\n') + self.prev_g0123 = '' + else : + self.write(self.STOP() + '\n') + self.prev_g0123 = '' + + def number_file(self, filename): + import tempfile + temp_filename = tempfile.gettempdir()+'/renumbering.txt' + + # make a copy of file + f_in = open(filename, 'r') + f_out = open(temp_filename, 'w') + while (True): + line = f_in.readline() + if (len(line) == 0) : break + f_out.write(line) + f_in.close() + f_out.close() + + # read copy + f_in = open(temp_filename, 'r') + f_out = open(filename, 'w') + n = self.start_block_number + while (True): + line = f_in.readline() + + if (len(line) == 0) : break + f_out.write(self.BLOCK() % n + self.SPACE_STR() + line) + n += self.block_number_increment + if self.block_number_restart_after != None: + if n >= self.block_number_restart_after: + n = self.start_block_number + f_in.close() + f_out.close() + + def program_end(self): + if self.z_for_g53 != None: + self.write(self.SPACE() + self.MACHINE_COORDINATES() + self.SPACE() + 'Z' + self.fmt.string(self.z_for_g53) + '\n') + self.write(self.SPACE() + self.PROGRAM_END() + '\n') + + if self.temp_file_to_append_on_close != None: + f_in = open(self.temp_file_to_append_on_close, 'r') + while (True): + line = f_in.readline() + if (len(line) == 0) : break + self.write(line) + f_in.close() + + self.file_close() + + if self.output_block_numbers: + # number every line of the file afterwards + self.number_file(self.filename) + + for f in self.subroutine_files: + self.number_file(f) + + def flush_nc(self): + if len(self.g_list) == 0 and len(self.m) == 0: return + self.write_preps() + self.write_misc() + self.write('\n') + + ############################################################################ + ## Subprograms + + def make_subroutine_name(self, id): + s = self.filename + for i in reversed(range(0, len(s))): + if s[i] == '.': + return s[0:i] + 'sub' + str(id) + s[i:] + + # '.' not found + return s + 'sub' + str(id) + + def sub_begin(self, id, name=None): + if id == None: + if self.current_sub_id == None: + self.current_sub_id = self.program_id + self.current_sub_id += 1 + id = self.current_sub_id + + if name == None: + name = self.program_name + ' subroutine ' + str(id) + + self.save_file = self.file + if self.subroutines_in_own_files: + new_name = self.make_subroutine_name(id) + self.file = open(new_name, 'w') + self.subroutine_files.append(new_name) + else: + ## use temporary file + import tempfile + temp_filename = tempfile.gettempdir()+'/subroutines.txt' + if self.temp_file_to_append_on_close == None: + self.temp_file_to_append_on_close = temp_filename + self.file = open(temp_filename, 'w') + else: + self.file = open(temp_filename, 'a') + + if self.PROGRAM() != None: + self.write((self.PROGRAM() % id) + self.SPACE() + (self.COMMENT(name))) + self.write('\n') + + def sub_call(self, id): + if id == None: + id = self.current_sub_id + self.write(self.SPACE() + (self.SUBPROG_CALL() % id) + '\n') + + def sub_end(self): + self.write(self.SPACE() + self.SUBPROG_END() + '\n') + + self.file.close() + self.file = self.save_file + + def disable_output(self): + self.output_disabled = True + + def enable_output(self): + self.output_disabled = False + + ############################################################################ + ## Settings + + def imperial(self): + self.g_list.append(self.IMPERIAL()) + self.fmt.number_of_decimal_places = 4 + + def metric(self): + self.g_list.append(self.METRIC()) + self.fmt.number_of_decimal_places = 3 + + def absolute(self): + self.g_list.append(self.ABSOLUTE()) + self.absolute_flag = True + + def incremental(self): + self.g_list.append(self.INCREMENTAL()) + self.absolute_flag = False + + def polar(self, on=True): + if (on) : self.g_list.append(self.POLAR_ON()) + else : self.g_list.append(self.POLAR_OFF()) + + def set_plane(self, plane): + if (plane == 0) : self.g_plane.set(self.PLANE_XY()) + elif (plane == 1) : self.g_plane.set(self.PLANE_XZ()) + elif (plane == 2) : self.g_plane.set(self.PLANE_YZ()) + + def set_temporary_origin(self, x=None, y=None, z=None, a=None, b=None, c=None): + self.write(self.SPACE() + (self.SET_TEMPORARY_COORDINATE_SYSTEM())) + if (x != None): self.write( self.SPACE() + 'X ' + (self.fmt.string(x + self.shift_x)) ) + if (y != None): self.write( self.SPACE() + 'Y ' + (self.fmt.string(y + self.shift_y)) ) + if (z != None): self.write( self.SPACE() + 'Z ' + (self.fmt.string(z + self.shift_z)) ) + if (a != None): self.write( self.SPACE() + 'A ' + (self.fmt.string(a)) ) + if (b != None): self.write( self.SPACE() + 'B ' + (self.fmt.string(b)) ) + if (c != None): self.write( self.SPACE() + 'C ' + (self.fmt.string(c)) ) + self.write('\n') + + def remove_temporary_origin(self): + self.write(self.SPACE() + (self.REMOVE_TEMPORARY_COORDINATE_SYSTEM())) + self.write('\n') + ############################################################################ + ## new graphics origin- make a new coordinate system and snap it onto the geometry + ## the toolpath generated should be translated + def translate(self,x=None, y=None, z=None): + self.shift_x = -x + self.shift_y = -y + self.shift_z = -z + + ############################################################################ + ## Tools + + def tool_change(self, id): + if self.output_comment_before_tool_change: + self.comment('tool change to ' + self.tool_defn_params[id]['name']); + + if self.output_cutviewer_comments: + import cutviewer + if id in self.tool_defn_params: + cutviewer.tool_defn(self, id, self.tool_defn_params[id]) + if (self.t != None) and (self.z_for_g53 != None): + self.write('G53 Z' + str(self.z_for_g53) + '\n') + self.write(self.SPACE() + (self.TOOL() % id)) + if self.output_g43_on_tool_change_line == True: + self.write(self.SPACE() + 'G43') + self.write('\n') + if self.output_h_and_d_at_tool_change == True: + if self.output_g43_on_tool_change_line == False: + self.write(self.SPACE() + 'G43') + self.write(self.SPACE() + 'D' + str(id) + self.SPACE() + 'H' + str(id) + '\n') + self.t = id + self.move_done_since_tool_change = False + + def tool_defn(self, id, name='',params=None): + self.tool_defn_params[id] = params + if self.output_tool_definitions: + self.write(self.SPACE() + self.TOOL_DEFINITION()) + self.write(self.SPACE() + ('P%i' % id) + ' ') + + if (params['diameter'] != None): + self.write(self.SPACE() + ('R%.3f' % (float(params['diameter'])/2))) + + if (params['cutting edge height'] != None): + self.write(self.SPACE() + 'Z%.3f' % float(params['cutting edge height'])) + + self.write('\n') + + def offset_radius(self, id, radius=None): + pass + + def offset_length(self, id, length=None): + pass + + def current_tool(self): + return self.t + + + ############################################################################ + ## Datums + + def datum_shift(self, x=None, y=None, z=None, a=None, b=None, c=None): + pass + + def datum_set(self, x=None, y=None, z=None, a=None, b=None, c=None): + pass + + # This is the coordinate system we're using. G54->G59, G59.1, G59.2, G59.3 + # These are selected by values from 1 to 9 inclusive. + def workplane(self, id): + if ((id >= 1) and (id <= 6)): + self.g_list.append(self.WORKPLANE() % (id + self.WORKPLANE_BASE())) + if ((id >= 7) and (id <= 9)): + self.g_list.append(((self.WORKPLANE() % (6 + self.WORKPLANE_BASE())) + ('.%i' % (id - 6)))) + + + ############################################################################ + ## Rates + Modes + + def feedrate(self, f): + self.f.set(f) + self.fhv = False + + def feedrate_hv(self, fh, fv): + self.fh = fh + self.fv = fv + self.fhv = True + + def calc_feedrate_hv(self, h, v): + if math.fabs(v) > math.fabs(h * 2): + # some horizontal, so it should be fine to use the horizontal feed rate + self.f.set(self.fv) + else: + # not much, if any horizontal component, so use the vertical feed rate + self.f.set(self.fh) + + def spindle(self, s, clockwise): + if clockwise == True: + self.s.set(s, self.SPINDLE_CW(), self.SPINDLE_CCW()) + else: + self.s.set(s, self.SPINDLE_CCW(), self.SPINDLE_CW()) + + def coolant(self, mode=0): + if (mode <= 0) : self.m.append(self.COOLANT_OFF()) + elif (mode == 1) : self.m.append(self.COOLANT_MIST()) + elif (mode == 2) : self.m.append(self.COOLANT_FLOOD()) + + def gearrange(self, gear=0): + if (gear <= 0) : self.m.append(self.GEAR_OFF()) + elif (gear <= 4) : self.m.append(self.GEAR() % (gear + GEAR_BASE())) + + ############################################################################ + ## Moves + + def rapid(self, x=None, y=None, z=None, a=None, b=None, c=None ): + if self.same_xyz(x, y, z, a, b, c): return + self.on_move() + + if self.g0123_modal: + if self.prev_g0123 != self.RAPID(): + self.write(self.SPACE() + self.RAPID()) + self.prev_g0123 = self.RAPID() + else: + self.write(self.SPACE() + self.RAPID()) + self.write_preps() + if (x != None): + if (self.absolute_flag ): + self.write(self.SPACE() + self.X() + (self.fmt.string(x + self.shift_x))) + else: + dx = x - self.x + self.write(self.SPACE() + self.X() + (self.fmt.string(dx))) + self.x = x + if (y != None): + if (self.absolute_flag ): + self.write(self.SPACE() + self.Y() + (self.fmt.string(y + self.shift_y))) + else: + dy = y - self.y + self.write(self.SPACE() + self.Y() + (self.fmt.string(dy))) + + self.y = y + if (z != None): + if (self.absolute_flag ): + self.write(self.SPACE() + self.Z() + (self.fmt.string(z + self.shift_z))) + else: + dz = z - self.z + self.write(self.SPACE() + self.Z() + (self.fmt.string(dz))) + + self.z = z + + if (a != None): + if (self.absolute_flag ): + self.write(self.SPACE() + self.A() + (self.fmt.string(a))) + else: + da = a - self.a + self.write(self.SPACE() + self.A() + (self.fmt.string(da))) + self.a = a + + if (b != None): + if (self.absolute_flag ): + self.write(self.SPACE() + self.B() + (self.fmt.string(b))) + else: + db = b - self.b + self.write(self.SPACE() + self.B() + (self.fmt.string(db))) + self.b = b + + if (c != None): + if (self.absolute_flag ): + self.write(self.SPACE() + self.C() + (self.fmt.string(c))) + else: + dc = c - self.c + self.write(self.SPACE() + self.C() + (self.fmt.string(dc))) + self.c = c + self.write_spindle() + self.write_misc() + self.write('\n') + + def feed(self, x=None, y=None, z=None, a=None, b=None, c=None): + if self.same_xyz(x, y, z, a, b, c): return + self.on_move() + if self.g0123_modal: + if self.prev_g0123 != self.FEED(): + self.write(self.SPACE() + self.FEED()) + self.prev_g0123 = self.FEED() + else: + self.write(self.SPACE() + self.FEED()) + self.write_preps() + dx = dy = dz = 0 + if (x != None): + dx = x - self.x + if (self.absolute_flag ): + self.write(self.SPACE() + self.X() + (self.fmt.string(x + self.shift_x))) + else: + self.write(self.SPACE() + self.X() + (self.fmt.string(dx))) + self.x = x + if (y != None): + dy = y - self.y + if (self.absolute_flag ): + self.write(self.SPACE() + self.Y() + (self.fmt.string(y + self.shift_y))) + else: + self.write(self.SPACE() + self.Y() + (self.fmt.string(dy))) + + self.y = y + if (z != None): + dz = z - self.z + if (self.absolute_flag ): + self.write(self.SPACE() + self.Z() + (self.fmt.string(z + self.shift_z))) + else: + self.write(self.SPACE() + self.Z() + (self.fmt.string(dz))) + + self.z = z + + if (a != None): + da = a - self.a + if (self.absolute_flag ): + self.write(self.SPACE() + self.A() + (self.fmt.string(a))) + else: + self.write(self.SPACE() + self.A() + (self.fmt.string(da))) + self.a = a + + if (b != None): + db = b - self.b + if (self.absolute_flag ): + self.write(self.SPACE() + self.B() + (self.fmt.string(b))) + else: + self.write(self.SPACE() + self.B() + (self.fmt.string(db))) + self.b = b + + if (c != None): + dc = c - self.c + if (self.absolute_flag ): + self.write(self.SPACE() + self.C() + (self.fmt.string(c))) + else: + self.write(self.SPACE() + self.C() + (self.fmt.string(dc))) + self.c = c + + if (self.fhv) : self.calc_feedrate_hv(math.sqrt(dx*dx+dy*dy), math.fabs(dz)) + self.write_feedrate() + self.write_spindle() + self.write_misc() + self.write('\n') + + def same_xyz(self, x=None, y=None, z=None, a=None, b=None, c=None): + if (x != None): + if (self.fmt.string(x + self.shift_x)) != (self.fmt.string(self.x)): + return False + if (y != None): + if (self.fmt.string(y + self.shift_y)) != (self.fmt.string(self.y)): + return False + if (z != None): + if (self.fmt.string(z + self.shift_z)) != (self.fmt.string(self.z)): + return False + if (a != None): + if (self.fmt.string(a)) != (self.fmt.string(self.a)): + return False + if (b != None): + if (self.fmt.string(b)) != (self.fmt.string(self.b)): + return False + if (c != None): + if (self.fmt.string(c)) != (self.fmt.string(self.c)): + return False + return True + + + def get_quadrant(self, dx, dy): + if dx < 0: + if dy < 0: + return 2 + else: + return 1 + + else: + if dy < 0: + return 3 + else: + return 0 + + def quadrant_start(self, q, i, j, rad): + while q > 3: q = q - 4 + if q == 0: + return i + rad, j + if q == 1: + return i, j + rad + if q == 2: + return i - rad, j + return i, j - rad + + def quadrant_end(self, q, i, j, rad): + return self.quadrant_start(q + 1, i, j, rad) + + def get_arc_angle(self, sdx, sdy, edx, edy, cw): + angle_s = math.atan2(sdy, sdx); + angle_e = math.atan2(edy, edx); + if cw: + if angle_s < angle_e: angle_s = angle_s + 2 * math.pi + else: + if angle_e < angle_s: angle_e = angle_e + 2 * math.pi + return angle_e - angle_s + + def arc(self, cw, x=None, y=None, z=None, i=None, j=None, k=None, r=None): + if self.same_xyz(x, y, z): return + + if self.output_arcs_as_lines or (self.can_do_helical_arcs == False and self.in_quadrant_splitting == False and (z != None) and (math.fabs(z - self.z) > 0.000001) and (self.fmt.string(z) != self.fmt.string(self.z))): + # split the helical arc into little line feed moves + + if x == None: x = self.x + if y == None: y = self.y + sdx = self.x - i + sdy = self.y - j + edx = x - i + edy = y - j + radius = math.sqrt(sdx*sdx + sdy*sdy) + arc_angle = self.get_arc_angle(sdx, sdy, edx, edy, cw) + angle_start = math.atan2(sdy, sdx); + tolerance = 0.02 + angle_step = 2.0 * math.atan( math.sqrt ( tolerance /(radius - tolerance) )) + segments = int(math.fabs(arc_angle / angle_step) + 1) + angle_step = arc_angle / segments + angle = angle_start + if z != None: + z_step = float(z - self.z)/segments + next_z = self.z + + for p in range(0, segments): + angle = angle + angle_step + next_x = i + radius * math.cos(angle) + next_y = j + radius * math.sin(angle) + if z == None: + next_z = None + else: + next_z = next_z + z_step + self.feed(next_x, next_y, next_z) + return + + if self.arc_centre_positive == True and self.in_quadrant_splitting == False: + # split in to quadrant arcs + self.in_quadrant_splitting = True + + if x == None: x = self.x + if y == None: y = self.y + sdx = self.x - i + sdy = self.y - j + edx = x - i + edy = y - j + + qs = self.get_quadrant(sdx, sdy) + qe = self.get_quadrant(edx, edy) + + if qs == qe: + arc_angle = math.fabs(self.get_arc_angle(sdx, sdy, edx, edy, cw)) + # arc_angle will be either less than pi/2 or greater than 3pi/2 + if arc_angle > 3.14: + if cw: + qs = qs + 4 + else: + qe = qe + 4 + + if qs == qe: + self.arc(cw, x, y, z, i, j, k, r) + else: + rad = math.sqrt(sdx * sdx + sdy * sdy) + if cw: + if qs < qe: qs = qs + 4 + else: + if qe < qs: qe = qe + 4 + + q = qs + while 1: + x1 = x + y1 = y + if q != qe: + if cw: + x1, y1 = self.quadrant_start(q, i, j, rad) + else: + x1, y1 = self.quadrant_end(q, i, j, rad) + + if (self.fmt.string(x1) != self.fmt.string(self.x)) or (self.fmt.string(y1) != self.fmt.string(self.y)): + if (math.fabs(x1 - self.x) > 0.01) or (math.fabs(y1 - self.y) > 0.01): + self.arc(cw, x1, y1, z, i, j, k, r) + else: + self.feed(x1, y1, z) + if q == qe: + break + if cw: + q = q - 1 + else: + q = q + 1 + + self.in_quadrant_splitting = False + return + + self.on_move() + arc_g_code = '' + if cw: arc_g_code = self.ARC_CW() + else: arc_g_code = self.ARC_CCW() + if self.g0123_modal: + if self.prev_g0123 != arc_g_code: + self.write(self.SPACE() + arc_g_code) + self.prev_g0123 = arc_g_code + else: + self.write(self.SPACE() + arc_g_code) + self.write_preps() + if (x != None): + dx = x - self.x + if (self.absolute_flag ): + self.write(self.SPACE() + self.X() + (self.fmt.string(x + self.shift_x))) + else: + self.write(self.SPACE() + self.X() + (self.fmt.string(dx))) + if (y != None): + dy = y - self.y + if (self.absolute_flag ): + self.write(self.SPACE() + self.Y() + (self.fmt.string(y + self.shift_y))) + else: + self.write(self.SPACE() + self.Y() + (self.fmt.string(dy))) + if (z != None): + dz = z - self.z + if (self.absolute_flag ): + self.write(self.SPACE() + self.Z() + (self.fmt.string(z + self.shift_z))) + else: + self.write(self.SPACE() + self.Z() + (self.fmt.string(dz))) + if (i != None): + if self.arc_centre_absolute == False: + i = i - self.x + s = self.fmt.string(i) + if self.arc_centre_positive == True: + if s[0] == '-': + s = s[1:] + self.write(self.SPACE() + self.CENTRE_X() + s) + if (j != None): + if self.arc_centre_absolute == False: + j = j - self.y + s = self.fmt.string(j) + if self.arc_centre_positive == True: + if s[0] == '-': + s = s[1:] + self.write(self.SPACE() + self.CENTRE_Y() + s) + if (k != None): + if self.arc_centre_absolute == False: + k = k - self.z + s = self.fmt.string(k) + if self.arc_centre_positive == True: + if s[0] == '-': + s = s[1:] + self.write(self.SPACE() + self.CENTRE_Z() + s) + if (r != None): + s = self.fmt.string(r) + if self.arc_centre_positive == True: + if s[0] == '-': + s = s[1:] + self.write(self.SPACE() + self.RADIUS() + s) +# use horizontal feed rate + if (self.fhv) : self.calc_feedrate_hv(1, 0) + self.write_feedrate() + self.write_spindle() + self.write_misc() + self.write('\n') + if (x != None): + self.x = x + if (y != None): + self.y = y + if (z != None): + self.z = z + + def arc_cw(self, x=None, y=None, z=None, i=None, j=None, k=None, r=None): + self.arc(True, x, y, z, i, j, k, r) + + def arc_ccw(self, x=None, y=None, z=None, i=None, j=None, k=None, r=None): + self.arc(False, x, y, z, i, j, k, r) + + def dwell(self, t): + self.write_preps() + self.write(self.SPACE() + self.DWELL(t)) + self.write_misc() + self.write('\n') + + def on_move(self): + if self.output_fixtures: + self.output_fixture() + self.move_done_since_tool_change = True + + def rapid_home(self, x=None, y=None, z=None, a=None, b=None, c=None): + pass + + def rapid_unhome(self): + pass + + def set_machine_coordinates(self): + self.write(self.SPACE() + self.MACHINE_COORDINATES()) + self.prev_g0123 = '' + + ############################################################################ + ## CRC + + def use_CRC(self): + return self.useCrc + + def CRC_nominal_path(self): + return self.useCrcCenterline + + def start_CRC(self, left = True, radius = 0.0): + # set up prep code, to be output on next line + if self.t == None: + raise "No tool specified for start_CRC()" + if left: + self.write(self.SPACE() + 'G41') + else: + self.write(self.SPACE() + 'G42') + self.write((self.SPACE() + 'D%i\n') % self.t) + + def end_CRC(self): + self.write(self.SPACE() + 'G40\n') + + ############################################################################ + ## Cycles + + def pattern(self): + pass + + def pattern_uses_subroutine(self): + return self.pattern_done_with_subroutine + + def pocket(self): + pass + + def profile(self): + pass + + def write_internal_coolant_commands(self, internal_coolant_on): + if (internal_coolant_on != None) and (self.output_internal_coolant_commands == True): + if internal_coolant_on == True: + if self.internal_coolant_on != True: + self.write(self.SPACE()) + self.write(self.INTERNAL_COOLANT_ON() + '\n') + self.internal_coolant_on = True + else: + if self.internal_coolant_on != False: + self.write(self.SPACE()) + self.write(self.INTERNAL_COOLANT_OFF() + '\n') + self.internal_coolant_on = False + + # The drill routine supports drilling (G81), drilling with dwell (G82) and peck drilling (G83). + # The x,y,z values are INITIAL locations (above the hole to be made. This is in contrast to + # the Z value used in the G8[1-3] cycles where the Z value is that of the BOTTOM of the hole. + # Instead, this routine combines the Z value and the depth value to determine the bottom of + # the hole. + # + # The standoff value is the distance up from the 'z' value (normally just above the surface) where the bit retracts + # to in order to clear the swarf. This combines with 'z' to form the 'R' value in the G8[1-3] cycles. + # + # The peck_depth value is the incremental depth (Q value) that tells the peck drilling + # cycle how deep to go on each peck until the full depth is achieved. + # + # NOTE: This routine forces the mode to absolute mode so that the values passed into + # the G8[1-3] cycles make sense. I don't know how to find the mode to revert it so I won't + # revert it. I must set the mode so that I can be sure the values I'm passing in make + # sense to the end-machine. + # + def drill(self, x=None, y=None, dwell=None, depthparams = None, retract_mode=None, spindle_mode=None, internal_coolant_on=None, rapid_to_clearance=None): + if (depthparams.clearance_height == None): + self.first_drill_pos = False + return + + self.write_internal_coolant_commands(internal_coolant_on) + + drillExpanded = self.drillExpanded + if (depthparams.step_down != 0) and (dwell != 0): + # pecking and dwell together + if self.dwell_allowed_in_G83 != True: + drillExpanded = True + + if drillExpanded: + # for machines which don't understand G81, G82 etc. + peck_depth = depthparams.step_down + if peck_depth == None: + peck_depth = depthparams.final_depth + current_z = depthparams.start_depth + self.rapid(x, y) + + first = True + last_cut = False + + while True: + next_z = current_z - peck_depth + if next_z < (depthparams.final_depth + 0.001): + next_z = depthparams.final_depth + last_cut = True + if next_z >= current_z: + break; + if first: + self.rapid(z = depthparams.start_depth + depthparams.rapid_safety_space) + else: + self.rapid(z = current_z) + self.feed(z = next_z) + if dwell != 0 and last_cut: + self.dwell(dwell) + if last_cut:self.rapid(z = depthparams.clearance_height) + else: + if rapid_to_clearance: + self.rapid(z = depthparams.clearance_height) + else: + self.rapid(z = depthparams.start_depth + depthparams.rapid_safety_space) + current_z = next_z + first = False + + self.first_drill_pos = False + return + + if self.output_g98_and_g99 == True: + if rapid_to_clearance == True: + if self.output_g43_z_before_drilling_if_g98: + if self.fmt.string(depthparams.clearance_height) != self.z_for_g43: + self.z_for_g43 = self.fmt.string(depthparams.clearance_height) + self.write(self.SPACE() + 'G43' + self.SPACE() + 'Z' + self.z_for_g43 + '\n') + + if self.first_drill_pos ==True and rapid_to_clearance == True: + self.rapid(x, y) + self.rapid(z = depthparams.clearance_height) + + self.in_canned_cycle = True + self.write_preps() + + if (depthparams.step_down != 0): + # G83 peck drilling + if self.drill_modal: + if self.PECK_DRILL() + self.PECK_DEPTH(depthparams.step_down) != self.prev_drill: + self.write(self.SPACE() + self.PECK_DRILL() + self.SPACE() + self.PECK_DEPTH(depthparams.step_down)) + self.prev_drill = self.PECK_DRILL() + self.PECK_DEPTH(depthparams.step_down) + else: + self.write(self.PECK_DRILL() + self.PECK_DEPTH(depthparams.step_down)) + + if (self.dwell != 0) and self.dwell_allowed_in_G83: + self.write(self.SPACE() + self.TIME() + (self.FORMAT_TIME().string(dwell))) + + else: + # We're either just drilling or drilling with dwell. + if (dwell == 0): + # We're just drilling. + if self.drill_modal: + if self.DRILL() != self.prev_drill: + self.write(self.SPACE() + self.DRILL()) + self.prev_drill = self.DRILL() + else: + self.write(self.SPACE() + self.DRILL()) + + else: + # We're drilling with dwell. + + if self.drill_modal: + if self.DRILL_WITH_DWELL(dwell) != self.prev_drill: + self.write(self.SPACE() + self.DRILL_WITH_DWELL(dwell)) + self.prev_drill = self.DRILL_WITH_DWELL(dwell) + else: + self.write(self.SPACE() + self.DRILL_WITH_DWELL(dwell)) + + if self.output_g98_and_g99 == True: + if rapid_to_clearance == True: + if self.g98_not_g99 != True: + self.write(self.SPACE() + self.RETRACT_TO_CLEARANCE()) + self.g98_not_g99 = True + else: + if self.g98_not_g99 != False: + self.write(self.SPACE() + self.RETRACT_TO_STANDOFF()) + self.g98_not_g99 = False + + # Set the retraction point to the 'standoff' distance above the starting z height. + retract_height = depthparams.start_depth + depthparams.rapid_safety_space + if (x != None): + self.write(self.SPACE() + self.X() + (self.fmt.string(x + self.shift_x))) + self.x = x + + if (y != None): + self.write(self.SPACE() + self.Y() + (self.fmt.string(y + self.shift_y))) + self.y = y + + if self.drill_modal: + if depthparams.start_depth != self.prev_z: + self.write(self.SPACE() + self.Z() + (self.fmt.string(depthparams.final_depth))) + self.prev_z=depthparams.start_depth + else: + self.write(self.SPACE() + self.Z() + (self.fmt.string(depthparams.final_depth))) # This is the 'z' value for the bottom of the hole. + self.z = (depthparams.start_depth + depthparams.rapid_safety_space) # We want to remember where z is at the end (at the top of the hole) + + if self.drill_modal: + if self.prev_retract != self.RETRACT(retract_height) : + self.write(self.SPACE() + self.RETRACT(retract_height)) + self.prev_retract = self.RETRACT(retract_height) + else: + self.write(self.SPACE() + self.RETRACT(retract_height)) + + if (self.fv) : + self.f.set(self.fv) + + self.write_feedrate() + self.write_spindle() + self.write_misc() + self.write('\n') + self.first_drill_pos = False + + def end_canned_cycle(self): + if self.in_canned_cycle == False: + return + self.write(self.SPACE() + self.END_CANNED_CYCLE() + '\n') + self.write_internal_coolant_commands(0) + self.prev_drill = '' + self.prev_g0123 = '' + self.prev_z = '' + self.prev_f = '' + self.prev_retract = '' + self.in_canned_cycle = False + self.first_drill_pos = True + + ############################################################################ + ## Misc + + def comment(self, text): + self.write((self.COMMENT(text) + '\n')) + + def insert(self, text): + pass + + def block_delete(self, on=False): + pass + + def variable(self, id): + return (self.VARIABLE() % id) + + def variable_set(self, id, value): + self.write(self.SPACE() + (self.VARIABLE() % id) + self.SPACE() + (self.VARIABLE_SET() % value) + '\n') + + # This routine uses the G92 coordinate system offsets to establish a temporary coordinate + # system at the machine's current position. It can then use absolute coordinates relative + # to this position which makes coding easy. It then moves to the 'point along edge' which + # should be above the workpiece but still on one edge. It then backs off from the edge + # to the 'retracted point'. It then plunges down by the depth value specified. It then + # probes back towards the 'destination point'. The probed X,Y location are stored + # into the 'intersection variable' variables. Finally the machine moves back to the + # original location. This is important so that the results of multiple calls to this + # routine may be compared meaningfully. + def probe_single_point(self, point_along_edge_x=None, point_along_edge_y=None, depth=None, retracted_point_x=None, retracted_point_y=None, destination_point_x=None, destination_point_y=None, intersection_variable_x=None, intersection_variable_y=None, probe_offset_x_component=None, probe_offset_y_component=None ): + self.write(self.SPACE() + (self.SET_TEMPORARY_COORDINATE_SYSTEM() + (' X 0 Y 0 Z 0') + ('\t(Temporarily make this the origin)\n'))) + + if (self.fhv) : self.calc_feedrate_hv(1, 0) + self.write_feedrate() + self.write('\t(Set the feed rate for probing)\n') + + self.rapid(point_along_edge_x,point_along_edge_y) + self.rapid(retracted_point_x,retracted_point_y) + self.feed(z=depth) + + self.write((self.PROBE_TOWARDS_WITH_SIGNAL() + (' X ' + (self.fmt.string(destination_point_x)) + ' Y ' + (self.fmt.string(destination_point_y)) ) + ('\t(Probe towards our destination point)\n'))) + + self.comment('Back off the workpiece and re-probe more slowly') + self.write(self.SPACE() + ('#' + intersection_variable_x + '= [#5061 - [ 0.5 * ' + probe_offset_x_component + ']]\n')) + self.write(self.SPACE() + ('#' + intersection_variable_y + '= [#5062 - [ 0.5 * ' + probe_offset_y_component + ']]\n')) + self.write(self.RAPID()) + self.write(self.SPACE() + ' X #' + intersection_variable_x + ' Y #' + intersection_variable_y + '\n') + + self.write(self.SPACE() + self.FEEDRATE() + self.ffmt.string(self.fh / 2.0) + '\n') + + self.write((self.SPACE() + self.PROBE_TOWARDS_WITH_SIGNAL() + (' X ' + (self.fmt.string(destination_point_x)) + ' Y ' + (self.fmt.string(destination_point_y)) ) + ('\t(Probe towards our destination point)\n'))) + + self.comment('Store the probed location somewhere we can get it again later') + self.write(('#' + intersection_variable_x + '=' + probe_offset_x_component + ' (Portion of probe radius that contributes to the X coordinate)\n')) + self.write(('#' + intersection_variable_x + '=[#' + intersection_variable_x + ' + #5061]\n')) + self.write(('#' + intersection_variable_y + '=' + probe_offset_y_component + ' (Portion of probe radius that contributes to the Y coordinate)\n')) + self.write(('#' + intersection_variable_y + '=[#' + intersection_variable_y + ' + #5062]\n')) + + self.comment('Now move back to the original location') + self.rapid(retracted_point_x,retracted_point_y) + self.rapid(z=0) + self.rapid(point_along_edge_x,point_along_edge_y) + self.rapid(x=0, y=0) + + self.write((self.REMOVE_TEMPORARY_COORDINATE_SYSTEM() + ('\t(Restore the previous coordinate system)\n'))) + + def probe_downward_point(self, x=None, y=None, depth=None, intersection_variable_z=None): + self.write((self.SET_TEMPORARY_COORDINATE_SYSTEM() + (' X 0 Y 0 Z 0') + ('\t(Temporarily make this the origin)\n'))) + if (self.fhv) : self.calc_feedrate_hv(1, 0) + self.write(self.FEEDRATE() + ' [' + self.ffmt.string(self.fh) + ' / 5.0 ]') + self.write('\t(Set the feed rate for probing)\n') + + if x != None and y != None: + self.write(self.RAPID()) + self.write(' X ' + x + ' Y ' + y + '\n') + + self.write((self.PROBE_TOWARDS_WITH_SIGNAL() + ' Z ' + (self.fmt.string(depth)) + ('\t(Probe towards our destination point)\n'))) + + self.comment('Store the probed location somewhere we can get it again later') + self.write(('#' + intersection_variable_z + '= #5063\n')) + + self.comment('Now move back to the original location') + self.rapid(z=0) + self.rapid(x=0, y=0) + + self.write((self.REMOVE_TEMPORARY_COORDINATE_SYSTEM() + ('\t(Restore the previous coordinate system)\n'))) + + + def report_probe_results(self, x1=None, y1=None, z1=None, x2=None, y2=None, z2=None, x3=None, y3=None, z3=None, x4=None, y4=None, z4=None, x5=None, y5=None, z5=None, x6=None, y6=None, z6=None, xml_file_name=None ): + pass + + def open_log_file(self, xml_file_name=None ): + pass + + def log_coordinate(self, x=None, y=None, z=None): + pass + + def log_message(self, message=None): + pass + + def close_log_file(self): + pass + + # Rapid movement to the midpoint between the two points specified. + # NOTE: The points are specified either as strings representing numbers or as strings + # representing variable names. This allows the HeeksCNC module to determine which + # variable names are used in these various routines. + def rapid_to_midpoint(self, x1=None, y1=None, z1=None, x2=None, y2=None, z2=None): + self.write(self.RAPID()) + if ((x1 != None) and (x2 != None)): + self.write((' X ' + '[[[' + x1 + ' - ' + x2 + '] / 2.0] + ' + x2 + ']')) + + if ((y1 != None) and (y2 != None)): + self.write((' Y ' + '[[[' + y1 + ' - ' + y2 + '] / 2.0] + ' + y2 + ']')) + + if ((z1 != None) and (z2 != None)): + self.write((' Z ' + '[[[' + z1 + ' - ' + z2 + '] / 2.0] + ' + z2 + ']')) + + self.write('\n') + + # Rapid movement to the intersection of two lines (in the XY plane only). This routine + # is based on information found in http://local.wasp.uwa.edu.au/~pbourke/geometry/lineline2d/ + # written by Paul Bourke. The ua_numerator, ua_denominator, ua and ub parameters + # represent variable names (with the preceding '#' included in them) for use as temporary + # variables. They're specified here simply so that HeeksCNC can manage which variables + # are used in which GCode calculations. + # + # As per the notes on the web page, the ua_denominator and ub_denominator formulae are + # the same so we don't repeat this. If the two lines are coincident or parallel then + # no movement occurs. + # + # NOTE: The points are specified either as strings representing numbers or as strings + # representing variable names. This allows the HeeksCNC module to determine which + # variable names are used in these various routines. + def rapid_to_intersection(self, x1, y1, x2, y2, x3, y3, x4, y4, intersection_x, intersection_y, ua_numerator, ua_denominator, ua, ub_numerator, ub): + self.comment('Find the intersection of the two lines made up by the four probed points') + self.write(ua_numerator + '=[[[' + x4 + ' - ' + x3 + '] * [' + y1 + ' - ' + y3 + ']] - [[' + y4 + ' - ' + y3 + '] * [' + x1 + ' - ' + x3 + ']]]\n') + self.write(ua_denominator + '=[[[' + y4 + ' - ' + y3 + '] * [' + x2 + ' - ' + x1 + ']] - [[' + x4 + ' - ' + x3 + '] * [' + y2 + ' - ' + y1 + ']]]\n') + self.write(ub_numerator + '=[[[' + x2 + ' - ' + x1 + '] * [' + y1 + ' - ' + y3 + ']] - [[' + y2 + ' - ' + y1 + '] * [' + x1 + ' - ' + x3 + ']]]\n') + + self.comment('If they are not parallel') + self.write('O900 IF [' + ua_denominator + ' NE 0]\n') + self.comment('And if they are not coincident') + self.write('O901 IF [' + ua_numerator + ' NE 0 ]\n') + + self.write(' ' + ua + '=[' + ua_numerator + ' / ' + ua_denominator + ']\n') + self.write(' ' + ub + '=[' + ub_numerator + ' / ' + ua_denominator + ']\n') # NOTE: ub denominator is the same as ua denominator + self.write(' ' + intersection_x + '=[' + x1 + ' + [[' + ua + ' * [' + x2 + ' - ' + x1 + ']]]]\n') + self.write(' ' + intersection_y + '=[' + y1 + ' + [[' + ua + ' * [' + y2 + ' - ' + y1 + ']]]]\n') + self.write(' ' + self.RAPID()) + self.write(' X ' + intersection_x + ' Y ' + intersection_y + '\n') + + self.write('O901 ENDIF\n') + self.write('O900 ENDIF\n') + + # We need to calculate the rotation angle based on the line formed by the + # x1,y1 and x2,y2 coordinate pair. With that angle, we need to move + # x_offset and y_offset distance from the current (0,0,0) position. + # + # The x1,y1,x2 and y2 parameters are all variable names that contain the actual + # values. + # The x_offset and y_offset are both numeric (floating point) values + def rapid_to_rotated_coordinate(self, x1, y1, x2, y2, ref_x, ref_y, x_current, y_current, x_final, y_final): + self.comment('Rapid to rotated coordinate') + self.write( '#1 = [atan[' + y2 + ' - ' + y1 + ']/[' + x2 +' - ' + x1 + ']] (nominal_angle)\n') + self.write( '#2 = [atan[' + ref_y + ']/[' + ref_x + ']] (reference angle)\n') + self.write( '#3 = [#1 - #2] (angle)\n' ) + self.write( '#4 = [[[' + (self.fmt.string(0)) + ' - ' + (self.fmt.string(x_current)) + '] * COS[ #3 ]] - [[' + (self.fmt.string(0)) + ' - ' + (self.fmt.string(y_current)) + '] * SIN[ #3 ]]]\n' ) + self.write( '#5 = [[[' + (self.fmt.string(0)) + ' - ' + (self.fmt.string(x_current)) + '] * SIN[ #3 ]] + [[' + (self.fmt.string(0)) + ' - ' + (self.fmt.string(y_current)) + '] * COS[ #3 ]]]\n' ) + + self.write( '#6 = [[' + (self.fmt.string(x_final)) + ' * COS[ #3 ]] - [' + (self.fmt.string(y_final)) + ' * SIN[ #3 ]]]\n' ) + self.write( '#7 = [[' + (self.fmt.string(y_final)) + ' * SIN[ #3 ]] + [' + (self.fmt.string(y_final)) + ' * COS[ #3 ]]]\n' ) + + self.write( self.RAPID() + ' X [ #4 + #6 ] Y [ #5 + #7 ]\n' ) + + def BEST_POSSIBLE_SPEED(self, motion_blending_tolerance, naive_cam_tolerance): + statement = 'G64' + + if (motion_blending_tolerance > 0): + statement += ' P ' + str(motion_blending_tolerance) + + if (naive_cam_tolerance > 0): + statement += ' Q ' + str(naive_cam_tolerance) + + return(statement) + + def set_path_control_mode(self, mode, motion_blending_tolerance, naive_cam_tolerance ): + if (mode == 0): + self.write( self.EXACT_PATH_MODE() + '\n' ) + if (mode == 1): + self.write( self.EXACT_STOP_MODE() + '\n' ) + if (mode == 2): + self.write( self.BEST_POSSIBLE_SPEED( motion_blending_tolerance, naive_cam_tolerance ) + '\n' ) + + +################################################################################ + +nc.creator = Creator() diff --git a/src/Mod/Path/PathScripts/nc/iso_codes.py b/src/Mod/Path/PathScripts/nc/iso_codes.py new file mode 100644 index 0000000000..fd6c270767 --- /dev/null +++ b/src/Mod/Path/PathScripts/nc/iso_codes.py @@ -0,0 +1,4 @@ +class Codes(): + pass + +codes = Codes() diff --git a/src/Mod/Path/PathScripts/nc/iso_crc.py b/src/Mod/Path/PathScripts/nc/iso_crc.py new file mode 100644 index 0000000000..f6db7974e5 --- /dev/null +++ b/src/Mod/Path/PathScripts/nc/iso_crc.py @@ -0,0 +1,21 @@ +################################################################################ +# iso_crc.py +# +# a class derived from iso machine, with Cutter Radius Compensation turned on. +# +# Dan Heeks, 4th May 2010 + +import nc +import iso +import math + +################################################################################ +class Creator(iso.Creator): + + def __init__(self): + iso.Creator.__init__(self) + self.useCrc = True + +################################################################################ + +nc.creator = Creator() diff --git a/src/Mod/Path/PathScripts/nc/iso_modal.py b/src/Mod/Path/PathScripts/nc/iso_modal.py new file mode 100644 index 0000000000..f53081767f --- /dev/null +++ b/src/Mod/Path/PathScripts/nc/iso_modal.py @@ -0,0 +1,22 @@ +################################################################################ +# iso_modal.py +# +# a class derived from iso machine, but with XYZF G1, G2 etc modal to reduce the size of the file. +# +# Dan Heeks, 4th May 2010 + +import nc +import iso +import math + +################################################################################ +class Creator(iso.Creator): + + def __init__(self): + iso.Creator.__init__(self) + self.f_modal = True + self.g0123_modal = True + self.drill_modal = True +################################################################################ + +nc.creator = Creator() diff --git a/src/Mod/Path/PathScripts/nc/iso_read.py b/src/Mod/Path/PathScripts/nc/iso_read.py new file mode 100644 index 0000000000..f2ebab943a --- /dev/null +++ b/src/Mod/Path/PathScripts/nc/iso_read.py @@ -0,0 +1,164 @@ +################################################################################ +# iso_read.py +# +# Simple ISO NC code parsing +# +# Hirutso Enni, 2009-01-13 + +import nc_read as nc +import re +import sys + +################################################################################ +class Parser(nc.Parser): + + def __init__(self, writer): + nc.Parser.__init__(self, writer) + + self.pattern_main = re.compile('([(!;].*|\s+|[a-zA-Z0-9_:](?:[+-])?\d*(?:\.\d*)?|\w\#\d+|\(.*?\)|\#\d+\=(?:[+-])?\d*(?:\.\d*)?)') + self.arc_centre_absolute = False + self.arc_centre_positive = False + self.oldx = None + self.oldy = None + self.oldz = None + + #if ( or ! or ; at least one space or a letter followed by some character or not followed by a +/- followed by decimal, with a possible decimal point + # followed by a possible deimcal, or a letter followed by # with a decimal . deimcal + # add your character here > [(!;] for comments char + # then look for the 'comment' function towards the end of the file and add another elif + + def ParseWord(self, word): + word == word.upper() + if (word[0] == 'A'): + self.col = "axis" + self.a = eval(word[1:]) + self.move = True + elif (word[0] == 'B'): + self.col = "axis" + self.b = eval(word[1:]) + self.move = True + elif (word[0] == 'C'): + self.col = "axis" + self.c = eval(word[1:]) + self.move = True + elif (word[0] == 'F'): + self.col = "axis" + self.writer.feedrate(word[1:]) + elif (word[0] == 'H'): + self.col = "axis" + self.h = eval(word[1:]) + self.move = True + elif (word == 'G0' or word == 'G00'): + self.path_col = "rapid" + self.col = "rapid" + self.arc = 0 + elif (word == 'G1' or word == 'G01'): + self.path_col = "feed" + self.col = "feed" + self.arc = 0 + elif (word == 'G2' or word == 'G02' or word == 'G12'): + self.path_col = "feed" + self.col = "feed" + self.arc = -1 + elif (word == 'G3' or word == 'G03' or word == 'G13'): + self.path_col = "feed" + self.col = "feed" + self.arc = +1 + elif (word == 'G10'): + self.no_move = True + elif (word == 'G53'): + self.no_move = True + elif (word == 'L1'): + self.no_move = True + elif (word == 'G61.1' or word == 'G61' or word == 'G64'): + self.no_move = True + elif (word == 'G20' or word == 'G70'): + self.col = "prep" + self.writer.imperial() + elif (word == 'G21' or word == 'G71'): + self.col = "prep" + self.writer.metric() + elif (word == 'G43'): + self.height_offset = True + self.move = True + self.path_col = "rapid" + self.col = "rapid" + elif (word == 'G80'): + self.drill_off = True + elif (word == 'G81'): + self.drill = True + self.no_move = True + self.path_col = "feed" + self.col = "feed" + elif (word == 'G82'): + self.drill = True; + self.no_move = True + self.path_col = "feed" + self.col = "feed" + elif (word == 'G83'): + self.drill = True + self.no_move = True + self.path_col = "feed" + self.col = "feed" + elif (word == 'G90'): + self.absolute() + elif (word == 'G91'): + self.incremental() + elif (word == 'G98'): + self.drilling_uses_clearance = True + elif (word == 'G99'): + self.drilling_uses_clearance = False + elif (word[0] == 'G') : col = "prep" + elif (word[0] == 'I'): + self.col = "axis" + self.i = eval(word[1:]) + self.move = True + elif (word[0] == 'J'): + self.col = "axis" + self.j = eval(word[1:]) + self.move = True + elif (word[0] == 'K'): + self.col = "axis" + self.k = eval(word[1:]) + self.move = True + elif (word[0] == 'M') : self.col = "misc" + elif (word[0] == 'N') : self.col = "blocknum" + elif (word[0] == 'O') : self.col = "program" + elif (word[0] == 'P'): + if (self.no_move != True): + self.col = "axis" + self.p = eval(word[1:]) + self.move = True + elif (word[0] == 'Q'): + if (self.no_move != True): + self.col = "axis" + self.q = eval(word[1:]) + self.move = True + elif (word[0] == 'R'): + self.col = "axis" + self.r = eval(word[1:]) + self.move = True + elif (word[0] == 'S'): + self.col = "axis" + self.writer.spindle(word[1:], (float(word[1:]) >= 0.0)) + elif (word[0] == 'T') : + self.col = "tool" + self.writer.tool_change( eval(word[1:]) ) + elif (word[0] == 'X'): + self.col = "axis" + self.x = eval(word[1:]) + self.move = True + elif (word[0] == 'Y'): + self.col = "axis" + self.y = eval(word[1:]) + self.move = True + elif (word[0] == 'Z'): + self.col = "axis" + self.z = eval(word[1:]) + self.move = True + elif (word[0] == '(') : (self.col, self.cdata) = ("comment", True) + elif (word[0] == '!') : (self.col, self.cdata) = ("comment", True) + elif (word[0] == ';') : (self.col, self.cdata) = ("comment", True) + elif (word[0] == '#') : self.col = "variable" + elif (word[0] == ':') : self.col = "blocknum" + elif (ord(word[0]) <= 32) : self.cdata = True diff --git a/src/Mod/Path/PathScripts/nc/mach3.py b/src/Mod/Path/PathScripts/nc/mach3.py new file mode 100755 index 0000000000..ca096c68ec --- /dev/null +++ b/src/Mod/Path/PathScripts/nc/mach3.py @@ -0,0 +1,20 @@ +import nc +import iso + +class Creator(iso.Creator): + def init(self): + iso.Creator.init(self) + + def SPACE(self): return(' ') + + def program_begin(self, id, comment): + self.write( ('(' + 'GCode created using the HeeksCNC Mach3 post processor' + ')' + '\n') ) + self.write( ('(' + comment + ')' + '\n') ) + + def tool_change(self, id): + self.write('G43H%i'% id +'\n') + self.write((self.TOOL() % id) + '\n') + self.t = id + +nc.creator = Creator() + diff --git a/src/Mod/Path/PathScripts/nc/machines.xml b/src/Mod/Path/PathScripts/nc/machines.xml new file mode 100644 index 0000000000..3a4f22657a --- /dev/null +++ b/src/Mod/Path/PathScripts/nc/machines.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/Mod/Path/PathScripts/nc/makerbotHBP.py b/src/Mod/Path/PathScripts/nc/makerbotHBP.py new file mode 100644 index 0000000000..deafb7f231 --- /dev/null +++ b/src/Mod/Path/PathScripts/nc/makerbotHBP.py @@ -0,0 +1,306 @@ +import nc +import makerbot_codes as maker +import datetime +import iso_modal +import math + + +now = datetime.datetime.now() + +################################################################################ +class CreatorMakerbotHBP(iso_modal.CreatorIsoModal): + def __init__(self): + iso_modal.CreatorIsoModal.__init__(self) + + self.absolute_flag = True + self.prev_g91 = '' + + +################################################################################ +# program begin and end + + def program_begin(self, id, name=''): + self.write((maker.codes.COMMENT(now))) + self.write((maker.codes.EXTRUDER_TEMP('220')) + (maker.codes.COMMENT('Extruder Temp')) ) + self.write((maker.codes.BUILD_BED_TEMP('110'))+ (maker.codes.COMMENT('Build Bed Temp')) ) + self.write((maker.codes.FAN_OFF()) + (maker.codes.COMMENT('Fan Off')) ) + self.write((maker.codes.METRIC()) + (maker.codes.COMMENT('Metric units')) ) + self.write((maker.codes.ABSOLUTE()) + (maker.codes.COMMENT('Absolute units')) ) + self.write('G92 X0 Y0 Z0 (You are now at 0,0,0)\n') + self.write('G0 Z15 (Move up for warmup)\n') + self.write((maker.codes.EXTRUDER_SPEED_PWM('255')) + (maker.codes.COMMENT('Extruder Speed')) ) + self.write('M6 T0 (Wait for tool to heat up)\n') + self.write('G04 P5000 (Wait 5 seconds)\n') + self.write((maker.codes.EXTRUDER_ON_FWD()) + (maker.codes.COMMENT('Extruder On')) ) + self.write('G04 P5000 (Wait 5 seconds)\n') + self.write((maker.codes.EXTRUDER_OFF()) + (maker.codes.COMMENT('Extruder Off')) ) + self.write('M01 (The heated build platform is heating up. Wait until after the lights have turned off for the first time, clear the test extrusion, and click yes.)\n') + self.write('G0 Z0 (Go back to zero.)\n') + + def program_end(self): + self.write((maker.codes.COMMENT('End of the file. Begin cool-down'))) + self.write((maker.codes.EXTRUDER_TEMP('0')) + (maker.codes.COMMENT('Extruder Temp')) ) + self.write((maker.codes.BUILD_BED_TEMP('0')) + (maker.codes.COMMENT('Build Bed Temp')) ) + self.write((maker.codes.FAN_ON()) + (maker.codes.COMMENT('Fan On')) ) + self.write('G92 Z0 (zero our z axis - hack b/c skeinforge mangles gcodes in end.txt)\n') + self.write('G1 Z10 (go up 10 b/c it was zeroed earlier.)\n') + self.write('G1 X0 Y0 Z10 (go to 0,0,z)\n') + self.write((maker.codes.STEPPERS_OFF()) + (maker.codes.COMMENT('Steppers Off')) ) + + def program_stop(self): + self.write((maker.codes.EXTRUDER_TEMP('0'))) + self.write((maker.codes.BUILD_BED_TEMP('0'))) + self.write((maker.codes.STEPPERS_OFF())) + +################################################################################ +# general + def write_blocknum(self): + pass + + def set_plane(self, plane): + pass + + def workplane(self, id): + pass + + def spindle(self, s, clockwise): + pass +################################################################################ +# Extruder Control + + def extruder_on(self): + self.write((maker.codes.EXTRUDER_ON()) + ('\n')) + + def extruder_off(self): + self.write((maker.codes.EXTRUDER_OFF()) + ('\n')) + + def set_extruder_flowrate(self, flowrate): + self.write((maker.codes.EXTRUDER_SPEED_PWM(flowrate)) + ('\n')) + + def extruder_temp(self, temp): + self.write((maker.codes.EXTRUDER_TEMP(temp)) + ('\n')) + +################################################################################ +# Build Environment Control + def build_bed_temp(self, temp): + self.write((maker.codes.BUILD_BED_TEMP(temp)) + ('\n')) + + def chamber_temp(self, temp): + self.write((maker.codes.CHAMBER_TEMP(temp)) + ('\n')) + +################################################################################ +# Fan Control + def fan_on(self): + self.write((maker.codes.FAN_ON()) + ('\n')) + + def fan_off(self): + self.write((maker.codes.FAN_OFF()) + ('\n')) + +################################################################################ +# Custom routines + + def wipe(self): + self.write(('(This would be a good place for a custom wipe routine)\n')) + +################################################################################ +# APT style INSERT- insert anything into program + + def insert(self, text): + self.write((text + '\n')) + +################################################################################ +# tool info + def tool_change(self, id): + pass + # self.write_blocknum() + # self.write((maker.codes.TOOL() % id) + '\n') + # self.t = id + + def tool_defn(self, id, name='', params=None): + pass +############################################################################ +## Moves + + def rapid(self, x=None, y=None, z=None, a=None, b=None, c=None ): + self.write_blocknum() + if self.g0123_modal: + if self.prev_g0123 != maker.codes.RAPID(): + self.write(maker.codes.RAPID()) + self.prev_g0123 = maker.codes.RAPID() + else: + self.write(maker.codes.RAPID()) + self.write_preps() + if (x != None): + dx = x - self.x + if (self.absolute_flag ): + self.write(maker.codes.X() + (self.fmt % x)) + else: + self.write(maker.codes.X() + (self.fmt % dx)) + self.x = x + if (y != None): + dy = y - self.y + if (self.absolute_flag ): + self.write(maker.codes.Y() + (self.fmt % y)) + else: + self.write(maker.codes.Y() + (self.fmt % dy)) + + self.y = y + if (z != None): + dz = z - self.z + if (self.absolute_flag ): + self.write(maker.codes.Z() + (self.fmt % z)) + else: + self.write(maker.codes.Z() + (self.fmt % dz)) + + self.z = z + + if (a != None): + da = a - self.a + if (self.absolute_flag ): + self.write(maker.codes.A() + (self.fmt % a)) + else: + self.write(maker.codes.A() + (self.fmt % da)) + self.a = a + + if (b != None): + db = b - self.b + if (self.absolute_flag ): + self.write(maker.codes.B() + (self.fmt % b)) + else: + self.write(maker.codes.B() + (self.fmt % db)) + self.b = b + + if (c != None): + dc = c - self.c + if (self.absolute_flag ): + self.write(maker.codes.C() + (self.fmt % c)) + else: + self.write(maker.codes.C() + (self.fmt % dc)) + self.c = c + self.write_spindle() + self.write_misc() + self.write('\n') + + def feed(self, x=None, y=None, z=None, a = None, b = None, c = None): + if self.same_xyz(x, y, z): return + self.write_blocknum() + if self.g0123_modal: + if self.prev_g0123 != maker.codes.FEED(): + self.write(maker.codes.FEED()) + self.prev_g0123 = maker.codes.FEED() + else: + self.write(maker.codes.FEED()) + self.write_preps() + dx = dy = dz = 0 + if (x != None): + dx = x - self.x + if (self.absolute_flag ): + self.write(maker.codes.X() + (self.fmt % x)) + else: + self.write(maker.codes.X() + (self.fmt % dx)) + self.x = x + if (y != None): + dy = y - self.y + if (self.absolute_flag ): + self.write(maker.codes.Y() + (self.fmt % y)) + else: + self.write(maker.codes.Y() + (self.fmt % dy)) + + self.y = y + if (z != None): + dz = z - self.z + if (self.absolute_flag ): + self.write(maker.codes.Z() + (self.fmt % z)) + else: + self.write(maker.codes.Z() + (self.fmt % dz)) + + self.z = z + if (self.fhv) : self.calc_feedrate_hv(math.sqrt(dx*dx+dy*dy), math.fabs(dz)) + self.write_feedrate() + self.write_spindle() + self.write_misc() + self.write('\n') + + def same_xyz(self, x=None, y=None, z=None): + if (x != None): + if (self.fmt % x) != (self.fmt % self.x): + return False + if (y != None): + if (self.fmt % y) != (self.fmt % self.y): + return False + if (z != None): + if (self.fmt % z) != (self.fmt % self.z): + return False + + return True + + def arc(self, cw, x=None, y=None, z=None, i=None, j=None, k=None, r=None): + if self.same_xyz(x, y, z): return + self.write_blocknum() + arc_g_code = '' + if cw: arc_g_code = maker.codes.ARC_CW() + else: arc_g_code = maker.codes.ARC_CCW() + if self.g0123_modal: + if self.prev_g0123 != arc_g_code: + self.write(arc_g_code) + self.prev_g0123 = arc_g_code + else: + self.write(arc_g_code) + self.write_preps() + if (x != None): + dx = x - self.x + if (self.absolute_flag ): + self.write(maker.codes.X() + (self.fmt % x)) + else: + self.write(maker.codes.X() + (self.fmt % dx)) + self.x = x + if (y != None): + dy = y - self.y + if (self.absolute_flag ): + self.write(maker.codes.Y() + (self.fmt % y)) + else: + self.write(maker.codes.Y() + (self.fmt % dy)) + self.y = y + if (z != None): + dz = z - self.z + if (self.absolute_flag ): + self.write(maker.codes.Z() + (self.fmt % z)) + else: + self.write(maker.codes.Z() + (self.fmt % dz)) + self.z = z + if (i != None) : self.write(maker.codes.CENTRE_X() + (self.fmt % i)) + if (j != None) : self.write(maker.codes.CENTRE_Y() + (self.fmt % j)) + if (k != None) : self.write(maker.codes.CENTRE_Z() + (self.fmt % k)) + if (r != None) : self.write(maker.codes.RADIUS() + (self.fmt % r)) +# use horizontal feed rate + if (self.fhv) : self.calc_feedrate_hv(1, 0) + self.write_feedrate() + self.write_spindle() + self.write_misc() + self.write('\n') + + def arc_cw(self, x=None, y=None, z=None, i=None, j=None, k=None, r=None): + self.arc(True, x, y, z, i, j, k, r) + + def arc_ccw(self, x=None, y=None, z=None, i=None, j=None, k=None, r=None): + self.arc(False, x, y, z, i, j, k, r) + + def dwell(self, t): + self.write_blocknum() + self.write_preps() + self.write(maker.codes.DWELL() + (maker.codes.TIME() % t)) + self.write_misc() + self.write('\n') + + def rapid_home(self, x=None, y=None, z=None, a=None, b=None, c=None): + pass + + def rapid_unhome(self): + pass + + def set_machine_coordinates(self): + self.write(maker.codes.MACHINE_COORDINATES()) + self.prev_g0123 = '' + +nc.creator = CreatorMakerbotHBP() + diff --git a/src/Mod/Path/PathScripts/nc/makerbot_codes.py b/src/Mod/Path/PathScripts/nc/makerbot_codes.py new file mode 100644 index 0000000000..50a1f34525 --- /dev/null +++ b/src/Mod/Path/PathScripts/nc/makerbot_codes.py @@ -0,0 +1,128 @@ +################################################################################ +# makerbot_codes.py +# +# a lot like iso_codes.py but with reprap/makerbot specific M codes. +# +# Brad Collette, 12th Sept 2010 +# +# Many of these codes have nothing to do with reprap/additive machining but are left here in anticipation of future hybrid machines. + +class Codes(): + def SPACE(self): return(' ') + def FORMAT_FEEDRATE(self): return('%.2f') + def FORMAT_IN(self): return('%.5f') + def FORMAT_MM(self): return('%.3f') + def FORMAT_ANG(self): return('%.1f') + def FORMAT_TIME(self): return('%.2f') + def FORMAT_DWELL(self): return('P%f') + + def BLOCK(self): return('N%i' + self.SPACE()) + def COMMENT(self,comment): return( (' (%s)\n' % comment ) ) + def VARIABLE(self): return( '#%i') + def VARIABLE_SET(self): return( '=%.3f') + + def PROGRAM(self): return( 'O%i') + def PROGRAM_END(self): return( 'M02') + + def SUBPROG_CALL(self): return( 'M98' + self.SPACE() + 'P%i') + def SUBPROG_END(self): return( 'M99') + + def STOP_OPTIONAL(self): return('M01') + def STOP(self): return('M00') + + def IMPERIAL(self): return(self.SPACE() + 'G20') + def METRIC(self): return(self.SPACE() + 'G21' + self.SPACE()) + def ABSOLUTE(self): return(self.SPACE() + 'G90' + self.SPACE()) + def INCREMENTAL(self): return(self.SPACE() + 'G91') + def SET_TEMPORARY_COORDINATE_SYSTEM(self): return('G92' + self.SPACE()) + def REMOVE_TEMPORARY_COORDINATE_SYSTEM(self): return('G92.1' + self.SPACE()) + def POLAR_ON(self): return(self.SPACE() + 'G16') + def POLAR_OFF(self): return(self.SPACE() + 'G15') + def PLANE_XY(self): return(self.SPACE() + 'G17') + def PLANE_XZ(self): return(self.SPACE() + 'G18') + def PLANE_YZ(self): return(self.SPACE() + 'G19') + + def TOOL(self): return(self.SPACE() +'T%i') + def TOOL_DEFINITION(self): return('G10' + self.SPACE() + 'L1' + self.SPACE()) + + def WORKPLANE(self): return('G%i') + def WORKPLANE_BASE(self): return(53) + + def FEEDRATE(self): return((self.SPACE() + ' F')) + def SPINDLE(self, format, speed): return(self.SPACE() + 'S' + (format % speed)) + def SPINDLE_CW(self): return(self.SPACE() + 'M03') + def SPINDLE_CCW(self): return(self.SPACE() + 'M04') + def COOLANT_OFF(self): return(self.SPACE() + 'M09') + def COOLANT_MIST(self): return(self.SPACE() + 'M07') + def COOLANT_FLOOD(self): return(self.SPACE() + 'M08') + def GEAR_OFF(self): return(self.SPACE() + '?') + def GEAR(self): return('M%i') + def GEAR_BASE(self): return(37) + + def RAPID(self): return('G0') + def FEED(self): return('G1') + def ARC_CW(self): return('G2') + def ARC_CCW(self): return('G3') + def DWELL(self): return('G04') + def DRILL(self): return(self.SPACE() + 'G81') + def DRILL_WITH_DWELL(self, format, dwell): return(self.SPACE() + 'G82' + (format % dwell)) + def PECK_DRILL(self): return(self.SPACE() + 'G83') + def PECK_DEPTH(self, format, depth): return(self.SPACE() + 'Q' + (format % depth)) + def RETRACT(self, format, height): return(self.SPACE() + 'R' + (format % height)) + def END_CANNED_CYCLE(self): return(self.SPACE() + 'G80') + + def X(self): return(self.SPACE() + 'X') + def Y(self): return(self.SPACE() + 'Y') + def Z(self): return(self.SPACE() + 'Z') + def A(self): return(self.SPACE() + 'A') + def B(self): return(self.SPACE() + 'B') + def C(self): return(self.SPACE() + 'C') + def CENTRE_X(self): return(self.SPACE() + 'I') + def CENTRE_Y(self): return(self.SPACE() + 'J') + def CENTRE_Z(self): return(self.SPACE() + 'K') + def RADIUS(self): return(self.SPACE() + 'R') + def TIME(self): return(self.SPACE() + 'P') + + def PROBE_TOWARDS_WITH_SIGNAL(self): return('G38.2' + self.SPACE()) + def PROBE_TOWARDS_WITHOUT_SIGNAL(self): return('G38.3' + self.SPACE()) + def PROBE_AWAY_WITH_SIGNAL(self): return('G38.4' + self.SPACE()) + def PROBE_AWAY_WITHOUT_SIGNAL(self): return('G38.5' + self.SPACE()) + + def MACHINE_COORDINATES(self): return('G53' + self.SPACE()) + + def EXTRUDER_ON (self): return('M101') #deprecated + def EXTRUDER_OFF (self): return('M103') + def EXTRUDER_TEMP (self, degree_celsius): return('M104 S' + '%s' % degree_celsius) + def EXTRUDER_TEMP_WAIT (self, degree_celsius): return('M109 S' + '%s' % degree_celsius) + def READ_EXTRUDER_TEMP (self): return('M105') + def EXTRUDER_SPEED_PWM (self, speed_in_PWM): return('M108 S' + '%s' % speed_in_PWM) #deprecated + def EXTRUDER_SPEED_RPM (self, speed_in_RPM): return('M108 P' + '%s' % speed_in_RPM) #deprecated + + def STEPPERS_OFF(self): return(self.SPACE() + 'M118') + + def ALL_WAIT (self): return(self.SPACE() + 'M116') # Wait for all temperature and slow-changing variables to reach set values + + def FAN_ON (self): return(self.SPACE() + 'M106') + def FAN_OFF (self): return(self.SPACE() + 'M107') + + def VALVE_OPEN (self, delay): return(self.SPACE() + ('M126 P' + '%' % delay) ) + def VALVE_CLOSE (self, delay): return(self.SPACE() + ('M127 P' + '%' % delay) ) + + def BUILD_BED_TEMP (self, degree_celsius): return('M140 S' + '%s' % degree_celsius) + def BED_HOLDING_PRESSURE (self, pressure): return('M142 S' + '%s' % pressure) + + def CHAMBER_TEMP (self, degree_celsius): return('M141 S' + '%s' % degree_celsius) + +#The following codes are listed on the reprap wiki page at http://reprap.org/wiki/Mendel_User_Manual:_RepRapGCodes but require more study. +# +#G28 G Y Xnnn Ynnn Znnn Move to origin (on specified axes only, if X/Y/Z parameters are present) +#M105 M N none Request current extruder and base temperatures (in Celsius) +#M110 M N none Set current line number to Nxxx value preceeding command +#M111 M N Snnn Set debug level bitfield to value of parameter (default 6) +#M112 M N none Emergency stop (stop immediately, discarding any buffered commands) +#M113 M N Snnn Set Extruder PWM (to value defined by pot, or to parameter value if present) +#M114 M N none Get Current Position (return current X, Y, Z and E values) +#M117 M N none Get Zero Position (return X, Y, Z and E values of endstop hits) + + +codes = Codes() diff --git a/src/Mod/Path/PathScripts/nc/nc.py b/src/Mod/Path/PathScripts/nc/nc.py new file mode 100644 index 0000000000..66b9733e4c --- /dev/null +++ b/src/Mod/Path/PathScripts/nc/nc.py @@ -0,0 +1,636 @@ +################################################################################ +# nc.py +# +# Base class for NC code creation +# And global functions for calling current creator +# +# Hirutso Enni, 2009-01-13 +# altered by Dan Falck 2010-08-04 +# added tap() arguments Michael Haberler 2010-10-07 +################################################################################ + +ncOFF = 0 + +ncLEFT = -1 +ncRIGHT = +1 + +ncCW = -1 +ncCCW = +1 + +ncMIST = 1 +ncFLOOD = 2 + +################################################################################ +class Creator: + + def __init__(self): + pass + + ############################################################################ + ## Internals + + def file_open(self, name): + if name == "mem": + self.gcode = "" + else: + self.file = open(name, 'w') + self.filename = name + + def file_close(self): + if self.filename != "mem": + self.file.close() + + def write(self, s): + if self.filename == "mem": + self.gcode += s + else: + self.file.write(s) + def retrieve_gcode(self): + return self.gcode + + ############################################################################ + ## Programs + + def program_begin(self, id, name=''): + """Begin a program""" + pass + + def add_stock(self, type_name, params): + pass + + def program_stop(self, optional=False): + """Stop the machine""" + pass + + def program_end(self): + """End the program""" + pass + + def flush_nc(self): + """Flush all pending codes""" + pass + + ############################################################################ + ## Subprograms + + def sub_begin(self, id, name=''): + """Begin a subprogram""" + pass + + def sub_call(self, id): + """Call a subprogram""" + pass + + def sub_end(self): + """Return from a subprogram""" + pass + + ############################################################################ + ## Settings + + def imperial(self): + """Set imperial units""" + pass + + def metric(self): + """Set metric units""" + pass + + def absolute(self): + """Set absolute coordinates""" + pass + + def incremental(self): + """Set incremental coordinates""" + pass + + def polar(self, on=True): + """Set polar coordinates""" + pass + + def set_plane(self, plane): + """Set plane""" + pass + + def set_temporary_origin(self, x=None, y=None, z=None, a=None, b=None, c=None): + """Set temporary origin G92""" + pass + + def remove_temporary_origin(self): + """Remote temporary origin G92.1""" + pass + + ############################################################################ + ## Tools + + def tool_change(self, id): + """Change the tool""" + pass + + def tool_defn(self, id, name='', params=None): + """Define a tool""" + pass + + def offset_radius(self, id, radius=None): + """Set tool radius offsetting""" + pass + + def offset_length(self, id, length=None): + """Set tool length offsetting""" + pass + + def current_tool(self): + return None + + ############################################################################ + ## Datums + + def datum_shift(self, x=None, y=None, z=None, a=None, b=None, c=None): + """Shift the datum""" + pass + + def datum_set(self, x=None, y=None, z=None, a=None, b=None, c=None): + """Set the datum""" + pass + + def workplane(self, id): + """Set the workplane""" + pass + + def clearanceplane(self,z=None): + """set clearance plane""" + pass + + ############################################################################ + ## APT360 like Transformation Definitions + ## These definitions were created while looking at Irvin Kraal's book on APT + ## - Numerical Control Progamming in APT - page 211 + + def matrix(self,a1=None,b1=None,c1=None,a2=None,b2=None,c2=None,a3=None,b3=None,c3=None): + """Create a matrix for transformations""" + pass + def translate(self,x=None,y=None,z=None): + """Translate in x,y,z direction""" + pass + def rotate(self,xyrot=None,yzrot=None,zxrot=None,angle=None): + """Rotate about a coordinate axis""" + pass + def scale(self,k=None): + """Scale by factor k""" + pass + def matrix_product(self,matrix1=None,matrix2=None): + """Create matrix that is the product of two other matrices""" + pass + def mirror_plane(self,plane1=None,plane2=None,plane3=None): + """Mirror image about one or more coordinate planes""" + pass + def mirror_line(self,line=None): + """Mirror about a line""" + pass + + + ############################################################################ + ## Rates + Modes + + def feedrate(self, f): + """Set the feedrate""" + pass + + def feedrate_hv(self, fh, fv): + """Set the horizontal and vertical feedrates""" + pass + + def spindle(self, s, clockwise=True): + """Set the spindle speed""" + pass + + def coolant(self, mode=0): + """Set the coolant mode""" + pass + + def gearrange(self, gear=0): + """Set the gear range""" + pass + + ############################################################################ + ## Moves + + def rapid(self, x=None, y=None, z=None): + """Rapid move""" + pass + + def feed(self, x=None, y=None, z=None): + """Feed move""" + pass + + def arc_cw(self, x=None, y=None, z=None, i=None, j=None, k=None, r=None): + """Clockwise arc move""" + pass + + def arc_ccw(self, x=None, y=None, z=None, i=None, j=None, k=None, r=None): + """Counterclockwise arc move""" + pass + + def dwell(self, t): + """Dwell""" + pass + + def rapid_home(self, x=None, y=None, z=None): + """Rapid relative to home position""" + pass + + def rapid_unhome(self): + """Return from rapid home""" + pass + + def set_machine_coordinates(self): + """Set machine coordinates""" + pass + + ############################################################################ + ## Cutter radius compensation + + def use_CRC(self): + """CRC""" + return False + + ############################################################################ + ## Cycles + + def pattern(self): + """Simple pattern eg. circle, rect""" + pass + + def pocket(self): + """Pocket routine""" + pass + + def profile(self): + """Profile routine""" + pass + + def drill(self, x=None, y=None, dwell=None, depthparams = None, retract_mode=None, spindle_mode=None, internal_coolant_on=None, rapid_to_clearance=None): + """Drilling routines""" + pass + + # original prototype was: + # def tap(self, x=None, y=None, z=None, zretract=None, depth=None, standoff=None, dwell_bottom=None, pitch=None, stoppos=None, spin_in=None, spin_out=None): + # + # current call is like so: + # tap(x=10, y=10, z=0, tap_mode=0, depth=12.7, standoff=6.35, direction=0, pitch=1.25) + # just add tap_mode & direction parameters + + def tap(self, x=None, y=None, z=None, zretract=None, depth=None, standoff=None, dwell_bottom=None, pitch=None, stoppos=None, spin_in=None, spin_out=None, tap_mode=None, direction=None): + """Tapping routines""" + pass + + def bore(self, x=None, y=None, z=None, zretract=None, depth=None, standoff=None, dwell_bottom=None, feed_in=None, feed_out=None, stoppos=None, shift_back=None, shift_right=None, backbore=False, stop=False): + """Boring routines""" + pass + + def end_canned_cycle(self): + pass + + ############################################################################ + ## Misc + + def comment(self, text): + """Insert a comment""" + pass + + def insert(self, text): + """APT style INSERT statement""" + pass + + def block_delete(self, on=False): + """block to ignore if block delete switch is on""" + pass + + def variable(self, id): + """Insert a variable""" + pass + + def variable_set(self, id, value): + """Set a variable""" + pass + + def probe_linear_centre_outside(self, x1=None, y1=None, depth=None, x2=None, y2=None ): + pass + + def probe_single_point(self, point_along_edge_x=None, point_along_edge_y=None, depth=None, retracted_point_x=None, retracted_point_y=None, destination_point_x=None, destination_point_y=None, intersection_variable_x=None, intersection_variable_y=None, probe_offset_x_component=None, probe_offset_y_component=None ): + pass + + def probe_downward_point(self, x=None, y=None, depth=None, intersection_variable_z=None): + pass + + def report_probe_results(self, x1=None, y1=None, z1=None, x2=None, y2=None, z2=None, x3=None, y3=None, z3=None, x4=None, y4=None, z4=None, x5=None, y5=None, z5=None, x6=None, y6=None, z6=None, xml_file_name=None ): + pass + + def open_log_file(self, xml_file_name=None ): + pass + + def log_coordinate(self, x=None, y=None, z=None): + pass + + def log_message(self, message=None): + pass + + def close_log_file(self): + pass + + def rapid_to_midpoint(self, x1=None, y1=None, z1=None, x2=None, y2=None, z2=None): + pass + + def rapid_to_intersection(self, x1, y1, x2, y2, x3, y3, x4, y4, intersection_x, intersection_y, ua_numerator, ua_denominator, ua, ub_numerator, ub): + pass + + def rapid_to_rotated_coordinate(self, x1, y1, x2, y2, ref_x, ref_y, x_current, y_current, x_final, y_final): + pass + + def set_path_control_mode(self, mode, motion_blending_tolerance, naive_cam_tolerance ): + pass + + +################################################################################ + +creator = Creator() + +############################################################################ +## Internals + +def write(s): + creator.write(s) + +def output(filename): + creator.file_open(filename) + +def retrieve_gcode(): + return creator.retrieve_gcode() + + +############################################################################ +## Programs + +def program_begin(id, name=''): + creator.program_begin(id, name) + +def add_stock(type_name, params): + creator.add_stock(type_name, params) + +def program_stop(optional=False): + creator.program_stop(optional) + +def program_end(): + creator.program_end() + +def flush_nc(): + creator.flush_nc() + +############################################################################ +## Subprograms + +def sub_begin(id, name=''): + creator.sub_begin(id, name) + +def sub_call(id): + creator.sub_call(id) + +def sub_end(): + creator.sub_end() + +############################################################################ +## Settings + +def imperial(): + creator.imperial() + +def metric(): + creator.metric() + +def absolute(): + creator.absolute() + +def incremental(): + creator.incremental() + +def polar(on=True): + creator.polar(on) + +def set_plane(plane): + creator.set_plane(plane) + +def set_temporary_origin(x=None, y=None, z=None, a=None, b=None, c=None): + creator.set_temporary_origin(x,y,z,a,b,c) + +def remove_temporary_origin(): + creator.remove_temporary_origin() + +############################################################################ +## Tools + +def tool_change(id): + creator.tool_change(id) + +def tool_defn(id, name='', params=None): + creator.tool_defn(id, name, params) + +def offset_radius(id, radius=None): + creator.offset_radius(id, radius) + +def offset_length(id, length=None): + creator.offset_length(id, length) + +def current_tool(self): + return creator.current_tool() + +############################################################################ +## Datums + +def datum_shift(x=None, y=None, z=None, a=None, b=None, c=None): + creator.datum_shift(x, y, z, a, b, c) + +def datum_set(x=None, y=None, z=None, a=None, b=None, c=None): + creator.datum_set(x, y, z, a, b, c) + +def workplane(id): + creator.workplane(id) + +def clearanceplane(z=None): + creator.clearanceplane(z) + +############################################################################ +## APT360 like Transformation Definitions +## These definitions were created while looking at Irvin Kraal's book on APT +## - Numerical Control Progamming in APT - page 211 + +def matrix(a1=None,b1=None,c1=None,a2=None,b2=None,c2=None,a3=None,b3=None,c3=None): + creator.matrix(a1,b1,c1,a2,b2,c2,a3,b3,c3) + +def translate(x=None,y=None,z=None): + creator.translate(x,y,z) + +def rotate(xyrot=None,yzrot=None,zxrot=None,angle=None): + creator.rotate(xyrot,yzrot,zxrot,angle) + +def scale(k=None): + creator.scale(k) + +def matrix_product(matrix1=None,matrix2=None): + creator.matrix_product(matrix1,matrix2) + +def mirror_plane(plane1=None,plane2=None,plane3=None): + creator.mirror_plane(plane1,plane2,plane3) + +def mirror_line(line=None): + creator.mirror_line(line) + + + +############################################################################ +## Rates + Modes + +def feedrate(f): + creator.feedrate(f) + +def feedrate_hv(fh, fv): + creator.feedrate_hv(fh, fv) + +def spindle(s, clockwise=True): + creator.spindle(s, clockwise) + +def coolant(mode=0): + creator.coolant(mode) + +def gearrange(gear=0): + creator.gearrange(gear) + +############################################################################ +## Moves + +def rapid(x=None, y=None, z=None): + creator.rapid(x, y, z) + +def feed(x=None, y=None, z=None): + creator.feed(x, y, z) + +def arc_cw(x=None, y=None, z=None, i=None, j=None, k=None, r=None): + creator.arc_cw(x, y, z, i, j, k, r) + +def arc_ccw(x=None, y=None, z=None, i=None, j=None, k=None, r=None): + creator.arc_ccw(x, y, z, i, j, k, r) + +def dwell(t): + creator.dwell(t) + +def rapid_home(x=None, y=None, z=None): + creator.rapid_home(x, y, z) + +def rapid_unhome(): + creator.rapid_unhome() + +def set_machine_coordinates(): + creator.set_machine_coordinates() + +############################################################################ +## Cutter radius compensation + +def use_CRC(): + return creator.use_CRC() + +def CRC_nominal_path(): + return creator.CRC_nominal_path() + +def start_CRC(left = True, radius = 0.0): + creator.start_CRC(left, radius) + +def end_CRC(): + creator.end_CRC() + +############################################################################ +## Cycles + +def pattern(): + creator.pattern() + +def pocket(): + creator.pocket() + +def profile(): + creator.profile() + +def drill(x=None, y=None, dwell=None, depthparams = None, retract_mode=None, spindle_mode=None, internal_coolant_on=None, rapid_to_clearance=None): + creator.drill(x, y, dwell, depthparams, retract_mode, spindle_mode, internal_coolant_on, rapid_to_clearance) + + +def tap(x=None, y=None, z=None, zretract=None, depth=None, standoff=None, dwell_bottom=None, pitch=None, stoppos=None, spin_in=None, spin_out=None, tap_mode=None, direction=None): + creator.tap(x, y, z, zretract, depth, standoff, dwell_bottom, pitch, stoppos, spin_in, spin_out, tap_mode, direction) + +def bore(x=None, y=None, z=None, zretract=None, depth=None, standoff=None, dwell_bottom=None, feed_in=None, feed_out=None, stoppos=None, shift_back=None, shift_right=None, backbore=False, stop=False): + creator.bore(x, y, z, zretract, depth, standoff, dwell_Bottom, feed_in, feed_out, stoppos, shift_back, shift_right, backbore, stop) + +def end_canned_cycle(): + creator.end_canned_cycle() + +def peck(count, first, last=None, step=0.0): + pecks = [] + peck = first + if (last == None) : last = first + for i in range(0,count): + pecks.append(peck) + if (peck - step > last) : peck -= step + return pecks + +############################################################################ +## Misc + +def comment(text): + creator.comment(text) + +def insert(text): + creator.insert(text) + +def block_delete(on=False): + creator.block_delete(on) + +def variable(id): + creator.variable(id) + +def variable_set(id, value): + creator.variable_set(id, value) + +def probe_single_point(point_along_edge_x=None, point_along_edge_y=None, depth=None, retracted_point_x=None, retracted_point_y=None, destination_point_x=None, destination_point_y=None, intersection_variable_x=None, intersection_variable_y=None, probe_offset_x_component=None, probe_offset_y_component=None ): + creator.probe_single_point(point_along_edge_x, point_along_edge_y, depth, retracted_point_x, retracted_point_y, destination_point_x, destination_point_y, intersection_variable_x, intersection_variable_y, probe_offset_x_component, probe_offset_y_component ) + +def probe_downward_point(x=None, y=None, depth=None, intersection_variable_z=None): + creator.probe_downward_point(x, y, depth, intersection_variable_z) + +def report_probe_results(x1=None, y1=None, z1=None, x2=None, y2=None, z2=None, x3=None, y3=None, z3=None, x4=None, y4=None, z4=None, x5=None, y5=None, z5=None, x6=None, y6=None, z6=None, xml_file_name=None ): + creator.report_probe_results(x1, y1, z1, x2, y2, z2, x3, y3, z3, x4, y4, z4, x5, y5, z5, x6, y6, z6, xml_file_name) + +def open_log_file(xml_file_name=None ): + creator.open_log_file(xml_file_name) + +def log_coordinate(x=None, y=None, z=None): + creator.log_coordinate(x, y, z) + +def log_message(message=None): + creator.log_message(message) + +def close_log_file(): + creator.close_log_file() + +def rapid_to_midpoint(x1=None, y1=None, z1=None, x2=None, y2=None, z2=None): + creator.rapid_to_midpoint(x1, y1, z1, x2, y2, z2) + +def rapid_to_intersection(x1, y1, x2, y2, x3, y3, x4, y4, intersection_x, intersection_y, ua_numerator, ua_denominator, ua, ub_numerator, ub): + creator.rapid_to_intersection(x1, y1, x2, y2, x3, y3, x4, y4, intersection_x, intersection_y, ua_numerator, ua_denominator, ua, ub_numerator, ub) + +def rapid_to_rotated_coordinate(x1, y1, x2, y2, ref_x, ref_y, x_current, y_current, x_final, y_final): + creator.rapid_to_rotated_coordinate(x1, y1, x2, y2, ref_x, ref_y, x_current, y_current, x_final, y_final) + +def set_path_control_mode(mode, motion_blending_tolerance, naive_cam_tolerance ): + creator.set_path_control_mode(mode, motion_blending_tolerance, naive_cam_tolerance ) + + + + diff --git a/src/Mod/Path/PathScripts/nc/nc_read.py b/src/Mod/Path/PathScripts/nc/nc_read.py new file mode 100644 index 0000000000..1eadbd5294 --- /dev/null +++ b/src/Mod/Path/PathScripts/nc/nc_read.py @@ -0,0 +1,171 @@ +################################################################################ +# nc_read.py +# +# Base class for NC code parsing + +################################################################################ +import area +import math +count = 0 + +class Program: # stores start and end lines of programs and subroutines + def __init__(self): + self.start_line = None + self.end_line = None + +class Parser: + + def __init__(self, writer): + self.writer = writer + self.currentx = None + self.currenty = None + self.currentz = None + self.absolute_flag = True + self.drillz = None + self.need_m6_for_t_change = True + + def __del__(self): + self.file_in.close() + + ############################################################################ + ## Internals + + def readline(self): + self.line = self.file_in.readline().rstrip() + if (len(self.line)) : return True + else : return False + + def set_current_pos(self, x, y, z): + if (x != None) : + if self.absolute_flag or self.currentx == None: self.currentx = x + else: self.currentx = self.currentx + x + if (y != None) : + if self.absolute_flag or self.currenty == None: self.currenty = y + else: self.currenty = self.currenty + y + if (z != None) : + if self.absolute_flag or self.currentz == None: self.currentz = z + else: self.currentz = self.currentz + z + + def incremental(self): + self.absolute_flag = False + + def absolute(self): + self.absolute_flag = True + + def Parse(self, name): + self.file_in = open(name, 'r') + + self.path_col = None + self.f = None + self.arc = 0 + self.q = None + self.r = None + self.drilling = None + self.drilling_uses_clearance = False + self.drilling_clearance_height = None + + while (self.readline()): + self.a = None + self.b = None + self.c = None + self.h = None + self.i = None + self.j = None + self.k = None + self.p = None + self.s = None + self.x = None + self.y = None + self.z = None + self.t = None + self.m6 = False + + self.writer.begin_ncblock() + + self.move = False + self.height_offset = False + self.drill = False + self.drill_off = False + self.no_move = False + + words = self.pattern_main.findall(self.line) + for word in words: + self.col = None + self.cdata = False + self.ParseWord(word) + self.writer.add_text(word, self.col, self.cdata) + + if self.t != None: + if (self.m6 == True) or (self.need_m6_for_t_change == False): + self.writer.tool_change( self.t ) + + if self.height_offset and (self.z != None): + self.drilling_clearance_height = self.z + + if self.drill: + self.drilling = True + + if self.drill_off: + self.drilling = False + + if self.drilling: + rapid_z = self.r + if self.drilling_uses_clearance and (self.drilling_clearance_height != None): + rapid_z = self.drilling_clearance_height + if self.z != None: self.drillz = self.z + self.writer.rapid(self.x, self.y, rapid_z) + self.writer.feed(self.x, self.y, self.drillz) + self.writer.feed(self.x, self.y, rapid_z) + + else: + if (self.move and not self.no_move): + if (self.arc==0): + if self.path_col == "feed": + self.writer.feed(self.x, self.y, self.z) + else: + self.writer.rapid(self.x, self.y, self.z, self.a, self.b, self.c) + else: + i = self.i + j = self.j + k = self.k + if self.arc_centre_absolute == True: + pass + else: + if (self.arc_centre_positive == True) and (self.oldx != None) and (self.oldy != None): + x = self.oldx + if self.x != None: x = self.x + if (self.x > self.oldx) != (self.arc > 0): + j = -j + y = self.oldy + if self.y != None: y = self.y + if (self.y > self.oldy) != (self.arc < 0): + i = -i + + #fix centre point + r = math.sqrt(i*i + j*j) + p0 = area.Point(self.oldx, self.oldy) + p1 = area.Point(x, y) + v = p1 - p0 + l = v.length() + h = l/2 + d = math.sqrt(r*r - h*h) + n = area.Point(-v.y, v.x) + n.normalize() + if self.arc == -1: d = -d + c = p0 + (v * 0.5) + (n * d) + i = c.x + j = c.y + + else: + i = i + self.oldx + j = j + self.oldy + if self.arc == -1: + self.writer.arc_cw(self.x, self.y, self.z, i, j, k) + else: + self.writer.arc_ccw(self.x, self.y, self.z, i, j, k) + if self.x != None: self.oldx = self.x + if self.y != None: self.oldy = self.y + if self.z != None: self.oldz = self.z + self.writer.end_ncblock() + + diff --git a/src/Mod/Path/PathScripts/nc/nclathe_read.py b/src/Mod/Path/PathScripts/nc/nclathe_read.py new file mode 100644 index 0000000000..2d174c5b28 --- /dev/null +++ b/src/Mod/Path/PathScripts/nc/nclathe_read.py @@ -0,0 +1,151 @@ +################################################################################ +# nc_read.py +# +# Base class for NC code parsing +# +# Hirutso Enni, 2009-01-13 + +################################################################################ +class Parser: + + def __init__(self): + self.currentx = 0.0 + self.currenty = 0.0 + self.currentz = 0.0 + self.absolute_flag = True + + ############################################################################ + ## Internals + + def files_open(self, name, oname=None): + if (oname == None ): + oname = (name+'.nc.xml') + self.file_in = open(name, 'r') + self.file_out = open(oname, 'w') + + self.file_out.write('\n') + self.file_out.write('\n') + + def files_close(self): + self.file_out.write('\n') + + self.file_in.close() + self.file_out.close() + + def readline(self): + self.line = self.file_in.readline().rstrip() + if (len(self.line)) : return True + else : return False + + def write(self, s): + self.file_out.write(s) + + ############################################################################ + ## Xml + + def begin_ncblock(self): + self.file_out.write('\t\n') + + def end_ncblock(self): + self.file_out.write('\t\n') + + def add_text(self, s, col=None, cdata=False): + s.replace('&', '&') + s.replace('"', '"') + s.replace('<', '<') + s.replace('>', '>') + if (cdata) : (cd1, cd2) = ('') + else : (cd1, cd2) = ('', '') + if (col != None) : self.file_out.write('\t\t'+cd1+s+cd2+'\n') + else : self.file_out.write('\t\t'+cd1+s+cd2+'\n') + + def set_mode(self, units=None): + self.file_out.write('\t\t\n') + + def set_tool(self, number=None): + self.file_out.write('\t\t\n') + + def begin_path(self, col=None): + if (col != None) : self.file_out.write('\t\t\n') + else : self.file_out.write('\t\t\n') + + def end_path(self): + self.file_out.write('\t\t\n') + + def add_line(self, x=None, y=None, z=None, a=None, b=None, c=None): + if (x == None and y == None and z == None and a == None and b == None and c == None) : return + self.file_out.write('\t\t\t\n') + + + def add_lathe_increment_line(self, u=None, w=None): +# needed for representing U and W moves in lathe code- these are non modal incremental moves +# U == X and W == Z + if (u == None and w == None ) : return + self.file_out.write('\t\t\t\n') + + + + + + + def add_arc(self, x=None, y=None, z=None, i=None, j=None, k=None, r=None, d=None): + if (x == None and y == None and z == None and i == None and j == None and k == None and r == None and d == None) : return + self.file_out.write('\t\t\t\n') + + def incremental(self): + self.absolute_flag = False + + def absolute(self): + self.absolute_flag = True diff --git a/src/Mod/Path/PathScripts/nc/num_reader.py b/src/Mod/Path/PathScripts/nc/num_reader.py new file mode 100644 index 0000000000..c43e5e25e2 --- /dev/null +++ b/src/Mod/Path/PathScripts/nc/num_reader.py @@ -0,0 +1,63 @@ +import nc_read as nc +import sys +import math + +# a base class for hpgl parsers, and maybe others + +class NumReader(nc.Parser): + + def __init__(self, writer): + nc.Parser.__init__(self, writer) + + def get_number(self): + number = '' + + # skip spaces and commas at start of number + while(self.line_index < self.line_length): + c = self.line[self.line_index] + if c == ' ' or c == ',': + self.parse_word += c + else: + break + self.line_index = self.line_index + 1 + + while(self.line_index < self.line_length): + c = self.line[self.line_index] + if c == '.' or c == '0' or c == '1' or c == '2' or c == '3' or c == '4' or c == '5' or c == '6' or c == '7' or c == '8' or c == '9' or c == '-': + number += c + else: + break + self.parse_word += c + self.line_index = self.line_index + 1 + + return number + + def add_word(self, color): + self.writer.add_text(self.parse_word, color, None) + self.parse_word = "" + + def Parse(self, name): + self.file_in = open(name, 'r') + + while self.readline(): + self.writer.begin_ncblock() + + self.parse_word = "" + self.line_index = 0 + self.line_length = len(self.line) + + while self.line_index < self.line_length: + c = self.line[self.line_index] + self.parse_word += c + + self.ParseFromFirstLetter(c) + + self.line_index = self.line_index + 1 + + self.writer.add_text(self.parse_word, None, None) + + self.writer.end_ncblock() + + self.file_in.close() + + diff --git a/src/Mod/Path/PathScripts/nc/printbot3d.py b/src/Mod/Path/PathScripts/nc/printbot3d.py new file mode 100644 index 0000000000..3ea45867fb --- /dev/null +++ b/src/Mod/Path/PathScripts/nc/printbot3d.py @@ -0,0 +1,53 @@ +################################################################################ +# printbot3d.py +# +# Dan Heeks 18th October 2010 + +import nc +import iso_modal +import math + +################################################################################ +class CreatorPrintbot(iso_modal.CreatorIsoModal): + + def __init__(self): + iso_modal.CreatorIsoModal.__init__(self) + + def tool_defn(self, id, name='', params=None): + pass + + def write_blocknum(self): + pass + + def set_plane(self, plane): + pass + + def workplane(self, id): + pass + +# Extruder Control + + def extruder_on(self): + self.write('M101\n') + + def extruder_off(self): + self.write('M103\n') + + def set_extruder_flowrate(self, flowrate): + # re-use the spindle speed function + self.spindle(flowrate, True) + + def extruder_temp(self, temp): + self.write((maker.codes.EXTRUDER_TEMP(temp)) + ('\n')) + +# General + def rapid(x=None, y=None, z=None, a=None, b=None, c=None): + # do a G1 even for rapid moves + iso_modal.CreatorIsoModal.feed(self, x, y, z) + + def feed(self, x=None, y=None, z=None, a = None, b = None, c = None): + iso_modal.CreatorIsoModal.feed(self, x, y, z) + +################################################################################ + +nc.creator = CreatorPrintbot() diff --git a/src/Mod/Path/PathScripts/nc/printbot3d_read.py b/src/Mod/Path/PathScripts/nc/printbot3d_read.py new file mode 100644 index 0000000000..4cdc6ce6ca --- /dev/null +++ b/src/Mod/Path/PathScripts/nc/printbot3d_read.py @@ -0,0 +1,17 @@ +import iso_read as iso +import sys + +# based on the iso reader + +class Parser(iso.Parser): + def __init__(self, writer): + iso.Parser.__init__(self, writer) + + def ParseWord(self, word): + iso.Parser.ParseWord(self, word) + if (word == 'M103'): + self.path_col = "rapid" + self.col = "rapid" + elif (word == 'M101'): + self.path_col = "feed" + self.col = "feed" diff --git a/src/Mod/Path/PathScripts/nc/recreator.py b/src/Mod/Path/PathScripts/nc/recreator.py new file mode 100644 index 0000000000..be57feba88 --- /dev/null +++ b/src/Mod/Path/PathScripts/nc/recreator.py @@ -0,0 +1,300 @@ +import nc + +units = 1.0 + +class Redirector(nc.Creator): + + def __init__(self, original): + nc.Creator.__init__(self) + + self.original = original + self.x = None + self.y = None + self.z = None + if original.x != None: self.x = original.x * units + if original.y != None: self.y = original.y * units + if original.z != None: self.z = original.z * units + self.imperial = False + + def cut_path(self): + pass + + ############################################################################ + ## Programs + def write(self, s): + self.original.write(s) + + def output_fixture(self): + self.original.output_fixture() + + def increment_fixture(self): + self.original.increment_fixture() + + def get_fixture(self): + return self.original.get_fixture() + + def set_fixture(self, fixture): + self.original.set_fixture(fixture) + + def program_begin(self, id, name=''): + self.cut_path() + self.original.program_begin(id, name) + + def program_stop(self, optional=False): + self.cut_path() + self.original.program_stop(optional) + + def program_end(self): + self.cut_path() + self.original.program_end() + + def flush_nc(self): + self.cut_path() + self.original.flush_nc() + + ############################################################################ + ## Subprograms + + def sub_begin(self, id, name=None): + self.cut_path() + self.original.sub_begin(id, name) + + def sub_call(self, id): + self.cut_path() + self.original.sub_call(id) + + def sub_end(self): + self.cut_path() + self.original.sub_end() + + def disable_output(self): + self.original.disable_output() + + def enable_output(self): + self.original.enable_output() + + ############################################################################ + ## Settings + + def imperial(self): + self.cut_path() + self.imperial = True + self.original.imperial() + + def metric(self): + self.cut_path() + self.original.metric() + + def absolute(self): + self.cut_path() + self.original.absolute() + + def incremental(self): + self.cut_path() + self.original.incremental() + + def polar(self, on=True): + self.cut_path() + self.original.polar(on) + + def set_plane(self, plane): + self.cut_path() + self.original.set_plane(plane) + + def set_temporary_origin(self, x=None, y=None, z=None, a=None, b=None, c=None): + self.cut_path() + self.original.set_temporary_origin(x,y,z,a,b,c) + + def remove_temporary_origin(self): + self.cut_path() + self.original.remove_temporary_origin() + + ############################################################################ + ## Tools + + def tool_change(self, id): + self.cut_path() + self.original.tool_change(id) + + def tool_defn(self, id, name='', params=None): + self.cut_path() + self.original.tool_defn(id, name, params) + + def offset_radius(self, id, radius=None): + self.cut_path() + self.original.offset_radius(id, radius) + + def offset_length(self, id, length=None): + self.cut_path() + self.original.offset_length(id, length) + + ############################################################################ + ## Datums + + def datum_shift(self, x=None, y=None, z=None, a=None, b=None, c=None): + self.cut_path() + self.original.datum_shift(x, y, z, a, b, c) + + def datum_set(self, x=None, y=None, z=None, a=None, b=None, c=None): + self.cut_path() + self.original.datum_set(x, y, z, a, b, c) + + def workplane(self, id): + self.cut_path() + self.original.workplane(id) + + ############################################################################ + ## Rates + Modes + + def feedrate(self, f): + self.cut_path() + self.original.feedrate(f) + + def feedrate_hv(self, fh, fv): + self.cut_path() + self.original.feedrate_hv(fh, fv) + + def spindle(self, s, clockwise=True): + self.cut_path() + self.original.spindle(s, clockwise) + + def coolant(self, mode=0): + self.cut_path() + self.original.coolant(mode) + + def gearrange(self, gear=0): + self.cut_path() + self.original.gearrange(gear) + + ############################################################################ + ## Moves + + def rapid(self, x=None, y=None, z=None, a=None, b=None, c=None): + self.cut_path() + self.original.rapid(x, y, z, a, b, c) + if x != None: self.x = x * units + if y != None: self.y = y * units + if z != None: self.z = z * units + + def cut_path(self): + pass + + def z2(self, z): + return z + + def feed(self, x=None, y=None, z=None, a = None, b = None, c = None): + px = self.x + py = self.y + pz = self.z + if x != None: self.x = x * units + if y != None: self.y = y * units + if z != None: self.z = z * units + if self.x == None or self.y == None or self.z == None: + self.cut_path() + self.original.feed(x, y, z) + return + if px == self.x and py == self.y: + # z move only + self.cut_path() + self.original.feed(self.x/units, self.y/units, self.z2(self.z)/units) + return + + def arc(self, x=None, y=None, z=None, i=None, j=None, k=None, r=None, ccw = True): + if self.x == None or self.y == None or self.z == None: + raise "first attached move can't be an arc" + px = self.x + py = self.y + pz = self.z + if x != None: self.x = x * units + if y != None: self.y = y * units + if z != None: self.z = z * units + + def arc_cw(self, x=None, y=None, z=None, i=None, j=None, k=None, r=None): + self.arc(x, y, z, i, j, k, r, False) + + def arc_ccw(self, x=None, y=None, z=None, i=None, j=None, k=None, r=None): + self.arc(x, y, z, i, j, k, r, True) + + def dwell(self, t): + self.cut_path() + self.original.dwell(t) + + def rapid_home(self, x=None, y=None, z=None, a=None, b=None, c=None): + self.cut_path() + self.original.rapid_home(x, y, z, a, b, c) + + def rapid_unhome(self): + self.cut_path() + self.original.rapid_unhome() + + ############################################################################ + ## Cutter radius compensation + + def use_CRC(self): + return self.original.use_CRC() + + def start_CRC(self, left = True, radius = 0.0): + self.cut_path() + self.original.start_CRC(left, radius) + + def end_CRC(self): + self.cut_path() + self.original.end_CRC() + + ############################################################################ + ## Cycles + + def pattern(self): + self.cut_path() + self.original.pattern() + + def pattern_uses_subroutine(self): + return self.original.pattern_uses_subroutine() + + def pocket(self): + self.cut_path() + self.original.pocket() + + def profile(self): + self.cut_path() + self.original.profile() + + def circular_pocket(self, x=None, y=None, ToolDiameter=None, HoleDiameter=None, ClearanceHeight=None, StartHeight=None, MaterialTop=None, FeedRate=None, SpindleRPM=None, HoleDepth=None, DepthOfCut=None, StepOver=None ): + self.cut_path() + self.circular_pocket(x, y, ToolDiameter, HoleDiameter, ClearanceHeight, StartHeight, MaterialTop, FeedRate, SpindleRPM, HoleDepth, DepthOfCut, StepOver) + + def drill(self, x=None, y=None, dwell=None, depthparams = None, retract_mode=None, spindle_mode=None, internal_coolant_on=None, rapid_to_clearance=None): + self.cut_path() + self.original.drill(x, y, dwell, depthparams, spindle_mode, internal_coolant_on, rapid_to_clearance) + + # argument list adapted for compatibility with Tapping module + # wild guess - I'm unsure about the purpose of this file and wether this works -haberlerm + def tap(self, x=None, y=None, z=None, zretract=None, depth=None, standoff=None, dwell_bottom=None, pitch=None, stoppos=None, spin_in=None, spin_out=None, tap_mode=None, direction=None): + self.cut_path() + self.original.tap( x, y, self.z2(z), self.z2(zretract), depth, standoff, dwell_bottom, pitch, stoppos, spin_in, spin_out, tap_mode, direction) + + + def bore(self, x=None, y=None, z=None, zretract=None, depth=None, standoff=None, dwell_bottom=None, feed_in=None, feed_out=None, stoppos=None, shift_back=None, shift_right=None, backbore=False, stop=False): + self.cut_path() + self.original.bore(x, y, self.z2(z), self.z2(zretract), depth, standoff, dwell_Bottom, feed_in, feed_out, stoppos, shift_back, shift_right, backbore, stop) + + def end_canned_cycle(self): + self.original.end_canned_cycle() + + ############################################################################ + ## Misc + + def comment(self, text): + self.cut_path() + self.original.comment(text) + + def variable(self, id): + self.cut_path() + self.original.variable(id) + + def variable_set(self, id, value): + self.cut_path() + self.original.variable_set(id, value) + + def set_ocl_cutter(self, cutter): + self.original.set_ocl_cutter(cutter) diff --git a/src/Mod/Path/PathScripts/nc/rez2.py b/src/Mod/Path/PathScripts/nc/rez2.py new file mode 100644 index 0000000000..2035fe5f8c --- /dev/null +++ b/src/Mod/Path/PathScripts/nc/rez2.py @@ -0,0 +1,553 @@ +# -*- coding: utf-8 -*- +################################################################################ +# iso.py +# +# Simple ISO NC code creator +# +# Hirutso Enni, 2009-01-13 + +import nc +import math +import circular_pocket as circular + +################################################################################ +class Creator(nc.Creator): + + def __init__(self): + nc.Creator.__init__(self) + + self.a = 0 + self.b = 0 + self.c = 0 + self.f = '' + self.fh = None + self.fv = None + self.fhv = False + self.g = '' + self.i = 0 + self.j = 0 + self.k = 0 + self.m = [] + self.n = 10 + self.r = 0 + self.s = '' + self.t = None + self.x = 0 + self.y = 0 + self.z = 500 + + self.x1=0 + self.x2=0 + self.y1=0 + self.y2=0 + self.SPACE=' ' + + self.fmt = self.FORMAT_MM() + pass + ############################################################################ + ## Internals + def FORMAT_IN(self): return('%.5f') + def FORMAT_MM(self): return('%.3f') + def COMMENT(self,comment): return( ('(%s)' % comment ) ) + + def write_feedrate(self): + #self.write(self.f) + self.f = '' + + def write_preps(self): + #self.write(self.g) + self.g = '' + + def write_misc(self): + #if (len(self.m)) : self.write(self.m.pop()) + pass + + def write_blocknum(self): + #self.write(iso.BLOCK % self.n) + #self.write(iso.SPACE) + #self.n += 10 + pass + + def write_spindle(self): + #self.write(self.s) + #self.s = '' + pass + + ############################################################################ + ## Programs + + def program_begin(self, id, name=''): + #self.write((iso.PROGRAM % id) + iso.SPACE + (iso.COMMENT % name)) + #self.write("// program v jazyku"+self.SPACE+"REZ\nMA 0.0000 , 0.0000\n") + self.write("// program v jazyku"+self.SPACE+"REZ\nGO_LEFT\nMA 0.0000 , 0.0000\n") + #self.write('\n') + + def program_stop(self, optional=False): + #self.write_blocknum() + #if (optional) : self.write(iso.STOP_OPTIONAL + '\n') + #else : self.write(iso.STOP + '\n') + self.write("// stop programu v jazyku REZ\n") + + def program_end(self): + #self.write_blocknum() + #self.write(iso.PROGRAM_END + '\n') + self.write('PL_OFF \n') + self.write('PARK \n') + self.write('FS \n') + self.write('MA 0.0000 , 0.0000\n') + self.write("// koniec programu v jazyku REZ\n") + #self.write(iso.PROGRAM_END + '\n') + def flush_nc(self): + #self.write_blocknum() + self.write_preps() + self.write_misc() + #self.write('\n') + + ############################################################################ + ## Subprograms + + def sub_begin(self, id, name=''): + #self.write((iso.PROGRAM % id) + iso.SPACE + (iso.COMMENT % name)) + #self.write('\n') + pass + def sub_call(self, id): + #self.write_blocknum() + #self.write((iso.SUBPROG_CALL % id) + '\n') + #self.write('\n') + pass + + def sub_end(self): + #self.write_blocknum() + #self.write(iso.SUBPROG_END + '\n') + #self.write('\n') + pass + ############################################################################ + ## Settings + + def imperial(self): + #self.g += iso.IMPERIAL + #self.fmt = iso.FORMAT_IN + #self.write('\n') + pass + + def metric(self): + #self.g += iso.METRIC + #self.fmt = iso.FORMAT_MM + #self.write('\n') + pass + + def absolute(self): + #self.g += iso.ABSOLUTE + #self.write('\n') + pass + + def incremental(self): + #self.g += iso.INCREMENTAL + #self.write('\n') + pass + def polar(self, on=True): + #if (on) : self.g += iso.POLAR_ON + #else : self.g += iso.POLAR_OFF + #self.write('\n') + pass + def set_plane(self, plane): + #if (plane == 0) : self.g += iso.PLANE_XY + #elif (plane == 1) : self.g += iso.PLANE_XZ + #elif (plane == 2) : self.g += iso.PLANE_YZ + #self.write('\n') + pass + ############################################################################ + ## Tools + + def tool_change(self, id): + #self.write_blocknum() + #self.write((iso.TOOL % id) + '\n') + #self.write('\n') + pass + def tool_defn(self, id, name='', params=None): + pass + + def offset_radius(self, id, radius=None): + pass + + def offset_length(self, id, length=None): + pass + + ############################################################################ + ## Datums + + def datum_shift(self, x=None, y=None, z=None, a=None, b=None, c=None): + pass + + def datum_set(self, x=None, y=None, z=None, a=None, b=None, c=None): + pass + + # This is the coordinate system we're using. G54->G59, G59.1, G59.2, G59.3 + # These are selected by values from 1 to 9 inclusive. + def workplane(self, id): + #if ((id >= 1) and (id <= 6)): + # self.g += iso.WORKPLANE % (id + iso.WORKPLANE_BASE) + #if ((id >= 7) and (id <= 9)): + # self.g += ((iso.WORKPLANE % (6 + iso.WORKPLANE_BASE)) + ('.%i' % (id - 6))) + pass + ############################################################################ + ## Rates + Modes + + def feedrate(self, f): + #self.f = iso.FEEDRATE + (self.fmt % f) + #self.fhv = False + pass + + def feedrate_hv(self, fh, fv): + #self.fh = fh + #self.fv = fv + #self.fhv = True + pass + def calc_feedrate_hv(self, h, v): + #l = math.sqrt(h*h+v*v) + #if (h == 0) : self.f = iso.FEEDRATE + (self.fmt % self.fv) + #elif (v == 0) : self.f = iso.FEEDRATE + (self.fmt % self.fh) + #else: + # self.f = iso.FEEDRATE + (self.fmt % (self.fh * l * min([1/h, 1/v]))) + pass + + def spindle(self, s, clockwise): + #if s < 0: clockwise = not clockwise + #s = abs(s) + #self.s = iso.SPINDLE % s + #if clockwise: + # self.s = self.s + iso.SPINDLE_CW + #else: + # self.s = self.s + iso.SPINDLE_CCW + pass + + def coolant(self, mode=0): + #if (mode <= 0) : self.m.append(iso.COOLANT_OFF) + #elif (mode == 1) : self.m.append(iso.COOLANT_MIST) + #elif (mode == 2) : self.m.append(iso.COOLANT_FLOOD) + pass + + def gearrange(self, gear=0): + #if (gear <= 0) : self.m.append(iso.GEAR_OFF) + #elif (gear <= 4) : self.m.append(iso.GEAR % (gear + GEAR_BASE)) + pass + + ############################################################################ + ## Moves + + #def rapid(self, x=None, y=None, z=None, a=None, b=None, c=None): + def rapid(self, x=0.0000, y=0.0000, z=0.0000, a=0.0000, b=0.0000, c=0.0000,how=False): + #self.write_blocknum() + if (x == None): + if (y == None): + return + print 'rychlopsuv' + print x + print y + print z + print a + print b + print c + self.write('PL_OFF\n') + self.write('PARK\n') + self.write('FS\n') + self.write('MA ') + #self.write_preps() + #if (x != None): + # dx = x - self.x + # self.write(iso.X + (self.fmt % x)) + # self.x = x + #if (y != None): + # dy = y - self.y + # self.write(iso.Y + (self.fmt % y)) + # self.y = y + #if (z != None): + # dz = z - self.z + # self.write(iso.Z + (self.fmt % z)) + # self.z = z + #if (a != None) : self.write(iso.A + (iso.FORMAT_ANG % a)) + #if (b != None) : self.write(iso.B + (iso.FORMAT_ANG % b)) + #if (c != None) : self.write(iso.C + (iso.FORMAT_ANG % c)) + #self.write_spindle() + #self.write_misc() + self.write((' %.4f' %x)) + #self.write(('%f' %x) ) + self.write(' , ') + self.write((' %.4f' %y)) + self.write('\n') + self.write('SS\n') + self.write('AD_W\n') + self.write('PL_ON') + self.write('\n') + self.x=x + self.y=y + + def feed(self, x=0.0000, y=0.0000, z=0.0000,how=False): + if self.same_xyz(x, y, z): + return + if (x == None): + if (y == None): + return + + print 'MA rez' + print x + print y + print z + #self.write_blocknum() + #self.write(iso.FEED) + #self.write_preps() + #dx = dy = dz = 0 + #if (x != None): + # dx = x - self.x + # self.write(iso.X + (self.fmt % x)) + # self.x = x + #if (y != None): + # dy = y - self.y + # self.write(iso.Y + (self.fmt % y)) + # self.y = y + #if (z != None): + # dz = z - self.z + # self.write(iso.Z + (self.fmt % z)) + # self.z = z + #if (self.fhv) : self.calc_feedrate_hv(math.sqrt(dx*dx+dy*dy), math.fabs(dz)) + #self.write_feedrate() + #self.write_spindle() + #self.write_misc() + self.write('MA ') + self.write((' %.4f' %x)) + #self.write(('%f' %x) ) + self.write(' , ') + self.write((' %.4f' %y)) + self.write('\n') + self.x=x + self.y=y + + def same_xyz(self, x=None, y=None, z=None): + if (x != None): + if (self.fmt % x) != (self.fmt % self.x): + return False + if (y != None): + if (self.fmt % y) != (self.fmt % self.y): + return False + if (z != None): + if (self.fmt % z) != (self.fmt % self.z): + return False + + return True + + def arc(self, cw, x=0.0000, y=0.0000, z=0.0000, i=0.0000, j=0.0000, k=0.0000, r=0.0000): + if self.same_xyz(x, y, z): return + if (x == None): + if (y == None): + return + #self.write_blocknum() + print 'ARC rez' + print x + print y + print z + print i + print j + print k + print r + self.write('C ') + # if cw: self.write(iso.ARC_CW) + # else: self.write(iso.ARC_CCW) + # self.write_preps() + #if (x != None): + # self.write(iso.X + (self.fmt % x)) + # self.x = x + #if (y != None): + # self.write(iso.Y + (self.fmt % y)) + # self.y = y + #if (z != None): + # self.write(iso.Z + (self.fmt % z)) + # self.z = z + #if (i != None) : self.write(iso.CENTRE_X + (self.fmt % i)) + #if (j != None) : self.write(iso.CENTRE_Y + (self.fmt % j)) + #if (k != None) : self.write(iso.CENTRE_Z + (self.fmt % k)) + #if (r != None) : self.write(iso.RADIUS + (self.fmt % r)) +# use horizontal feed rate + #if (self.fhv) : self.calc_feedrate_hv(1, 0) + #self.write_feedrate() + #self.write_spindle() + #self.write_misc() + self.write((' %.4f' %(self.x + i))) + #self.write(('%f' %x) ) + self.write(' , ') + self.write((' %.4f' %(self.y + j))) + + self.write(' , ') + angle=0.0000 + + self.x1=-i + self.y1=-j + self.x2=x-(self.x+i) + self.y2=y-(self.y+j) + + dx=self.x1*self.x2 + dy=self.y1*self.y2 + ssucin=dx+dy + r=math.sqrt((i*i)+(j*j)) + + #dx=i*(x-(self.x + i)) + #dy=j*(y-(self.y + j)) + #ssucin=dx+dy + #r=math.sqrt((i*i)+(j*j)) + ratio=ssucin/(r*r) + angle= math.acos(ratio) * 180 / math.pi + print 'angle' + print angle +# while(angle>=90.0001): +# if(cw): self.write((' -90.0000\n' )) +# else: self.write((' 90.0000\n')) + #self.write(('90.0000\n' )) +# self.write('C ') +# self.write((' %.4f' %( self.x + i) )) +# self.write(' , ') +# self.write((' %.4f' %( self.y + j))) +# self.write(' , ') +# angle-=90 + if(cw): self.write((' %.4f' %(-angle))) + else: self.write((' %.4f' %(angle))) + + + #self.write((', %.4f' % )) + self.write('\n') + self.x=x + self.y=y + self.write('// stred') + self.write((' %f' %i)) + self.write(' ') + self.write( (' %f' %j)) + self.write('\n') + + self.write('// koniec') + self.write((' %f' %self.x)) + self.write(' ') + self.write( (' %f' %self.y)) + self.write('\n') + + def arc_cw(self, x=None, y=None, z=None, i=None, j=None, k=None, r=None): + self.arc(True, x, y, z, i, j, k, r) + + def arc_ccw(self, x=None, y=None, z=None, i=None, j=None, k=None, r=None): + self.arc(False, x, y, z, i, j, k, r) + + def dwell(self, t): + self.write_blocknum() + self.write_preps() + self.write(iso.DWELL + (iso.TIME % t)) + self.write_misc() + self.write('\n') + + def rapid_home(self, x=None, y=None, z=None, a=None, b=None, c=None): + pass + + def rapid_unhome(self): + pass + + ############################################################################ + ## Cycles + + def pattern(self): + pass + + def pocket(self): + pass + + def profile(self): + pass + + def circular_pocket(self, x=None, y=None, ToolDiameter=None, HoleDiameter=None, ClearanceHeight=None, StartHeight=None, MaterialTop=None, FeedRate=None, SpindleRPM=None, HoleDepth=None, DepthOfCut=None, StepOver=None ): + self.write_preps() + circular.pocket.block_number = self.n + (self.g_code, self.n) = circular.pocket.GeneratePath( x,y, ToolDiameter, HoleDiameter, ClearanceHeight, StartHeight, MaterialTop, FeedRate, SpindleRPM, HoleDepth, DepthOfCut, StepOver ) + self.write(self.g_code) + + # The drill routine supports drilling (G81), drilling with dwell (G82) and peck drilling (G83). + # The x,y,z values are INITIAL locations (above the hole to be made. This is in contrast to + # the Z value used in the G8[1-3] cycles where the Z value is that of the BOTTOM of the hole. + # Instead, this routine combines the Z value and the depth value to determine the bottom of + # the hole. + # + # The standoff value is the distance up from the 'z' value (normally just above the surface) where the bit retracts + # to in order to clear the swarf. This combines with 'z' to form the 'R' value in the G8[1-3] cycles. + # + # The peck_depth value is the incremental depth (Q value) that tells the peck drilling + # cycle how deep to go on each peck until the full depth is achieved. + # + # NOTE: This routine forces the mode to absolute mode so that the values passed into + # the G8[1-3] cycles make sense. I don't know how to find the mode to revert it so I won't + # revert it. I must set the mode so that I can be sure the values I'm passing in make + # sense to the end-machine. + # + def drill(self, x=None, y=None, dwell=None, depthparams = None, retract_mode=None, spindle_mode=None, internal_coolant_on=None, rapid_to_clearance=None): + if (standoff == None): + # This is a bad thing. All the drilling cycles need a retraction (and starting) height. + return + + if (z == None): return # We need a Z value as well. This input parameter represents the top of the hole + + self.write_blocknum() + self.write_preps() + + if (peck_depth != 0): + # We're pecking. Let's find a tree. + self.write(iso.PECK_DRILL + iso.SPACE + iso.PECK_DEPTH + (self.fmt % peck_depth )) + else: + # We're either just drilling or drilling with dwell. + if (dwell == 0): + # We're just drilling. + self.write(iso.DRILL + iso.SPACE) + else: + # We're drilling with dwell. + self.write(iso.DRILL_WITH_DWELL + (iso.FORMAT_DWELL % dwell) + iso.SPACE) + + # Set the retraction point to the 'standoff' distance above the starting z height. + retract_height = z + standoff + #self.write(iso.RETRACT + (self.fmt % retract_height)) + if (x != None): + dx = x - self.x + self.write(iso.X + (self.fmt % x) + iso.SPACE) + self.x = x + if (y != None): + dy = y - self.y + self.write(iso.Y + (self.fmt % y) + iso.SPACE) + self.y = y + + dz = (z + standoff) - self.z + # In the end, we will be standoff distance above the z value passed in. + self.write(iso.Z + (self.fmt % (z - depth)) + iso.SPACE) # This is the 'z' value for the bottom of the hole. + self.z = (z + standoff) # We want to remember where z is at the end (at the top of the hole) + self.write(iso.RETRACT + (self.fmt % retract_height)) + self.write_spindle() + self.write_misc() + self.write('\n') + + +# def tap(self, x=None, y=None, z=None, zretract=None, depth=None, standoff=None, dwell_bottom=None, pitch=None, stoppos=None, spin_in=None, spin_out=None): +# pass + + # argument list adapted for compatibility with Tapping module + def tap(self, x=None, y=None, z=None, zretract=None, depth=None, standoff=None, dwell_bottom=None, pitch=None, stoppos=None, spin_in=None, spin_out=None, tap_mode=None, direction=None): + pass + + def bore(self, x=None, y=None, z=None, zretract=None, depth=None, standoff=None, dwell_bottom=None, feed_in=None, feed_out=None, stoppos=None, shift_back=None, shift_right=None, backbore=False, stop=False): + pass + + ############################################################################ + ## Misc + + def comment(self, text): + self.write('// ' + (self.COMMENT( text)) + '\n') + + def variable(self, id): + return (iso.VARIABLE % id) + + def variable_set(self, id, value): + self.write_blocknum() + self.write((iso.VARIABLE % id) + (iso.VARIABLE_SET % value) + '\n') + +################################################################################ + +nc.creator = Creator() diff --git a/src/Mod/Path/PathScripts/nc/rez2_read.py b/src/Mod/Path/PathScripts/nc/rez2_read.py new file mode 100644 index 0000000000..b7150b7145 --- /dev/null +++ b/src/Mod/Path/PathScripts/nc/rez2_read.py @@ -0,0 +1,291 @@ +# -*- coding: utf-8 -*- +################################################################################ +# iso_.py +# + +import nc_read as nc +import re +import sys +import math + +################################################################################ +class Parser(nc.Parser): + + + def __init__(self, writer): + nc.Parser.__init__(self, writer) + + #self.pattern_main = re.compile('(\s+|\w(?:[+])?\d*(?:\.\d*)?|\w\#\d+|\(.*?\)|\#\d+\=(?:[+])?\d*(?:\.\d*)?)') + + #rada + self.pattern_main = re.compile(r'(\s+|,|-?\w\+?\d*(?:\.\d*)?|\w#\d+|\(.*?\)|#\d+=\+?\d*(?:\.\d*)?)') + #self.pattern_main = re.compile('\s+\w') + #self.pattern_main = re.compile('(\s+|\w(?:[+])?[+-\w]\d*(?:\.\d*)?|\w\#[+-\w]\d+|\(.*?\)|[\#[+-\w]\d+\=(?:[+])?[+-\w]\d*(?:\.\d*)?)') + #self.pattern_main = re.compile('\s\w[\S]\w,\w[+-\w]\d*\w,\w[+-\w]\d*\w,\w[+-\w]\d*') + #self.pattern_main = re.compile('(\s|\w(?:)?\d*(?:\.\d*)?|\w\#\d+|\(.*?\)|\#\d+\=(?:)?\d*(?:\.\d*)?)') + #self.pattern_main = re.compile(' ') + + self.a = 0 + self.b = 0 + self.c = 0 + self.f = 0 + self.i = 0 + self.j = 0 + self.k = 0 + self.p = 0 + self.q = 0 + self.r = 0 + self.s = 0 + self.x = 0 + self.y = 0 + self.z = 500 + self.FS = 1 + self.endx=0 + self.endy=0 + self.startx=0 + self.starty=0 + self.x1=0 + self.y1=0 + self.dy=0 + self.dx=0 + self.angle=0 + self.SPACE = ' ' + + def add_text(self, s, col=None): + s.replace('&', '&') + s.replace('"', '"') + s.replace('<', '<') + s.replace('>', '>') + s+=self.SPACE+'\n' + if (col != None) : self.file_out.write('\t\t'+s+' \n') + else : self.file_out.write('\t\t'+s+' \n') + #def add_text(self, s, col=None): + # if (col != None) : self.file_out.write('\t\t'+s+'\n') + # else : self.file_out.write('\t\t'+s+'\n') + + def Parse(self, name, oname=None): + self.files_open(name,oname) + + while (self.readline()): + self.begin_ncblock() + + move = False; + arc = 0; + path_col = None + col=None + if(self.line[0]=='C'): col = "axis" + if(self.line[0]=='M' and self.line[1]=='A'): col = "feed" + if (self.line[0] == "/" and self.line[1] == "/") : col = "comment" + + if (self.FS==1 and not (self.line[0]=='S' and self.line[1]=='S') and col=="feed" ): col="rapid" + self.add_text(self.line, col) + #words = self.pattern_main.findall(self.line) + words=self.line.split() + #print self.line + #print ' AAAA ' + words[0]=words[0]+self.SPACE + print words + for word in words: + col = None + #if (word[0] == 'A' or word[0] == 'a'): + # col = "axis" + # self.a = eval(word[1:]) + # move = True + #elif (word[0] == 'B' or word[0] == 'b'): + # col = "axis" + # self.b = eval(word[1:]) + + # move = True + if (word == ('C'+self.SPACE)): + #print words + col = "axis" + self.startx=self.x + self.starty=self.y + words[0]=words[0]+self.SPACE + words[2]=self.SPACE+words[2]+self.SPACE + words[4]=self.SPACE+words[4]+self.SPACE + + #print 'x,y' + #print self.x + #print self.y + #self.x1=self.x-eval(words[1]) + self.x1=self.x-eval(words[1]) + #j=self.y-eval(words[5]) + #self.y1=self.y-eval(words[3]) + self.y1=self.y-eval(words[3]) + #self.c = eval(word[1:]) + #print 'self x,y' + #print self.x1 + #print self.y1 + self.dx=(self.x1)*1 + self.dy=(self.y1)*0 + #print 'x1' + #print self.x1 + #print 'y1' + #print self.y1 + ssucin=self.dx+self.dy + r=math.sqrt(((self.x1)*(self.x1))+((self.y1)*(self.y1))) + #print 'skalarny sucin' + #print ssucin + #print 'r' + #print r + if (ssucin!=0): + ratio=ssucin/(r*1) + #print 'ratio' + #print ratio + angle= (math.acos(ratio) * 180 / math.pi) + if(self.y1<0):angle=360-angle + elif (self.y1>0): angle=+90 + elif (self.y1<0): angle=-90 + else: angle=0 + #print words[8] + #print 'angles' + #print angle + #if (i<0 and j<0): angle=180+angle + #if (i<0 and j>0): angle=180-angle + #if (j>0): angle=-angle + #print ('reverzacia') + #angle= angle+ eval(words[8]) + #print angle + self.angle=+ angle+ eval(words[5]) + #print self.angle + #if(angle>180): angle=360-angle + angle=self.angle*math.pi/180 + #print eval(words[8]) + self.endx=eval(words[1])+(r*math.cos(angle)) + #j=eval(words[5])+(r*math.sin(angle)) + self.endy=eval(words[3])+(r*math.sin(angle)) + self.x=self.endx + self.y=self.endy + path_col = "feed" + #arc=-eval(words[8])/math.fabs(eval(words[8])) + arc=eval(words[5])/math.fabs(eval(words[5])) + #if(arc==-1): arc=0 + #arc=-1 + #col = "feed" + move = True + + + elif (word == 'P' and words[1]=='L' and words[4]=='F'): + self.FS=1 + elif (word == 'P' and words[1]=='L' and words[4]=='N'): + self.FS=0 + elif (word == ('FS'+self.SPACE)): + self.FS=1 + elif (word == ('SS'+self.SPACE)): + self.FS=0 + elif (word == ('MA'+self.SPACE)): + words[2]=self.SPACE+words[2]+self.SPACE + if (self.FS==1): + path_col = "rapid" + col = "rapid" + else: + path_col = "feed" + col = "feed" + self.x=eval(words[1]) + #self.y=eval(words[6]) + self.y=eval(words[3]) + move=True + #elif (word == 'G1' or word == 'G01' or word == 'g1' or word == 'g01'): + # path_col = "feed" + # col = "feed" + #elif (word == 'G2' or word == 'G02' or word == 'g2' or word == 'g02' or word == 'G12' or word == 'g12'): + # path_col = "feed" + # col = "feed" + # arc = -1 + #elif (word == 'G3' or word == 'G03' or word == 'g3' or word == 'g03' or word == 'G13' or word == 'g13'): + # path_col = "feed" + # col = "feed" + # arc = +1 + #elif (word == 'G10' or word == 'g10'): + # move = False + #elif (word == 'L1' or word == 'l1'): + # move = False + #elif (word == 'G20'): + # col = "prep" + # self.set_mode(units=25.4) + #elif (word == 'G21'): + # col = "prep" + # self.set_mode(units=1.0) + #elif (word == 'G81' or word == 'g81'): + # path_col = "feed" + # col = "feed" + #elif (word == 'G82' or word == 'g82'): + # path_col = "feed" + # col = "feed" + #elif (word == 'G83' or word == 'g83'): + # path_col = "feed" + # col = "feed" + #elif (word[0] == 'G') : col = "prep" + #elif (word[0] == 'I' or word[0] == 'i'): + # col = "axis" + # self.i = eval(word[1:]) + # move = True + #elif (word[0] == 'J' or word[0] == 'j'): + # col = "axis" + # self.j = eval(word[1:]) + # move = True + #elif (word[0] == 'K' or word[0] == 'k'): + # col = "axis" + # self.k = eval(word[1:]) + # move = True + #elif (word[0] == 'M') : col = "misc" + #elif (word[0] == 'N') : col = "blocknum" + #elif (word[0] == 'O') : col = "program" + #elif (word[0] == 'P' or word[0] == 'p'): + # col = "axis" + # self.p = eval(word[1:]) + # move = True + #elif (word[0] == 'Q' or word[0] == 'q'): + # col = "axis" + # self.q = eval(word[1:]) + # move = True + #elif (word[0] == 'R' or word[0] == 'r'): + # col = "axis" + # self.r = eval(word[1:]) + # move = True + #elif (word[0] == 'S' or word[0zypp] == 's'): + # col = "axis" + # self.s = eval(word[1:]) + # move = True + #elif (word[0] == 'T') : col = "tool" + #elif (word[0] == 'X' or word[0] == 'x'): + # col = "axis" + # = eval(word[1:]) + # move = True + #elif (word[0] == 'Y' or word[0] == 'y'): + # col = "axis" + # self.y = eval(word[1:]) + # move = True + #elif (word[0] == 'Z' or word[0] == 'z'): + # col = "axis" + # self.z = eval(word[1:]) + # move = True + elif (word[0] == '(') : col = "comment" + elif (word[0] == '#') : col = "variable" + elif (words[0] == ("//"+self.SPACE)) : col = "comment" + elif (words[0] == ("/*"+self.SPACE)) : col = "comment" + #self.add_text(word, col) + + if (move): + self.begin_path(path_col) + if (arc) : + #self.add_arc(self.x, self.y, 0.0000, self.i, self.j, 0.0000, arc) + #self.add_arc(self.i, self.j, 0.0000, eval(words[2])-self.x, eval(words[5])-self.y, 0.0000, arc) + #print '' + #print eval(words[2])-self.startx + #print eval(words[6])-self.starty + #self.add_arc(self.endx, self.endy, 0.0000, eval(words[1])-self.startx, eval(words[3])-self.starty, 0.0000, arc) + print arc + self.add_arc(self.endx, self.endy, 0.0000,eval(words[1])-self.startx, eval(words[3])-self.starty, 0.0000,0.0000, arc) + + #self.add_arc(self.x, self.y, 0.0000, self.i, self.j, 0.0000, arc) + #self.x=self.i + #self.y=self.j + else : + self.add_line(self.x, self.y) + self.end_path() + + self.end_ncblock() + + self.files_close() diff --git a/src/Mod/Path/PathScripts/nc/series1.py b/src/Mod/Path/PathScripts/nc/series1.py new file mode 100644 index 0000000000..85dae14086 --- /dev/null +++ b/src/Mod/Path/PathScripts/nc/series1.py @@ -0,0 +1,31 @@ +import nc +import iso_modal +import math + +################################################################################ +class Creator(iso_modal.Creator): + + def __init__(self): + iso_modal.Creator.__init__(self) + self.arc_centre_positive = True + self.drillExpanded = True + self.can_do_helical_arcs = False + self.fmt.number_of_decimal_places = 2 + + def tool_defn(self, id, name='', params=None): + pass + + def dwell(self, t): + # to do, find out what dwell is on this machine + pass + + def metric(self): + iso_modal.Creator.metric(self) + self.fmt.number_of_decimal_places = 2 + + def SPACE(self): + return('') + +################################################################################ + +nc.creator = Creator() diff --git a/src/Mod/Path/PathScripts/nc/series1_read.py b/src/Mod/Path/PathScripts/nc/series1_read.py new file mode 100644 index 0000000000..7e422fc384 --- /dev/null +++ b/src/Mod/Path/PathScripts/nc/series1_read.py @@ -0,0 +1,9 @@ +import iso_read as iso +import sys + +# use the iso reader, but with i_and_j_always_positive + +class Parser(iso.Parser): + def __init__(self, writer): + iso.Parser.__init__(self, writer) + self.arc_centre_positive = True diff --git a/src/Mod/Path/PathScripts/nc/siegkx1.py b/src/Mod/Path/PathScripts/nc/siegkx1.py new file mode 100644 index 0000000000..8743cde254 --- /dev/null +++ b/src/Mod/Path/PathScripts/nc/siegkx1.py @@ -0,0 +1,22 @@ +################################################################################ +# siegkx1.py +# +# Post Processor for the Sieg KX1 machine +# It is just an ISO machine, but I don't want the tool definition lines +# +# Dan Heeks, 5th March 2009 + +import nc +import iso_modal +import math + +################################################################################ +class Creator(iso_modal.Creator): + + def __init__(self): + iso_modal.Creator.__init__(self) + self.output_tool_definitions = False + +################################################################################ + +nc.creator = Creator() diff --git a/src/Mod/Path/PathScripts/nc/swap.py b/src/Mod/Path/PathScripts/nc/swap.py new file mode 100644 index 0000000000..a38343fd62 --- /dev/null +++ b/src/Mod/Path/PathScripts/nc/swap.py @@ -0,0 +1,54 @@ +################################################################################ +# attach.py +# +# NC code creator for attaching Z coordinates to a surface +# + +import recreator +import nc + +swap = False + +PUT_Y_VALUE_IN_A = 1 + +################################################################################ +class Creator(recreator.Redirector): + + def __init__(self, original, type, factor): + recreator.Redirector.__init__(self, original) + self.factor = factor + self.type = type + + def feed(self, x=None, y=None, z=None, a=None, b=None, c=None): + if self.type == PUT_Y_VALUE_IN_A: + a = None + if y != None: a = y * self.factor + self.original.feed(x, None, z, a, b, c) + + def rapid(self, x=None, y=None, z=None, a=None, b=None, c=None): + if self.type == PUT_Y_VALUE_IN_A: + a = None + if y != None: a = y * self.factor + self.original.rapid(x, None, z, a, b, c) + + def arc(self, x=None, y=None, z=None, i=None, j=None, k=None, r=None, ccw = True): + # to do + pass + +################################################################################ + +def use_a_for_y(radius): + cancel_swap() + if radius < 0.001: + return + global swap + radians_factor = 1 / radius + factor = radians_factor * 180 / 3.1415926535897932384 + nc.creator = Creator(nc.creator, PUT_Y_VALUE_IN_A, factor) + swap = True + +def cancel_swap(): + global swap + if swap: + nc.creator = nc.creator.original + swap = False diff --git a/src/Mod/Path/PathScripts/nc/tnc151.py b/src/Mod/Path/PathScripts/nc/tnc151.py new file mode 100644 index 0000000000..d88406edbb --- /dev/null +++ b/src/Mod/Path/PathScripts/nc/tnc151.py @@ -0,0 +1,90 @@ +################################################################################ +# tnc151.py +# +# Post Processor for the Heidenhain TNC151 machine +# + +import nc +import iso_modal +import math + +################################################################################ +class Creator(iso_modal.Creator): + + def __init__(self): + iso_modal.Creator.__init__(self) + self.fmt.add_plus = True + self.fmt.add_trailing_zeros = True + self.f.fmt.add_plus = True + self.s.fmt.add_plus = True + self.n = 1 + self.waiting_t = None + self.waiting_for_program_begin = False + + ######## Codes + + def SPACE(self): return(' ') + def TOOL(self): return('T%i') + + ######## Overridden functions + + def write_blocknum(self): + self.write(self.BLOCK() % self.n) + self.n += 1 + + def program_begin(self, id, name=''): + self.waiting_for_program_begin = True + + def write_waiting_program_begin(self): + if self.waiting_for_program_begin == True: + self.write('% 123') + self.waiting_for_program_begin = False + + def imperial(self): + self.write_waiting_program_begin() + self.write(' G70\n') + self.fmt.number_of_decimal_places = 4 + + def metric(self): + self.write_waiting_program_begin() + self.write(' G71\n') + self.fmt.number_of_decimal_places = 3 + + + # no tool definition lines wanted + def tool_defn(self, id, name='', params=None): + pass + + # no comments wanted + def comment(self, text): + pass + + def spindle(self, s, clockwise): + iso_modal.Creator.spindle(self, s, clockwise) + self.write_waiting_tool_change() + + def tool_change(self, id): + self.waiting_t = id + + def write_waiting_tool_change(self): + if self.waiting_t: + if len(self.g_list) > 0: + self.write_blocknum() + for g in self.g_list: + self.write(self.SPACE() + g) + self.g_list = [] + self.write('\n') + self.write_blocknum() + self.write(self.SPACE() + (self.TOOL() % self.waiting_t)) + self.write_preps() + self.write_spindle() + self.write_misc() + self.write('\n') + self.t = self.waiting_t + self.waiting_t = None + + def workplane(self, id): + pass +################################################################################ + +nc.creator = Creator() diff --git a/src/Mod/Path/PathScripts/nc/transform.py b/src/Mod/Path/PathScripts/nc/transform.py new file mode 100644 index 0000000000..fcffc2a07a --- /dev/null +++ b/src/Mod/Path/PathScripts/nc/transform.py @@ -0,0 +1,270 @@ +################################################################################ +# transform.py +# +# NC code creator for attaching Z coordinates to a surface +# + +import nc +import area +import recreator + +transformed = False + +class FeedXY: + def __init__(self, x, y): + self.x = x + self.y = y + + def Do(self, original, matrix): + x,y,z = matrix.TransformedPoint(self.x, self.y, 0) + original.feed(x, y) + +class FeedZ: + def __init__(self, z): + self.z = z + + def Do(self, original, matrix): + original.feed(z = self.z) + +class FeedXYZ: + def __init__(self, x, y, z): + self.x = x + self.y = y + self.z = z + + def Do(self, original, matrix): + x,y,z = matrix.TransformedPoint(self.x, self.y, self.z) + original.feed(x,y,z) + +class RapidXY: + def __init__(self, x, y): + self.x = x + self.y = y + + def Do(self, original, matrix): + x,y,z = matrix.TransformedPoint(self.x, self.y, 0) + original.rapid(x, y) + +class RapidZ: + def __init__(self, z): + self.z = z + + def Do(self, original, matrix): + original.rapid(z = self.z) + +class RapidXYZ: + def __init__(self, x, y, z): + self.x = x + self.y = y + self.z = z + + def Do(self, original, matrix): + x,y,z = matrix.TransformedPoint(self.x, self.y, self.z) + original.rapid(x,y,z) + +class Feedrate: + def __init__(self, h, v): + self.h = h + self.v = v + + def Do(self, original, matrix): + original.feedrate_hv(self.h, self.v) + +class Arc: + def __init__(self, x, y, z, i, j, ccw): + self.x = x + self.y = y + self.z = z + self.i = i + self.j = j + self.ccw = ccw + + def Do(self, original, matrix): + x,y,z = matrix.TransformedPoint(self.x, self.y, self.z) + cx,cy,cz = matrix.TransformedPoint(original.x + self.i, original.y + self.j, self.z) + i = cx - original.x + j = cy - original.y + if self.ccw: + original.arc_ccw(x, y, z, i, j) + else: + original.arc_cw(x, y, z, i, j) + +class Drill: + def __init__(self, x, y, dwell, depthparams, retract_mode, spindle_mode, internal_coolant_on, rapid_to_clearance): + self.x = x + self.y = y + self.dwell = dwell + self.depthparams = depthparams + self.retract_mode = retract_mode + self.spindle_mode = spindle_mode + self.internal_coolant_on = internal_coolant_on + self.rapid_to_clearance = rapid_to_clearance + + def Do(self, original, matrix): + x,y,z = matrix.TransformedPoint(self.x, self.y, 0.0) + original.drill(x, y, self.dwell, self.depthparams, self.retract_mode, self.spindle_mode, self.internal_coolant_on, self.rapid_to_clearance) + +class Absolute: + def __init__(self): + pass + + def Do(self, original, matrix): + original.absolute() + +class Incremental: + def __init__(self): + pass + + def Do(self, original, matrix): + original.incremental() + +class EndCannedCycle: + def __init__(self): + pass + + def Do(self, original, matrix): + original.end_canned_cycle() + +class Comment: + def __init__(self, text): + self.text = text + + def Do(self, original, matrix): + original.comment(self.text) + +################################################################################ +matrix_fixtures = {} + +class Creator(recreator.Redirector): + def __init__(self, original, matrix_list): + recreator.Redirector.__init__(self, original) + self.matrix_list = matrix_list + self.commands = [] + + # allocate fixtures to pattern positions + if self.pattern_uses_subroutine() == True: + save_fixture = self.get_fixture() + for matrix in self.matrix_list: + global matrix_fixtures + if (matrix in matrix_fixtures) == False: + matrix_fixtures[matrix] = self.get_fixture() + self.increment_fixture() + self.set_fixture(save_fixture) + + def DoAllCommands(self): + subroutine_written = False + + for matrix in self.matrix_list: + if len(self.commands) > 0: + if self.pattern_uses_subroutine() == True: + # set fixture + global matrix_fixtures + self.set_fixture(matrix_fixtures[matrix]) + # rapid to the pattern point in x and y + x,y,z = matrix.TransformedPoint(0.0, 0.0, 0.0) + self.original.rapid(x, y) + + subroutine_started = False + output_disabled = False + + for command in self.commands: + if (output_disabled == False) and (self.pattern_uses_subroutine() == True): + # in main program do commands up to the first z move + cname = command.__class__.__name__ + if cname == 'FeedZ' or cname == 'Drill': + if subroutine_written: + self.sub_call(None) + self.disable_output() # ignore all the other commands + output_disabled = True + else: + if subroutine_started == False: + self.sub_begin(None) + subroutine_started = True + self.incremental() + command.Do(self.original, matrix) + + self.enable_output() + output_disabled = False + + if subroutine_started: + self.absolute() + self.flush_nc() + self.sub_end() + subroutine_written = True + self.sub_call(None) + + def tool_change(self, id): + if id != self.original.current_tool(): + self.DoAllCommands() + self.commands = [] + self.original.tool_change(id) + + def feedrate(self, f): + self.commands.append(Feedrate(f, f)) + + def feedrate_hv(self, fh, fv): + self.commands.append(Feedrate(fh, fv)) + + def rapid(self, x=None, y=None, z=None, a=None, b=None, c=None): + if x != None: self.x = x + if y != None: self.y = y + if z != None: self.z = z + if self.x == None and self.y == None and self.z == None: + return + if z == None: + self.commands.append(RapidXY(self.x, self.y)) + elif x == None and y == None: + self.commands.append(RapidZ(self.z)) + else: + self.commands.append(RapidXYZ(self.x, self.y, self.z)) + + def feed(self, x=None, y=None, z=None, a = None, b = None, c = None): + if x != None: self.x = x + if y != None: self.y = y + if z != None: self.z = z + if self.x == None and self.y == None and self.z == None: + return + if z == None: + self.commands.append(FeedXY(self.x, self.y)) + elif x == None and y == None: + self.commands.append(FeedZ(self.z)) + else: + self.commands.append(FeedXYZ(self.x, self.y, self.z)) + + def arc(self, x=None, y=None, z=None, i=None, j=None, k=None, r=None, ccw = True): + if x != None: self.x = x + if y != None: self.y = y + if z != None: self.z = z + if self.x == None and self.y == None and self.z == None: + return + self.commands.append(Arc(self.x, self.y, self.z, i, j, ccw)) + + def drill(self, x=None, y=None, dwell=None, depthparams = None, retract_mode=None, spindle_mode=None, internal_coolant_on=None, rapid_to_clearance=None): + self.commands.append(Drill(x, y, dwell, depthparams, retract_mode, spindle_mode, internal_coolant_on, rapid_to_clearance)) + + def end_canned_cycle(self): + self.commands.append(EndCannedCycle()) + +# def absolute(self): +# self.commands.append(Absolute()) + +# def incremental(self): +# self.commands.append(Incremental()) + + def comment(self, text): + self.commands.append(Comment(text)) + +################################################################################ + +def transform_begin(matrix_list): + global transformed + if transformed == True: + transform_end() + nc.creator = Creator(nc.creator, matrix_list) + transformed = True + +def transform_end(): + global transformed + nc.creator.DoAllCommands() + nc.creator = nc.creator.original + transformed = False