def buildpathlibarea(self, obj, a): """Build the face path using libarea algorithm""" import PathScripts.PathAreaUtils as PathAreaUtils from PathScripts.PathUtils import depth_params FreeCAD.Console.PrintMessage( translate("PathFace", "Generating toolpath with libarea offsets.\n")) depthparams = depth_params(obj.ClearanceHeight.Value, obj.SafeHeight.Value, obj.StartDepth.Value, obj.StepDown, obj.FinishDepth.Value, obj.FinalDepth.Value) extraoffset = 1 - obj.PassExtension.Value stepover = (self.radius * 2) * (float(obj.StepOver) / 100) use_zig_zag = obj.UseZigZag zig_angle = obj.ZigZagAngle from_center = (obj.StartAt == "Center") keep_tool_down = obj.KeepToolDown zig_unidirectional = obj.ZigUnidirectional start_point = None cut_mode = obj.CutMode PathAreaUtils.flush_nc() PathAreaUtils.output('mem') PathAreaUtils.feedrate_hv(self.horizFeed, self.vertFeed) if obj.UseStartPoint: start_point = (obj.StartPoint.x, obj.StartPoint.y) PathAreaUtils.pocket(a, self.radius, extraoffset, stepover, depthparams, from_center, keep_tool_down, use_zig_zag, zig_angle, zig_unidirectional, start_point, cut_mode) return PathAreaUtils.retrieve_gcode()
def buildpathlibarea(self, obj, a): """Build the pocket path using libarea algorithm""" import PathScripts.PathAreaUtils as PathAreaUtils from PathScripts.PathUtils import depth_params PathLog.debug("Generating toolpath with libarea offsets.\n") depthparams = depth_params(obj.ClearanceHeight.Value, obj.SafeHeight.Value, obj.StartDepth.Value, obj.StepDown, obj.FinishDepth.Value, obj.FinalDepth.Value) extraoffset = obj.MaterialAllowance.Value stepover = (self.radius * 2) * (float(obj.StepOver) / 100) use_zig_zag = obj.UseZigZag zig_angle = obj.ZigZagAngle from_center = (obj.StartAt == "Center") keep_tool_down = obj.KeepToolDown zig_unidirectional = obj.ZigUnidirectional start_point = None cut_mode = obj.CutMode PathAreaUtils.flush_nc() PathAreaUtils.output('mem') PathAreaUtils.feedrate_hv(self.horizFeed, self.vertFeed) if obj.UseStartPoint: start_point = (obj.StartPoint.x, obj.StartPoint.y) # print "a," + str(self.radius) + "," + str(extraoffset) + "," + str(stepover) + ",depthparams, " + str(from_center) + "," + str(keep_tool_down) + "," + str(use_zig_zag) + "," + str(zig_angle) + "," + str(zig_unidirectional) + "," + str(start_point) + "," + str(cut_mode) PathAreaUtils.pocket(a, self.radius, extraoffset, stepover, depthparams, from_center, keep_tool_down, use_zig_zag, zig_angle, zig_unidirectional, start_point, cut_mode) return PathAreaUtils.retrieve_gcode()
def _buildPathLibarea(self, obj, edgelist): import PathScripts.PathKurveUtils as PathKurveUtils import math import area output = "" if obj.Comment != "": output += '(' + str(obj.Comment)+')\n' if obj.StartPoint and obj.UseStartPoint: startpoint = obj.StartPoint else: startpoint = None if obj.EndPoint and obj.UseEndPoint: endpoint = obj.EndPoint else: endpoint = None PathKurveUtils.output('mem') PathKurveUtils.feedrate_hv(self.horizFeed, self.vertFeed) output = "" output += "G0 Z" + str(obj.ClearanceHeight.Value) + "F " + PathUtils.fmt(self.vertRapid) + "\n" curve = PathKurveUtils.makeAreaCurve(edgelist, obj.Direction, startpoint, endpoint) 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 if obj.UseComp is False: obj.Side = 'On' else: if obj.Direction == 'CW': obj.Side = 'Left' else: obj.Side = 'Right' 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) depthparams = depth_params( obj.ClearanceHeight.Value, obj.SafeHeight.Value, obj.StartDepth.Value, obj.StepDown, 0.0, obj.FinalDepth.Value, None) PathKurveUtils.profile2( curve, obj.Side, self.radius, self.vertFeed, self.horizFeed, self.vertRapid, self.horizRapid, obj.OffsetExtra.Value, roll_radius, None, None, depthparams, extend_at_start, extend_at_end, lead_in_line_len, lead_out_line_len) output += PathKurveUtils.retrieve_gcode() return output
def _buildPathLibarea(self, obj, edgelist): import PathScripts.PathKurveUtils as PathKurveUtils # import math # import area output = "" if obj.Comment != "": output += '(' + str(obj.Comment) + ')\n' if obj.StartPoint and obj.UseStartPoint: startpoint = obj.StartPoint else: startpoint = None if obj.EndPoint and obj.UseEndPoint: endpoint = obj.EndPoint else: endpoint = None PathKurveUtils.output('mem') PathKurveUtils.feedrate_hv(self.horizFeed, self.vertFeed) output = "" output += "G0 Z" + str( obj.ClearanceHeight.Value) + "F " + PathUtils.fmt( self.vertRapid) + "\n" curve = PathKurveUtils.makeAreaCurve(edgelist, obj.Direction, startpoint, endpoint) '''The following line uses a profile function written for use with FreeCAD. It's clean but incomplete. It doesn't handle print("x = " + str(point.x)) print("y - " + str(point.y)) 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 h toolLoad = obj.activeTCeekscnc 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 depthparams = depth_params(obj.ClearanceHeight.Value, obj.SafeHeight.Value, obj.StartDepth.Value, obj.StepDown.Value, 0.0, obj.FinalDepth.Value, None) PathKurveUtils.profile2(curve, obj.Side, self.radius, self.vertFeed, self.horizFeed, self.vertRapid, self.horizRapid, obj.OffsetExtra.Value, roll_radius, None, None, depthparams, extend_at_start, extend_at_end, lead_in_line_len, lead_out_line_len) output += PathKurveUtils.retrieve_gcode() return output
def _buildPathLibarea(self, obj, edgelist): import PathScripts.PathKurveUtils as PathKurveUtils # import math # import area output = "" if obj.Comment != "": output += '(' + str(obj.Comment)+')\n' if obj.StartPoint and obj.UseStartPoint: startpoint = obj.StartPoint else: startpoint = None if obj.EndPoint and obj.UseEndPoint: endpoint = obj.EndPoint else: endpoint = None PathKurveUtils.output('mem') PathKurveUtils.feedrate_hv(self.horizFeed, self.vertFeed) output = "" output += "G0 Z" + str(obj.ClearanceHeight.Value) + "F " + PathUtils.fmt(self.vertRapid) + "\n" curve = PathKurveUtils.makeAreaCurve(edgelist, obj.Direction, startpoint, endpoint) '''The following line uses a profile function written for use with FreeCAD. It's clean but incomplete. It doesn't handle print("x = " + str(point.x)) print("y - " + str(point.y)) 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 h toolLoad = obj.activeTCeekscnc 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 depthparams = depth_params( obj.ClearanceHeight.Value, obj.SafeHeight.Value, obj.StartDepth.Value, obj.StepDown.Value, 0.0, obj.FinalDepth.Value, None) PathKurveUtils.profile2( curve, obj.Side, self.radius, self.vertFeed, self.horizFeed, self.vertRapid, self.horizRapid, obj.OffsetExtra.Value, roll_radius, None, None, depthparams, extend_at_start, extend_at_end, lead_in_line_len, lead_out_line_len) output += PathKurveUtils.retrieve_gcode() return output
def _buildPathArea(self, obj, baseobject, start=None): PathLog.track() profile = Path.Area() profile.setPlane(makeWorkplane(baseobject)) profile.add(baseobject) profileparams = {'Fill': 0, 'Coplanar': 0} if obj.UseComp is False: profileparams['Offset'] = 0.0 else: profileparams['Offset'] = self.radius + obj.OffsetExtra.Value depthparams = depth_params(clearance_height=obj.ClearanceHeight.Value, rapid_safety_space=obj.SafeHeight.Value, start_depth=obj.StartDepth.Value, step_down=obj.StepDown.Value, z_finish_step=0.0, final_depth=obj.FinalDepth.Value, user_depths=None) PathLog.debug('depths: {}'.format(depthparams.get_depths())) profile.setParams(**profileparams) PathLog.debug("Contour with params: {}".format(profile.getParams())) sections = profile.makeSections(mode=0, project=True, heights=depthparams.get_depths()) shapelist = [sec.getShape() for sec in sections] params = { 'shapes': shapelist, 'feedrate': self.horizFeed, 'feedrate_v': self.vertFeed, 'verbose': True, 'resume_height': obj.StepDown.Value, 'retraction': obj.ClearanceHeight.Value, 'return_end': True } if obj.Direction == 'CCW': params['orientation'] = 1 else: params['orientation'] = 0 if self.endVector is not None: params['start'] = self.endVector elif start is not None: params['start'] = start (pp, end_vector) = Path.fromShapes(**params) PathLog.debug("Generating Path with params: {}".format(params)) PathLog.debug('pp: {}, end vector: {}'.format(pp, end_vector)) self.endVector = end_vector return pp
def _buildPathLibarea(self, obj, edgelist): import PathScripts.PathKurveUtils as PathKurveUtils # import math # import area output = "" if obj.Comment != "": output += '(' + str(obj.Comment) + ')\n' if obj.StartPoint and obj.UseStartPoint: startpoint = obj.StartPoint else: startpoint = None if obj.EndPoint and obj.UseEndPoint: endpoint = obj.EndPoint else: endpoint = None PathKurveUtils.output('mem') PathKurveUtils.feedrate_hv(self.horizFeed, self.vertFeed) output = "" output += "G0 Z" + str( obj.ClearanceHeight.Value) + "F " + PathUtils.fmt( self.vertRapid) + "\n" curve = PathKurveUtils.makeAreaCurve(edgelist, obj.Direction, startpoint, endpoint) 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 if obj.UseComp is False: obj.Side = 'On' else: if obj.Direction == 'CW': obj.Side = 'Left' else: obj.Side = 'Right' depthparams = depth_params(obj.ClearanceHeight.Value, obj.SafeHeight.Value, obj.StartDepth.Value, obj.StepDown.Value, 0.0, obj.FinalDepth.Value, None) PathKurveUtils.profile2(curve, obj.Side, self.radius, self.vertFeed, self.horizFeed, self.vertRapid, self.horizRapid, obj.OffsetExtra.Value, roll_radius, None, None, depthparams, extend_at_start, extend_at_end, lead_in_line_len, lead_out_line_len) output += PathKurveUtils.retrieve_gcode() return output
def buildpathlibarea(self, obj, a): """Build the face path using libarea algorithm""" import PathScripts.PathAreaUtils as PathAreaUtils from PathScripts.PathUtils import depth_params PathLog.track() for p in a.getCurves(): PathLog.debug(p.text()) FreeCAD.Console.PrintMessage(translate("PathMillFace", "Generating toolpath with libarea offsets.\n")) depthparams = depth_params( obj.ClearanceHeight.Value, obj.SafeHeight.Value, obj.StartDepth.Value, obj.StepDown, obj.FinishDepth.Value, obj.FinalDepth.Value) extraoffset = - obj.PassExtension.Value stepover = (self.radius * 2) * (float(obj.StepOver)/100) use_zig_zag = obj.UseZigZag zig_angle = obj.ZigZagAngle from_center = (obj.StartAt == "Center") keep_tool_down = obj.KeepToolDown zig_unidirectional = obj.ZigUnidirectional start_point = None cut_mode = obj.CutMode PathAreaUtils.flush_nc() PathAreaUtils.output('mem') PathAreaUtils.feedrate_hv(self.horizFeed, self.vertFeed) if obj.UseStartPoint: start_point = (obj.StartPoint.x, obj.StartPoint.y) PathAreaUtils.pocket( a, self.radius, extraoffset, stepover, depthparams, from_center, keep_tool_down, use_zig_zag, zig_angle, zig_unidirectional, start_point, cut_mode) return PathAreaUtils.retrieve_gcode()
def buildpathlibarea(self, obj, a): """Build the pocket path using libarea algorithm""" import PathScripts.PathAreaUtils as PathAreaUtils from PathScripts.PathUtils import depth_params FreeCAD.Console.PrintMessage(translate("PathPocket", "Generating toolpath with libarea offsets.\n")) depthparams = depth_params( obj.ClearanceHeight.Value, obj.SafeHeight.Value, obj.StartDepth.Value, obj.StepDown, obj.FinishDepth.Value, obj.FinalDepth.Value) extraoffset = obj.MaterialAllowance.Value stepover = (self.radius * 2) * (float(obj.StepOver)/100) use_zig_zag = obj.UseZigZag zig_angle = obj.ZigZagAngle from_center = (obj.StartAt == "Center") keep_tool_down = obj.KeepToolDown zig_unidirectional = obj.ZigUnidirectional start_point = None cut_mode = obj.CutMode PathAreaUtils.flush_nc() PathAreaUtils.output('mem') PathAreaUtils.feedrate_hv(self.horizFeed, self.vertFeed) if obj.UseStartPoint: start_point = (obj.StartPoint.x, obj.StartPoint.y) # print "a," + str(self.radius) + "," + str(extraoffset) + "," + str(stepover) + ",depthparams, " + str(from_center) + "," + str(keep_tool_down) + "," + str(use_zig_zag) + "," + str(zig_angle) + "," + str(zig_unidirectional) + "," + str(start_point) + "," + str(cut_mode) PathAreaUtils.pocket( a, self.radius, extraoffset, stepover, depthparams, from_center, keep_tool_down, use_zig_zag, zig_angle, zig_unidirectional, start_point, cut_mode) return PathAreaUtils.retrieve_gcode()
def _buildPathLibarea(self, obj, edgelist): import PathScripts.PathKurveUtils as PathKurveUtils import math import area output = "" if obj.StartPoint and obj.UseStartPoint: startpoint = obj.StartPoint else: startpoint = None if obj.EndPoint and obj.UseEndPoint: endpoint = obj.EndPoint else: endpoint = None PathKurveUtils.output('mem') PathKurveUtils.feedrate_hv(self.horizFeed, self.vertFeed) output = "" output += "G0 Z" + str(obj.ClearanceHeight.Value) curve = PathKurveUtils.makeAreaCurve(edgelist, obj.Direction, startpoint, endpoint) '''The following line uses a profile function written for use with FreeCAD. It's clean but incomplete. It doesn't handle print "x = " + str(point.x) print "y - " + str(point.y) 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) depthparams = depth_params( obj.ClearanceHeight.Value, obj.SafeHeight.Value, obj.StartDepth.Value, obj.StepDown, 0.0, obj.FinalDepth.Value, None) PathKurveUtils.profile2( curve, obj.Side, self.radius, self.vertFeed, self.horizFeed, obj.OffsetExtra.Value, roll_radius, None, None, depthparams, extend_at_start, extend_at_end, lead_in_line_len, lead_out_line_len) output += PathKurveUtils.retrieve_gcode() return output
def execute(self, obj, getsim=False): PathLog.track() self.endVector = None if not obj.Active: path = Path.Path("(inactive operation)") obj.Path = path obj.ViewObject.Visibility = False return commandlist = [] toolLoad = obj.ToolController self.depthparams = depth_params( clearance_height=obj.ClearanceHeight.Value, safe_height=obj.SafeHeight.Value, start_depth=obj.StartDepth.Value, step_down=obj.StepDown.Value, z_finish_step=0.0, final_depth=obj.FinalDepth.Value, user_depths=None) if toolLoad is None or toolLoad.ToolNumber == 0: FreeCAD.Console.PrintError( "No Tool Controller is selected. We need a tool to build a Path." ) return else: self.vertFeed = toolLoad.VertFeed.Value self.horizFeed = toolLoad.HorizFeed.Value self.vertRapid = toolLoad.VertRapid.Value self.horizRapid = toolLoad.HorizRapid.Value tool = toolLoad.Proxy.getTool(toolLoad) if not tool or tool.Diameter == 0: FreeCAD.Console.PrintError( "No Tool found or diameter is zero. We need a tool to build a Path." ) return else: self.radius = tool.Diameter / 2 commandlist.append(Path.Command("(" + obj.Label + ")")) if obj.UseComp: commandlist.append( Path.Command("(Compensated Tool Path. Diameter: " + str(self.radius * 2) + ")")) else: commandlist.append(Path.Command("(Uncompensated Tool Path)")) parentJob = PathUtils.findParentJob(obj) if parentJob is None: return baseobject = parentJob.Base if baseobject is None: return # Let's always start by rapid to clearance...just for safety commandlist.append(Path.Command("G0", {"Z": obj.ClearanceHeight.Value})) isPanel = False if hasattr(baseobject, "Proxy"): if isinstance(baseobject.Proxy, ArchPanel.PanelSheet): # process the sheet isPanel = True baseobject.Proxy.execute(baseobject) shapes = baseobject.Proxy.getOutlines(baseobject, transform=True) for shape in shapes: f = Part.makeFace([shape], 'Part::FaceMakerSimple') thickness = baseobject.Group[0].Source.Thickness contourshape = f.extrude(FreeCAD.Vector(0, 0, thickness)) try: (pp, sim) = self._buildPathArea(obj, contourshape, start=obj.StartPoint, getsim=getsim) commandlist.extend(pp.Commands) except Exception as e: FreeCAD.Console.PrintError(e) FreeCAD.Console.PrintError( "Something unexpected happened. Unable to generate a contour path. Check project and tool config." ) if hasattr(baseobject, "Shape") and not isPanel: #bb = baseobject.Shape.BoundBox env = PathUtils.getEnvelope(partshape=baseobject.Shape, subshape=None, depthparams=self.depthparams) try: (pp, sim) = self._buildPathArea(obj, env, start=obj.StartPoint, getsim=getsim) commandlist.extend(pp.Commands) except Exception as e: FreeCAD.Console.PrintError(e) FreeCAD.Console.PrintError( "Something unexpected happened. Unable to generate a contour path. Check project and tool config." ) # Let's finish by rapid to clearance...just for safety commandlist.append(Path.Command("G0", {"Z": obj.ClearanceHeight.Value})) path = Path.Path(commandlist) obj.Path = path #obj.ViewObject.Visibility = True return sim
def buildpathocc(self, obj, shape): """Build pocket Path using Native OCC algorithm.""" import Part import DraftGeomUtils from PathScripts.PathUtils import fmt, helicalPlunge, rampPlunge, depth_params FreeCAD.Console.PrintMessage(translate("PathPocket", "Generating toolpath with OCC native offsets.\n")) extraoffset = obj.MaterialAllowance.Value # Build up the offset loops output = "" if obj.Comment != "": output += '(' + str(obj.Comment)+')\n' output += 'G0 Z' + fmt(obj.ClearanceHeight.Value) + "F " + PathUtils.fmt(self.vertRapid) + "\n" offsets = [] nextradius = self.radius + extraoffset result = DraftGeomUtils.pocket2d(shape, nextradius) while result: offsets.extend(result) nextradius += (self.radius * 2) * (float(obj.StepOver)/100) result = DraftGeomUtils.pocket2d(shape, nextradius) # revert the list so we start with the outer wires if obj.StartAt != 'Edge': offsets.reverse() plungePos = None rampEdge = None if obj.UseEntry: # Try to find an entry location toold = self.radius*2 helixBounds = DraftGeomUtils.pocket2d(shape, self.radius * (1 + obj.HelixSize)) if helixBounds: rampD = obj.RampSize if obj.StartAt == 'Edge': plungePos = helixBounds[0].Edges[0].Vertexes[0].Point else: plungePos = offsets[0].Edges[0].Vertexes[0].Point # If it turns out this is invalid for some reason, nuke plungePos [perp, idx] = DraftGeomUtils.findPerpendicular(plungePos, shape.Edges) if not perp or perp.Length < self.radius * (1 + obj.HelixSize): plungePos = None FreeCAD.Console.PrintError(translate("PathPocket", "Helical Entry location not found.\n")) # FIXME: Really need to do a point-in-polygon operation to make sure this is within helixBounds # Or some math to prove that it has to be (doubt that's true) # Maybe reverse helixBounds and pick off that? if plungePos is None: # If we didn't find a place to helix, how about a ramp? FreeCAD.Console.PrintMessage(translate("PathPocket", "Attempting ramp entry.\n")) if (offsets[0].Edges[0].Length >= toold * rampD) and not (isinstance(offsets[0].Edges[0].Curve, Part.Circle)): rampEdge = offsets[0].Edges[0] # The last edge also connects with the starting location- try that elif (offsets[0].Edges[-1].Length >= toold * rampD) and not (isinstance(offsets[0].Edges[-1].Curve, Part.Circle)): rampEdge = offsets[0].Edges[-1] else: FreeCAD.Console.PrintError(translate("PathPocket", "Ramp Entry location not found.\n")) # print "Neither edge works: " + str(offsets[0].Edges[0]) + ", " + str(offsets[0].Edges[-1]) # FIXME: There's got to be a smarter way to find a place to ramp # For helix-ing/ramping, know where we were last time # FIXME: Can probably get this from the "machine"? lastZ = obj.ClearanceHeight.Value startPoint = None depthparams = depth_params( obj.ClearanceHeight.Value, obj.SafeHeight.Value, obj.StartDepth.Value, obj.StepDown, obj.FinishDepth.Value, obj.FinalDepth.Value) for vpos in depthparams.get_depths(): first = True # loop over successive wires for currentWire in offsets: last = None for edge in currentWire.Edges: if not last: # we set the base GO to our fast move to our starting pos if first: # If we can helix, do so if plungePos: output += helicalPlunge(plungePos, obj.RampAngle, vpos, lastZ, self.radius*2, obj.HelixSize, self.horizFeed) lastZ = vpos # Otherwise, see if we can ramp # FIXME: This could be a LOT smarter (eg, searching for a longer leg of the edge to ramp along) elif rampEdge: output += rampPlunge(rampEdge, obj.RampAngle, vpos, lastZ) lastZ = vpos # Otherwise, straight plunge... Don't want to, but sometimes you might not have a choice. # FIXME: At least not with the lazy ramp programming above... else: print "WARNING: Straight-plunging... probably not good, but we didn't find a place to helix or ramp" startPoint = edge.Vertexes[0].Point output += "G0 Z" + fmt(obj.ClearanceHeight.Value) + "F " + PathUtils.fmt(self.vertRapid) + "\n" output += "G0 X" + fmt(startPoint.x) + " Y" + fmt(startPoint.y) +\ " Z" + fmt(obj.ClearanceHeight.Value) + "F " + PathUtils.fmt(self.horizRapid) + "\n" first = False # then move slow down to our starting point for our profile last = edge.Vertexes[0].Point output += "G1 X" + fmt(last.x) + " Y" + fmt(last.y) + " Z" + fmt(vpos) + " F" + fmt(self.vertFeed) + "\n" if DraftGeomUtils.geomType(edge) == "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" + fmt(point.x) + " Y" + fmt(point.y) + " Z" + fmt(vpos) output += " I" + fmt(relcenter.x) + " J" + fmt(relcenter.y) + " K" + fmt(relcenter.z) + " F" + fmt(self.horizFeed) 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" + fmt(point.x) + " Y" + fmt(point.y) + " Z" + fmt(vpos) + " F" + fmt(self.horizFeed) + "\n" last = point # move back up output += "G0 Z" + fmt(obj.ClearanceHeight.Value) + "F " + PathUtils.fmt(self.vertRapid) + "\n" return output
def execute(self, obj, getsim=False): import Part if not obj.Active: path = Path.Path("(inactive operation)") obj.Path = path obj.ViewObject.Visibility = False return self.depthparams = depth_params( clearance_height=obj.ClearanceHeight.Value, safe_height=obj.SafeHeight.Value, start_depth=obj.StartDepth.Value, step_down=obj.StepDown.Value, z_finish_step=0.0, final_depth=obj.FinalDepth.Value, user_depths=None) commandlist = [] toolLoad = obj.ToolController if toolLoad is None or toolLoad.ToolNumber == 0: FreeCAD.Console.PrintError("No Tool Controller is selected. We need a tool to build a Path.") return else: self.vertFeed = toolLoad.VertFeed.Value self.horizFeed = toolLoad.HorizFeed.Value self.vertRapid = toolLoad.VertRapid.Value self.horizRapid = toolLoad.HorizRapid.Value tool = toolLoad.Proxy.getTool(toolLoad) if not tool or tool.Diameter == 0: FreeCAD.Console.PrintError("No Tool found or diameter is zero. We need a tool to build a Path.") return else: self.radius = tool.Diameter/2 commandlist.append(Path.Command("(" + obj.Label + ")")) if obj.UseComp: commandlist.append(Path.Command("(Compensated Tool Path. Diameter: " + str(self.radius * 2) + ")")) else: commandlist.append(Path.Command("(Uncompensated Tool Path)")) parentJob = PathUtils.findParentJob(obj) if parentJob is None: return baseobject = parentJob.Base if baseobject is None: return if obj.Base: # The user has selected subobjects from the base. Process each. holes = [] faces = [] for b in obj.Base: for sub in b[1]: shape = getattr(b[0].Shape, sub) if isinstance(shape, Part.Face): faces.append(shape) if numpy.isclose(abs(shape.normalAt(0, 0).z), 1): # horizontal face holes += shape.Wires[1:] else: FreeCAD.Console.PrintWarning("found a base object which is not a face. Can't continue.") return for wire in holes: f = Part.makeFace(wire, 'Part::FaceMakerSimple') drillable = PathUtils.isDrillable(baseobject.Shape, wire) if (drillable and obj.processCircles) or (not drillable and obj.processHoles): env = PathUtils.getEnvelope(baseobject.Shape, subshape=f, depthparams=self.depthparams) try: (pp, sim) = self._buildPathArea(obj, baseobject=env, isHole=True, start=None, getsim=getsim) commandlist.extend(pp.Commands) except Exception as e: FreeCAD.Console.PrintError(e) FreeCAD.Console.PrintError("Something unexpected happened. Unable to generate a contour path. Check project and tool config.") if len(faces) > 0: profileshape = Part.makeCompound(faces) if obj.processPerimeter: env = PathUtils.getEnvelope(baseobject.Shape, subshape=profileshape, depthparams=self.depthparams) try: (pp, sim) = self._buildPathArea(obj, baseobject=env, start=None, getsim=getsim) commandlist.extend(pp.Commands) except Exception as e: FreeCAD.Console.PrintError(e) FreeCAD.Console.PrintError("Something unexpected happened. Unable to generate a contour path. Check project and tool config.") else: # Try to build targets from the job base if hasattr(baseobject, "Proxy"): if isinstance(baseobject.Proxy, ArchPanel.PanelSheet): # process the sheet if obj.processPerimeter: shapes = baseobject.Proxy.getOutlines(baseobject, transform=True) for shape in shapes: for wire in shape.Wires: f = Part.makeFace(wire, 'Part::FaceMakerSimple') env = PathUtils.getEnvelope(baseobject.Shape, subshape=f, depthparams=self.depthparams) try: (pp, sim) = self._buildPathArea(obj, baseobject=env, isHole=False, start=None, getsim=getsim) commandlist.extend(pp.Commands) except Exception as e: FreeCAD.Console.PrintError(e) FreeCAD.Console.PrintError("Something unexpected happened. Unable to generate a contour path. Check project and tool config.") shapes = baseobject.Proxy.getHoles(baseobject, transform=True) for shape in shapes: for wire in shape.Wires: drillable = PathUtils.isDrillable(baseobject.Proxy, wire) if (drillable and obj.processCircles) or (not drillable and obj.processHoles): f = Part.makeFace(wire, 'Part::FaceMakerSimple') env = PathUtils.getEnvelope(baseobject.Shape, subshape=f, depthparams=self.depthparams) try: (pp, sim) = self._buildPathArea(obj, baseobject=env, isHole=True, start=None, getsim=getsim) commandlist.extend(pp.Commands) except Exception as e: FreeCAD.Console.PrintError(e) FreeCAD.Console.PrintError("Something unexpected happened. Unable to generate a contour path. Check project and tool config.") # Let's finish by rapid to clearance...just for safety commandlist.append(Path.Command("G0", {"Z": obj.ClearanceHeight.Value})) path = Path.Path(commandlist) obj.Path = path
def execute(self, obj, getsim=False): commandlist = [] sim = None if not obj.Active: path = Path.Path("(inactive operation)") obj.Path = path obj.ViewObject.Visibility = False return parentJob = PathUtils.findParentJob(obj) if parentJob is None: return baseobject = parentJob.Base if baseobject is None: return self.depthparams = depth_params( clearance_height=obj.ClearanceHeight.Value, safe_height=obj.SafeHeight.Value, start_depth=obj.StartDepth.Value, step_down=obj.StepDown.Value, z_finish_step=0.0, final_depth=obj.FinalDepth.Value, user_depths=None) toolLoad = obj.ToolController if toolLoad is None or toolLoad.ToolNumber == 0: FreeCAD.Console.PrintError( "No Tool Controller is selected. We need a tool to build a Path." ) else: self.vertFeed = toolLoad.VertFeed.Value self.horizFeed = toolLoad.HorizFeed.Value self.vertRapid = toolLoad.VertRapid.Value self.horizRapid = toolLoad.HorizRapid.Value tool = toolLoad.Proxy.getTool(toolLoad) if not tool or tool.Diameter == 0: FreeCAD.Console.PrintError( "No Tool found or diameter is zero. We need a tool to build a Path." ) return else: self.radius = tool.Diameter / 2 commandlist.append(Path.Command("(" + obj.Label + ")")) if obj.UseComp: commandlist.append( Path.Command("(Compensated Tool Path. Diameter: " + str(self.radius * 2) + ")")) else: commandlist.append(Path.Command("(Uncompensated Tool Path)")) if obj.Base: wires = [] for b in obj.Base: edgelist = [] for sub in b[1]: edgelist.append(getattr(b[0].Shape, sub)) wires.extend(findWires(edgelist)) for wire in wires: f = Part.makeFace(wire, 'Part::FaceMakerSimple') # shift the compound to the bottom of the base object for # proper sectioning zShift = b[0].Shape.BoundBox.ZMin - f.BoundBox.ZMin newPlace = FreeCAD.Placement(FreeCAD.Vector(0, 0, zShift), f.Placement.Rotation) f.Placement = newPlace env = PathUtils.getEnvelope(baseobject.Shape, subshape=f, depthparams=self.depthparams) try: (pp, sim) = self._buildPathArea(obj, baseobject=env, start=obj.StartPoint, getsim=getsim) commandlist.extend(pp.Commands) except Exception as e: FreeCAD.Console.PrintError(e) FreeCAD.Console.PrintError( "Something unexpected happened. Unable to generate a contour path. Check project and tool config." ) # Let's finish by rapid to clearance...just for safety commandlist.append(Path.Command("G0", {"Z": obj.ClearanceHeight.Value})) path = Path.Path(commandlist) obj.Path = path obj.ViewObject.Visibility = True return sim
def execute(self, obj, getsim=False): PathLog.track() commandlist = [] simlist = [] commandlist.append(Path.Command("(" + obj.Label + ")")) if not obj.Active: path = Path.Path("(inactive operation)") obj.Path = path obj.ViewObject.Visibility = False return parentJob = PathUtils.findParentJob(obj) if parentJob is None: return baseobject = parentJob.Base if baseobject is None: return self.depthparams = depth_params( clearance_height=obj.ClearanceHeight.Value, safe_height=obj.SafeHeight.Value, start_depth=obj.StartDepth.Value, step_down=obj.StepDown.Value, z_finish_step=0.0, final_depth=obj.FinalDepth.Value, user_depths=None) toolLoad = obj.ToolController if toolLoad is None or toolLoad.ToolNumber == 0: FreeCAD.Console.PrintError( "No Tool Controller is selected. We need a tool to build a Path." ) else: self.vertFeed = toolLoad.VertFeed.Value self.horizFeed = toolLoad.HorizFeed.Value self.vertRapid = toolLoad.VertRapid.Value self.horizRapid = toolLoad.HorizRapid.Value tool = toolLoad.Proxy.getTool(toolLoad) if not tool or tool.Diameter == 0: FreeCAD.Console.PrintError( "No Tool found or diameter is zero. We need a tool to build a Path." ) return else: self.radius = tool.Diameter / 2 if obj.Base: PathLog.debug("base items exist. Processing...") for b in obj.Base: PathLog.debug("Base item: {}".format(b)) for sub in b[1]: if "Face" in sub: shape = Part.makeCompound([getattr(b[0].Shape, sub)]) #shape = getattr(b[0].Shape, sub) else: edges = [getattr(b[0].Shape, sub) for sub in b[1]] shape = Part.makeFace(edges, 'Part::FaceMakerSimple') env = PathUtils.getEnvelope(baseobject.Shape, subshape=shape, depthparams=self.depthparams) removal = env.cut(baseobject.Shape) if PathLog.getLevel( PathLog.thisModule()) == PathLog.Level.DEBUG: removalshape = FreeCAD.ActiveDocument.addObject( "Part::Feature", "removalshape") removalshape.Shape = removal try: (pp, sim) = self._buildPathArea(obj, removal, getsim=getsim) if sim is not None: simlist.append(sim) commandlist.extend(pp.Commands) except Exception as e: FreeCAD.Console.PrintError(e) FreeCAD.Console.PrintError( "Something unexpected happened. Unable to generate a pocket path. Check project and tool config." ) else: # process the job base object as a whole PathLog.debug("processing the whole job base object") env = PathUtils.getEnvelope(baseobject.Shape, subshape=None, depthparams=self.depthparams) removal = env.cut(baseobject.Shape) if PathLog.getLevel(PathLog.thisModule()) == PathLog.Level.DEBUG: removalshape = FreeCAD.ActiveDocument.addObject( "Part::Feature", "removalshape") removalshape.Shape = removal try: (pp, sim) = self._buildPathArea(obj, removal, getsim=getsim) commandlist.extend(pp.Commands) if sim is not None: simlist.append(sim) #commandlist.extend(self._buildPathArea(obj, env.cut(baseobject.Shape)).Commands) except Exception as e: FreeCAD.Console.PrintError(e) FreeCAD.Console.PrintError( "Something unexpected happened. Unable to generate a pocket path. Check project and tool config." ) # Let's finish by rapid to clearance...just for safety commandlist.append(Path.Command("G0", {"Z": obj.ClearanceHeight.Value})) path = Path.Path(commandlist) obj.Path = path obj.ViewObject.Visibility = True PathLog.debug(simlist) simshape = None if len(simlist) > 1: simshape = simlist[0].fuse(simlist[1:]) elif len(simlist) == 1: simshape = simlist[0] if simshape is not None and PathLog.getLevel( PathLog.thisModule()) == PathLog.Level.DEBUG: sim = FreeCAD.ActiveDocument.addObject("Part::Feature", "simshape") sim.Shape = simshape return simshape
def _waterline(self, obj, s, bb): import ocl from PathScripts.PathUtils import depth_params, fmt import time def drawLoops(loops): nloop = 0 waterlinestring = "" waterlinestring += "(waterline begin)" for loop in loops: p = loop[0] loopstring = "(loop begin)" + "\n" loopstring += "G0 Z" + str(obj.SafeHeight.Value) + "\n" loopstring += "G0 X" + \ str(fmt(p.x)) + " Y" + str(fmt(p.y)) + "\n" loopstring += "G1 Z" + str(fmt(p.z)) + "\n" for p in loop[1:]: loopstring += "G1 X" + \ str(fmt(p.x)) + " Y" + str(fmt(p.y)) + \ " Z" + str(fmt(p.z)) + "\n" zheight = p.z p = loop[0] loopstring += "G1 X" + \ str(fmt(p.x)) + " Y" + str(fmt(p.y)) + \ " Z" + str(fmt(zheight)) + "\n" loopstring += "(loop end)" + "\n" print " loop ", nloop, " with ", len(loop), " points" nloop = nloop + 1 waterlinestring += loopstring waterlinestring += "(waterline end)" + "\n" return waterlinestring depthparams = depth_params(obj.ClearanceHeight.Value, obj.SafeHeight.Value, obj.StartDepth.Value, obj.StepDown, obj.FinishDepth.Value, obj.FinalDepth.Value) # stlfile = "../../stl/gnu_tux_mod.stl" # surface = STLSurfaceSource(stlfile) surface = s t_before = time.time() zheights = depthparams.get_depths() wl = ocl.Waterline() # wl = ocl.AdaptiveWaterline() # this is slower, ca 60 seconds on i7 # CPU wl.setSTL(surface) diam = 0.5 length = 10.0 # any ocl MillingCutter class should work here cutter = ocl.BallCutter(diam, length) wl.setCutter(cutter) # this should be smaller than the smallest details in the STL file wl.setSampling(obj.SampleInterval) # AdaptiveWaterline() also has settings for minimum sampling interval # (see c++ code) all_loops = [] for zh in zheights: print "calculating Waterline at z= ", zh wl.reset() wl.setZ(zh) # height for this waterline wl.run() all_loops.append(wl.getLoops()) t_after = time.time() calctime = t_after - t_before n = 0 output = "" for loops in all_loops: # at each z-height, we may get many loops print " %d/%d:" % (n, len(all_loops)) output += drawLoops(loops) n = n + 1 print "(" + str(calctime) + ")" return output
def _buildPathLibarea(self, obj, edgelist, isHole): import PathScripts.PathKurveUtils as PathKurveUtils import math import area output = "" if obj.Comment != "": output += '(' + str(obj.Comment) + ')\n' if obj.StartPoint and obj.UseStartPoint: startpoint = obj.StartPoint else: startpoint = None if obj.EndPoint and obj.UseEndPoint: endpoint = obj.EndPoint else: endpoint = None PathKurveUtils.output('mem') PathKurveUtils.feedrate_hv(self.horizFeed, self.vertFeed) # Reverse the direction for holes if isHole: direction = "CW" if obj.Direction == "CCW" else "CCW" else: direction = obj.Direction output = "" output += "G0 Z" + str( obj.ClearanceHeight.Value) + "F " + PathUtils.fmt( self.vertRapid) + "\n" 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 print "x = " + str(point.x) print "y - " + str(point.y) 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 h toolLoad = obj.activeTCeekscnc 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) depthparams = depth_params(obj.ClearanceHeight.Value, obj.SafeHeight.Value, obj.StartDepth.Value, obj.StepDown, 0.0, obj.FinalDepth.Value, None) PathKurveUtils.profile2(curve, obj.Side, self.radius, self.vertFeed, self.horizFeed, self.vertRapid, self.horizRapid, obj.OffsetExtra.Value, roll_radius, None, None, depthparams, extend_at_start, extend_at_end, lead_in_line_len, lead_out_line_len) output += PathKurveUtils.retrieve_gcode() return output
def execute(self, obj): PathLog.track() if not obj.Active: path = Path.Path("(inactive operation)") obj.Path = path obj.ViewObject.Visibility = False return commandlist = [] toolLoad = obj.ToolController self.depthparams = depth_params( clearance_height=obj.ClearanceHeight.Value, safe_height=obj.SafeHeight.Value, start_depth=obj.StartDepth.Value, step_down=obj.StepDown, z_finish_step=obj.FinishDepth.Value, final_depth=obj.FinalDepth.Value, user_depths=None) if toolLoad is None or toolLoad.ToolNumber == 0: FreeCAD.Console.PrintError( "No Tool Controller is selected. We need a tool to build a Path." ) return else: self.vertFeed = toolLoad.VertFeed.Value self.horizFeed = toolLoad.HorizFeed.Value self.vertRapid = toolLoad.VertRapid.Value self.horizRapid = toolLoad.HorizRapid.Value tool = toolLoad.Proxy.getTool(toolLoad) if tool.Diameter == 0: FreeCAD.Console.PrintError( "No Tool found or diameter is zero. We need a tool to build a Path." ) return else: self.radius = tool.Diameter / 2 commandlist.append(Path.Command("(" + obj.Label + ")")) # Facing is done either against base objects if obj.Base: PathLog.debug("obj.Base: {}".format(obj.Base)) faces = [] for b in obj.Base: for sub in b[1]: shape = getattr(b[0].Shape, sub) if isinstance(shape, Part.Face): faces.append(shape) else: PathLog.debug('The base subobject is not a face') return planeshape = Part.makeCompound(faces) PathLog.info("Working on a collection of faces {}".format(faces)) # If no base object, do planing of top surface of entire model else: parentJob = PathUtils.findParentJob(obj) if parentJob is None: PathLog.debug("No base object. No parent job found") return baseobject = parentJob.Base if baseobject is None: PathLog.debug("Parent job exists but no Base Object") return planeshape = baseobject.Shape PathLog.info("Working on a shape {}".format(baseobject.Name)) # Let's start by rapid to clearance...just for safety commandlist.append(Path.Command("G0", {"Z": obj.ClearanceHeight.Value})) # if user wants the boundbox, calculate that PathLog.info("Boundary Shape: {}".format(obj.BoundaryShape)) bb = planeshape.BoundBox if obj.BoundaryShape == 'Boundbox': bbperim = Part.makeBox(bb.XLength, bb.YLength, 1, FreeCAD.Vector(bb.XMin, bb.YMin, bb.ZMin), FreeCAD.Vector(0, 0, 1)) env = PathUtils.getEnvelope(partshape=bbperim, depthparams=self.depthparams) else: env = PathUtils.getEnvelope(partshape=planeshape, depthparams=self.depthparams) try: commandlist.extend(self._buildPathArea(obj, env).Commands) except Exception as e: FreeCAD.Console.PrintError(e) FreeCAD.Console.PrintError( translate( "Path_MillFace", "The selected settings did not produce a valid path.\n")) # Let's finish by rapid to clearance...just for safety commandlist.append(Path.Command("G0", {"Z": obj.ClearanceHeight.Value})) path = Path.Path(commandlist) obj.Path = path obj.ViewObject.Visibility = True