def circularHoleExecute(self, obj, holes): '''circularHoleExecute(obj, holes) ... generate drill operation for each hole in holes.''' PathLog.track() self.commandlist.append(Path.Command("(Begin Drilling)")) # rapid to clearance height self.commandlist.append(Path.Command('G0', {'Z': obj.ClearanceHeight.Value, 'F': self.vertRapid})) tiplength = 0.0 if obj.ExtraOffset == 'Drill Tip': tiplength = PathUtils.drillTipLength(self.tool) elif obj.ExtraOffset == '2x Drill Tip': tiplength = PathUtils.drillTipLength(self.tool) * 2 holes = PathUtils.sort_jobs(holes, ['x', 'y']) self.commandlist.append(Path.Command('G90')) self.commandlist.append(Path.Command(obj.ReturnLevel)) cmd = "G81" cmdParams = {} cmdParams['Z'] = obj.FinalDepth.Value - tiplength cmdParams['F'] = self.vertFeed cmdParams['R'] = obj.RetractHeight.Value if obj.PeckEnabled and obj.PeckDepth.Value > 0: cmd = "G83" cmdParams['Q'] = obj.PeckDepth.Value elif obj.DwellEnabled and obj.DwellTime > 0: cmd = "G82" cmdParams['P'] = obj.DwellTime # parentJob = PathUtils.findParentJob(obj) # startHeight = obj.StartDepth.Value + parentJob.SetupSheet.SafeHeightOffset.Value startHeight = obj.StartDepth.Value + self.job.SetupSheet.SafeHeightOffset.Value for p in holes: params = {} params['X'] = p['x'] params['Y'] = p['y'] # move to hole location self.commandlist.append(Path.Command('G0', {'X': p['x'], 'Y': p['y'], 'F': self.horizRapid})) self.commandlist.append(Path.Command('G0', {'Z': startHeight, 'F': self.vertRapid})) self.commandlist.append(Path.Command('G1', {'Z': obj.StartDepth.Value, 'F': self.vertFeed})) # Update changes to parameters params.update(cmdParams) # Perform canned drilling cycle self.commandlist.append(Path.Command(cmd, params)) # Cancel canned drilling cycle self.commandlist.append(Path.Command('G80')) self.commandlist.append(Path.Command('G0', {'Z': obj.SafeHeight.Value}))
def opExecute(self, obj, getsim=False): '''opExecute(obj, getsim=False) ... implementation of Path.Area ops. determines the parameters for _buildPathArea(). Do not overwrite, implement areaOpAreaParams(obj, isHole) ... op specific area param dictionary areaOpPathParams(obj, isHole) ... op specific path param dictionary areaOpShapes(obj) ... the shape for path area to process areaOpUseProjection(obj) ... return true if operation can use projection instead.''' PathLog.track() self.endVector = None finish_step = obj.FinishDepth.Value if hasattr(obj, "FinishDepth") else 0.0 self.depthparams = PathUtils.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=finish_step, final_depth=obj.FinalDepth.Value, user_depths=None) if PathOp.FeatureStartPoint & self.opFeatures(obj) and obj.UseStartPoint: start = obj.StartPoint else: start = None shapes = self.areaOpShapes(obj) jobs = [{ 'x': s[0].BoundBox.XMax, 'y': s[0].BoundBox.YMax, 'shape': s } for s in shapes] jobs = PathUtils.sort_jobs(jobs, ['x', 'y']) shapes = [j['shape'] for j in jobs] sims = [] for (shape, isHole) in shapes: try: (pp, sim) = self._buildPathArea(obj, shape, isHole, start, getsim) self.commandlist.extend(pp.Commands) sims.append(sim) except Exception as e: FreeCAD.Console.PrintError(e) FreeCAD.Console.PrintError("Something unexpected happened. Check project and tool config.") if self.areaOpRetractTool(obj): self.endVector = None return sims
def circularHoleExecute(self, obj, holes): '''circularHoleExecute(obj, holes) ... generate drill operation for each hole in holes.''' PathLog.track() self.commandlist.append(Path.Command("(Begin Drilling)")) # rapid to clearance height self.commandlist.append( Path.Command('G0', { 'Z': obj.ClearanceHeight.Value, 'F': self.vertRapid })) tiplength = 0.0 if obj.AddTipLength: tiplength = PathUtils.drillTipLength(self.tool) holes = PathUtils.sort_jobs(holes, ['x', 'y']) self.commandlist.append(Path.Command('G90')) self.commandlist.append(Path.Command(obj.ReturnLevel)) # ml: I'm not sure whey these were here, they seem redundant ## rapid to first hole location, with spindle still retracted: #p0 = holes[0] #self.commandlist.append(Path.Command('G0', {'X': p0['x'], 'Y': p0['y'], 'F': self.horizRapid})) ## move tool to clearance plane #self.commandlist.append(Path.Command('G0', {'Z': obj.ClearanceHeight.Value, 'F': self.vertRapid})) cmd = "G81" cmdParams = {} cmdParams['Z'] = obj.FinalDepth.Value - tiplength cmdParams['F'] = self.vertFeed cmdParams['R'] = obj.RetractHeight.Value if obj.PeckEnabled and obj.PeckDepth.Value > 0: cmd = "G83" cmdParams['Q'] = obj.PeckDepth.Value elif obj.DwellEnabled and obj.DwellTime > 0: cmd = "G82" cmdParams['P'] = obj.DwellTime for p in holes: params = {} params['X'] = p['x'] params['Y'] = p['y'] params.update(cmdParams) self.commandlist.append(Path.Command(cmd, params)) self.commandlist.append(Path.Command('G80'))
def circularHoleExecute(self, obj, holes): '''circularHoleExecute(obj, holes) ... generate drill operation for each hole in holes.''' PathLog.track() self.commandlist.append(Path.Command("(Begin Drilling)")) # rapid to clearance height self.commandlist.append(Path.Command('G0', {'Z': obj.ClearanceHeight.Value, 'F': self.vertRapid})) tiplength = 0.0 if obj.AddTipLength: tiplength = PathUtils.drillTipLength(self.tool) holes = PathUtils.sort_jobs(holes, ['x', 'y']) self.commandlist.append(Path.Command('G90')) self.commandlist.append(Path.Command(obj.ReturnLevel)) # ml: I'm not sure whey these were here, they seem redundant ## rapid to first hole location, with spindle still retracted: #p0 = holes[0] #self.commandlist.append(Path.Command('G0', {'X': p0['x'], 'Y': p0['y'], 'F': self.horizRapid})) ## move tool to clearance plane #self.commandlist.append(Path.Command('G0', {'Z': obj.ClearanceHeight.Value, 'F': self.vertRapid})) cmd = "G81" cmdParams = {} cmdParams['Z'] = obj.FinalDepth.Value - tiplength cmdParams['F'] = self.vertFeed cmdParams['R'] = obj.RetractHeight.Value if obj.PeckEnabled and obj.PeckDepth.Value > 0: cmd = "G83" cmdParams['Q'] = obj.PeckDepth.Value elif obj.DwellEnabled and obj.DwellTime > 0: cmd = "G82" cmdParams['P'] = obj.DwellTime for p in holes: params = {} params['X'] = p['x'] params['Y'] = p['y'] params.update(cmdParams) self.commandlist.append(Path.Command(cmd, params)) self.commandlist.append(Path.Command('G80'))
def execute(self, obj): PathLog.track() if not obj.Active: path = Path.Path("(inactive operation)") obj.Path = path obj.ViewObject.Visibility = False return output = "" if obj.Comment != "": output += '(' + str(obj.Comment) + ')\n' 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 tiplength = 0.0 if obj.AddTipLength: tiplength = PathUtils.drillTipLength(tool) if len(obj.Names) == 0: parentJob = PathUtils.findParentJob(obj) if parentJob is None: return baseobject = parentJob.Base if baseobject is None: return # Arch PanelSheet if hasattr(baseobject, "Proxy"): holes = [] if isinstance(baseobject.Proxy, ArchPanel.PanelSheet): baseobject.Proxy.execute(baseobject) i = 0 holeshapes = baseobject.Proxy.getHoles(baseobject, transform=True) tooldiameter = obj.ToolController.Proxy.getTool( obj.ToolController).Diameter for holeshape in holeshapes: PathLog.debug('Entering new HoleShape') for wire in holeshape.Wires: PathLog.debug('Entering new Wire') for edge in wire.Edges: if PathUtils.isDrillable( baseobject, edge, tooldiameter): PathLog.debug( 'Found drillable hole edges: {}'. format(edge)) x = edge.Curve.Center.x y = edge.Curve.Center.y diameter = edge.BoundBox.XLength holes.append({ 'x': x, 'y': y, 'featureName': baseobject.Name + '.' + 'Drill' + str(i), 'd': diameter }) i = i + 1 else: holes = self.findHoles(obj, baseobject.Shape) for i in range(len(holes)): holes[i]['featureName'] = baseobject.Name + '.' + holes[i][ 'featureName'] names = [] positions = [] enabled = [] diameters = [] for h in holes: if len(names) == 0: self.setDepths(obj, baseobject, h) names.append(h['featureName']) positions.append(FreeCAD.Vector(h['x'], h['y'], 0)) enabled.append(1) diameters.append(h['d']) obj.Names = names obj.Positions = positions obj.Enabled = enabled obj.Diameters = diameters locations = [] output = "(Begin Drilling)\n" for i in range(len(obj.Names)): if obj.Enabled[i] > 0: locations.append({ 'x': obj.Positions[i].x, 'y': obj.Positions[i].y }) if len(locations) > 0: locations = PathUtils.sort_jobs(locations, ['x', 'y']) output += "G90 " + obj.ReturnLevel + "\n" # rapid to clearance height output += "G0 Z" + str( obj.ClearanceHeight.Value) + "F " + PathUtils.fmt( self.vertRapid) + "\n" # rapid to first hole location, with spindle still retracted: p0 = locations[0] output += "G0 X" + fmt(p0['x']) + " Y" + fmt( p0['y']) + "F " + PathUtils.fmt(self.horizRapid) + "\n" # move tool to clearance plane output += "G0 Z" + fmt( obj.ClearanceHeight.Value) + "F " + PathUtils.fmt( self.vertRapid) + "\n" pword = "" qword = "" if obj.PeckDepth.Value > 0 and obj.PeckEnabled: cmd = "G83" qword = " Q" + fmt(obj.PeckDepth.Value) elif obj.DwellTime > 0 and obj.DwellEnabled: cmd = "G82" pword = " P" + fmt(obj.DwellTime) else: cmd = "G81" for p in locations: output += cmd + \ " X" + fmt(p['x']) + \ " Y" + fmt(p['y']) + \ " Z" + fmt(obj.FinalDepth.Value - tiplength) + qword + pword + \ " R" + str(obj.RetractHeight.Value) + \ " F" + str(self.vertFeed) + "\n" \ output += "G80\n" path = Path.Path(output) obj.Path = path
def opExecute(self, obj, getsim=False): # pylint: disable=arguments-differ '''opExecute(obj, getsim=False) ... implementation of Path.Area ops. determines the parameters for _buildPathArea(). Do not overwrite, implement areaOpAreaParams(obj, isHole) ... op specific area param dictionary areaOpPathParams(obj, isHole) ... op specific path param dictionary areaOpShapes(obj) ... the shape for path area to process areaOpUseProjection(obj) ... return true if operation can use projection instead.''' PathLog.track() # Instantiate class variables for operation reference self.endVector = None # pylint: disable=attribute-defined-outside-init self.rotateFlag = False # pylint: disable=attribute-defined-outside-init self.leadIn = 2.0 # pylint: disable=attribute-defined-outside-init self.cloneNames = [] # pylint: disable=attribute-defined-outside-init self.guiMsgs = [] # pylint: disable=attribute-defined-outside-init self.tempObjectNames = [] # pylint: disable=attribute-defined-outside-init self.stockBB = PathUtils.findParentJob(obj).Stock.Shape.BoundBox # pylint: disable=attribute-defined-outside-init self.useTempJobClones( 'Delete') # Clear temporary group and recreate for temp job clones self.rotStartDepth = None # pylint: disable=attribute-defined-outside-init if obj.EnableRotation != 'Off': # Calculate operation heights based upon rotation radii opHeights = self.opDetermineRotationRadii(obj) (self.xRotRad, self.yRotRad, self.zRotRad) = opHeights[0] # pylint: disable=attribute-defined-outside-init (self.clrOfset, self.safOfst) = opHeights[1] # pylint: disable=attribute-defined-outside-init # Set clearance and safe heights based upon rotation radii if obj.EnableRotation == 'A(x)': strDep = self.xRotRad elif obj.EnableRotation == 'B(y)': strDep = self.yRotRad else: strDep = max(self.xRotRad, self.yRotRad) finDep = -1 * strDep self.rotStartDepth = strDep obj.ClearanceHeight.Value = strDep + self.clrOfset obj.SafeHeight.Value = strDep + self.safOfst # Create visual axes when debugging. if PathLog.getLevel(PathLog.thisModule()) == 4: self.visualAxis() else: strDep = obj.StartDepth.Value finDep = obj.FinalDepth.Value # Set axial feed rates based upon horizontal feed rates safeCircum = 2 * math.pi * obj.SafeHeight.Value self.axialFeed = 360 / safeCircum * self.horizFeed # pylint: disable=attribute-defined-outside-init self.axialRapid = 360 / safeCircum * self.horizRapid # pylint: disable=attribute-defined-outside-init # Initiate depthparams and calculate operation heights for rotational operation self.depthparams = self._customDepthParams(obj, obj.StartDepth.Value, obj.FinalDepth.Value) # Set start point if PathOp.FeatureStartPoint & self.opFeatures( obj) and obj.UseStartPoint: start = obj.StartPoint else: start = None aOS = self.areaOpShapes(obj) # pylint: disable=assignment-from-no-return # Adjust tuples length received from other PathWB tools/operations beside PathPocketShape shapes = [] for shp in aOS: if len(shp) == 2: (fc, iH) = shp # fc, iH, sub, angle, axis, strtDep, finDep tup = fc, iH, 'otherOp', 0.0, 'S', obj.StartDepth.Value, obj.FinalDepth.Value shapes.append(tup) else: shapes.append(shp) if len(shapes) > 1: jobs = list() for s in shapes: if s[2] == 'OpenEdge': shp = Part.makeCompound(s[0]) else: shp = s[0] jobs.append({ 'x': shp.BoundBox.XMax, 'y': shp.BoundBox.YMax, 'shape': s }) jobs = PathUtils.sort_jobs(jobs, ['x', 'y']) shapes = [j['shape'] for j in jobs] sims = [] numShapes = len(shapes) for ns in range(0, numShapes): profileEdgesIsOpen = False (shape, isHole, sub, angle, axis, strDep, finDep) = shapes[ns] # pylint: disable=unused-variable if sub == 'OpenEdge': profileEdgesIsOpen = True if PathOp.FeatureStartPoint & self.opFeatures( obj) and obj.UseStartPoint: osp = obj.StartPoint self.commandlist.append( Path.Command('G0', { 'X': osp.x, 'Y': osp.y, 'F': self.horizRapid })) if ns < numShapes - 1: nextAxis = shapes[ns + 1][4] else: nextAxis = 'L' self.depthparams = self._customDepthParams(obj, strDep, finDep) try: if profileEdgesIsOpen: (pp, sim) = self._buildProfileOpenEdges( obj, shape, isHole, start, getsim) else: (pp, sim) = self._buildPathArea(obj, shape, isHole, start, getsim) except Exception as e: # pylint: disable=broad-except FreeCAD.Console.PrintError(e) FreeCAD.Console.PrintError( "Something unexpected happened. Check project and tool config." ) else: if profileEdgesIsOpen: ppCmds = pp else: ppCmds = pp.Commands if obj.EnableRotation != 'Off' and self.rotateFlag is True: # Rotate model to index for cut if axis == 'X': axisOfRot = 'A' elif axis == 'Y': axisOfRot = 'B' elif axis == 'Z': axisOfRot = 'C' else: axisOfRot = 'A' # Rotate Model to correct angle ppCmds.insert( 0, Path.Command('G0', { axisOfRot: angle, 'F': self.axialRapid })) # Raise cutter to safe height ppCmds.insert( 0, Path.Command('G0', { 'Z': obj.SafeHeight.Value, 'F': self.vertRapid })) # Return index to starting position if axis of rotation changes. if numShapes > 1: if ns != numShapes - 1: if axis != nextAxis: ppCmds.append( Path.Command('G0', { axisOfRot: 0.0, 'F': self.axialRapid })) # Eif # Save gcode commands to object command list self.commandlist.extend(ppCmds) sims.append(sim) # Eif if self.areaOpRetractTool( obj) and self.endVector is not None and len( self.commandlist) > 1: self.endVector[2] = obj.ClearanceHeight.Value self.commandlist.append( Path.Command('G0', { 'Z': obj.ClearanceHeight.Value, 'F': self.vertRapid })) # Raise cutter to safe height and rotate back to original orientation # based on next rotational operation in job if self.rotateFlag is True: resetAxis = False lastJobOp = None nextJobOp = None opIdx = 0 JOB = PathUtils.findParentJob(obj) jobOps = JOB.Operations.Group numJobOps = len(jobOps) for joi in range(0, numJobOps): jo = jobOps[joi] if jo.Name == obj.Name: opIdx = joi lastOpIdx = opIdx - 1 nextOpIdx = opIdx + 1 if lastOpIdx > -1: lastJobOp = jobOps[lastOpIdx] if nextOpIdx < numJobOps: nextJobOp = jobOps[nextOpIdx] if lastJobOp is not None: if hasattr(lastJobOp, 'EnableRotation'): PathLog.debug( 'Last Op, {}, has `EnableRotation` set to {}'.format( lastJobOp.Label, lastJobOp.EnableRotation)) if lastJobOp.EnableRotation != obj.EnableRotation: resetAxis = True # if ns == numShapes - 1: # If last shape, check next op EnableRotation setting if nextJobOp is not None: if hasattr(nextJobOp, 'EnableRotation'): PathLog.debug( 'Next Op, {}, has `EnableRotation` set to {}'.format( nextJobOp.Label, nextJobOp.EnableRotation)) if nextJobOp.EnableRotation != obj.EnableRotation: resetAxis = True # Raise to safe height if rotation activated self.commandlist.append( Path.Command('G0', { 'Z': obj.SafeHeight.Value, 'F': self.vertRapid })) # reset rotational axes if necessary if resetAxis is True: self.commandlist.append( Path.Command('G0', { 'A': 0.0, 'F': self.axialRapid })) self.commandlist.append( Path.Command('G0', { 'B': 0.0, 'F': self.axialRapid })) self.useTempJobClones( 'Delete') # Delete temp job clone group and contents self.guiMessage('title', None, show=True) # Process GUI messages to user for ton in self.tempObjectNames: # remove temporary objects by name FreeCAD.ActiveDocument.removeObject(ton) PathLog.debug("obj.Name: " + str(obj.Name) + "\n\n") return sims
def circularHoleExecute(self, obj, holes): '''circularHoleExecute(obj, holes) ... generate drill operation for each hole in holes.''' PathLog.track() PathLog.debug("\ncircularHoleExecute() in PathDrilling.py") lastAxis = None lastAngle = 0.0 self.commandlist.append(Path.Command("(Begin Drilling)")) # rapid to clearance height self.commandlist.append( Path.Command('G0', { 'Z': obj.ClearanceHeight.Value, 'F': self.vertRapid })) tiplength = 0.0 if obj.AddTipLength: tiplength = PathUtils.drillTipLength(self.tool) holes = PathUtils.sort_jobs(holes, ['x', 'y']) self.commandlist.append(Path.Command('G90')) self.commandlist.append(Path.Command(obj.ReturnLevel)) for p in holes: cmd = "G81" cmdParams = {} cmdParams['Z'] = p['trgtDep'] - tiplength cmdParams['F'] = self.vertFeed cmdParams['R'] = obj.RetractHeight.Value if obj.PeckEnabled and obj.PeckDepth.Value > 0: cmd = "G83" cmdParams['Q'] = obj.PeckDepth.Value elif obj.DwellEnabled and obj.DwellTime > 0: cmd = "G82" cmdParams['P'] = obj.DwellTime params = {} params['X'] = p['x'] params['Y'] = p['y'] params.update(cmdParams) if obj.EnableRotation != 'Off': angle = p['angle'] axis = p['axis'] # Rotate model to index for hole if axis == 'X': axisOfRot = 'A' elif axis == 'Y': axisOfRot = 'B' # Reverse angle temporarily to match model. Error in FreeCAD render of B axis rotations if obj.B_AxisErrorOverride is True: angle = -1 * angle elif axis == 'Z': axisOfRot = 'C' else: axisOfRot = 'A' # Set initial values for last axis and angle if lastAxis is None: lastAxis = axisOfRot lastAngle = angle # Handle axial and angular transitions if axisOfRot != lastAxis: self.commandlist.append( Path.Command('G0', { 'Z': obj.SafeHeight.Value, 'F': self.vertRapid })) self.commandlist.append( Path.Command('G0', { lastAxis: 0.0, 'F': self.axialRapid })) elif angle != lastAngle: self.commandlist.append( Path.Command('G0', { 'Z': obj.SafeHeight.Value, 'F': self.vertRapid })) # Prepare for drilling cycle self.commandlist.append( Path.Command('G0', { axisOfRot: angle, 'F': self.axialRapid })) self.commandlist.append( Path.Command('G0', { 'X': p['x'], 'Y': p['y'], 'F': self.horizRapid })) self.commandlist.append( Path.Command('G1', { 'Z': p['stkTop'], 'F': self.vertFeed })) # Perform and cancel canned drilling cycle self.commandlist.append(Path.Command(cmd, params)) self.commandlist.append( Path.Command('G0', {'Z': obj.SafeHeight.Value})) # shift axis and angle values if obj.EnableRotation != 'Off': lastAxis = axisOfRot lastAngle = angle if obj.EnableRotation != 'Off': self.commandlist.append( Path.Command('G0', { 'Z': obj.SafeHeight.Value, 'F': self.vertRapid })) self.commandlist.append( Path.Command('G0', { lastAxis: 0.0, 'F': self.axialRapid }))
def opExecute(self, obj, getsim=False): # pylint: disable=arguments-differ '''opExecute(obj, getsim=False) ... implementation of Path.Area ops. determines the parameters for _buildPathArea(). Do not overwrite, implement areaOpAreaParams(obj, isHole) ... op specific area param dictionary areaOpPathParams(obj, isHole) ... op specific path param dictionary areaOpShapes(obj) ... the shape for path area to process areaOpUseProjection(obj) ... return true if operation can use projection instead.''' PathLog.track() # Instantiate class variables for operation reference self.endVector = None # pylint: disable=attribute-defined-outside-init self.rotateFlag = False # pylint: disable=attribute-defined-outside-init self.leadIn = 2.0 # pylint: disable=attribute-defined-outside-init self.cloneNames = [] # pylint: disable=attribute-defined-outside-init self.guiMsgs = [] # pylint: disable=attribute-defined-outside-init self.stockBB = PathUtils.findParentJob(obj).Stock.Shape.BoundBox # pylint: disable=attribute-defined-outside-init self.useTempJobClones( 'Delete') # Clear temporary group and recreate for temp job clones # Import OpFinalDepth from pre-existing operation for recompute() scenarios if self.defValsSet is True: PathLog.debug("self.defValsSet is True.") if self.initOpStartDepth is not None: if self.initOpStartDepth != obj.OpStartDepth.Value: obj.OpStartDepth.Value = self.initOpStartDepth obj.StartDepth.Value = self.initOpStartDepth if self.initOpFinalDepth is not None: if self.initOpFinalDepth != obj.OpFinalDepth.Value: obj.OpFinalDepth.Value = self.initOpFinalDepth obj.FinalDepth.Value = self.initOpFinalDepth self.defValsSet = False if obj.EnableRotation != 'Off': # Calculate operation heights based upon rotation radii opHeights = self.opDetermineRotationRadii(obj) (self.xRotRad, self.yRotRad, self.zRotRad) = opHeights[0] # pylint: disable=attribute-defined-outside-init (self.clrOfset, self.safOfst) = opHeights[1] # pylint: disable=attribute-defined-outside-init # Set clearnance and safe heights based upon rotation radii if obj.EnableRotation == 'A(x)': strDep = self.xRotRad elif obj.EnableRotation == 'B(y)': strDep = self.yRotRad else: strDep = max(self.xRotRad, self.yRotRad) finDep = -1 * strDep obj.ClearanceHeight.Value = strDep + self.clrOfset obj.SafeHeight.Value = strDep + self.safOfst if self.initWithRotation is False: if obj.FinalDepth.Value == obj.OpFinalDepth.Value: obj.FinalDepth.Value = finDep if obj.StartDepth.Value == obj.OpStartDepth.Value: obj.StartDepth.Value = strDep # Create visual axes when debugging. if PathLog.getLevel(PathLog.thisModule()) == 4: self.visualAxis() else: strDep = obj.StartDepth.Value finDep = obj.FinalDepth.Value # Set axial feed rates based upon horizontal feed rates safeCircum = 2 * math.pi * obj.SafeHeight.Value self.axialFeed = 360 / safeCircum * self.horizFeed # pylint: disable=attribute-defined-outside-init self.axialRapid = 360 / safeCircum * self.horizRapid # pylint: disable=attribute-defined-outside-init # Initiate depthparams and calculate operation heights for rotational operation finish_step = obj.FinishDepth.Value if hasattr(obj, "FinishDepth") else 0.0 self.depthparams = PathUtils.depth_params( # pylint: disable=attribute-defined-outside-init clearance_height=obj.ClearanceHeight.Value, safe_height=obj.SafeHeight.Value, start_depth=obj.StartDepth.Value, step_down=obj.StepDown.Value, z_finish_step=finish_step, final_depth=obj.FinalDepth.Value, user_depths=None) # Set start point if PathOp.FeatureStartPoint & self.opFeatures( obj) and obj.UseStartPoint: start = obj.StartPoint else: start = None aOS = self.areaOpShapes(obj) # pylint: disable=assignment-from-no-return # Adjust tuples length received from other PathWB tools/operations beside PathPocketShape shapes = [] for shp in aOS: if len(shp) == 2: (fc, iH) = shp # fc, iH, sub, angle, axis tup = fc, iH, 'otherOp', 0.0, 'S', obj.StartDepth.Value, obj.FinalDepth.Value shapes.append(tup) else: shapes.append(shp) if len(shapes) > 1: jobs = [{ 'x': s[0].BoundBox.XMax, 'y': s[0].BoundBox.YMax, 'shape': s } for s in shapes] jobs = PathUtils.sort_jobs(jobs, ['x', 'y']) shapes = [j['shape'] for j in jobs] # PathLog.debug("Pre_path depths are Start: {}, and Final: {}".format(obj.StartDepth.Value, obj.FinalDepth.Value)) sims = [] numShapes = len(shapes) # if numShapes == 1: # nextAxis = shapes[0][4] # elif numShapes > 1: # nextAxis = shapes[1][4] # else: # nextAxis = 'L' for ns in range(0, numShapes): (shape, isHole, sub, angle, axis, strDep, finDep) = shapes[ns] # pylint: disable=unused-variable if ns < numShapes - 1: nextAxis = shapes[ns + 1][4] else: nextAxis = 'L' finish_step = obj.FinishDepth.Value if hasattr( obj, "FinishDepth") else 0.0 self.depthparams = PathUtils.depth_params( # pylint: disable=attribute-defined-outside-init clearance_height=obj.ClearanceHeight.Value, safe_height=obj.SafeHeight.Value, start_depth=strDep, # obj.StartDepth.Value, step_down=obj.StepDown.Value, z_finish_step=finish_step, final_depth=finDep, # obj.FinalDepth.Value, user_depths=None) try: (pp, sim) = self._buildPathArea(obj, shape, isHole, start, getsim) except Exception as e: # pylint: disable=broad-except FreeCAD.Console.PrintError(e) FreeCAD.Console.PrintError( "Something unexpected happened. Check project and tool config." ) else: ppCmds = pp.Commands if obj.EnableRotation != 'Off' and self.rotateFlag is True: # Rotate model to index for cut if axis == 'X': axisOfRot = 'A' elif axis == 'Y': axisOfRot = 'B' # Reverse angle temporarily to match model. Error in FreeCAD render of B axis rotations if obj.B_AxisErrorOverride is True: angle = -1 * angle elif axis == 'Z': axisOfRot = 'C' else: axisOfRot = 'A' # Rotate Model to correct angle ppCmds.insert( 0, Path.Command('G1', { axisOfRot: angle, 'F': self.axialFeed })) ppCmds.insert(0, Path.Command('N100', {})) # Raise cutter to safe depth and return index to starting position ppCmds.append(Path.Command('N200', {})) ppCmds.append( Path.Command('G0', { 'Z': obj.SafeHeight.Value, 'F': self.vertRapid })) if axis != nextAxis: ppCmds.append( Path.Command('G0', { axisOfRot: 0.0, 'F': self.axialRapid })) # Eif # Save gcode commands to object command list self.commandlist.extend(ppCmds) sims.append(sim) # Eif if self.areaOpRetractTool(obj): self.endVector = None # pylint: disable=attribute-defined-outside-init # Raise cutter to safe height and rotate back to original orientation if self.rotateFlag is True: self.commandlist.append( Path.Command('G0', { 'Z': obj.SafeHeight.Value, 'F': self.vertRapid })) self.commandlist.append( Path.Command('G0', { 'A': 0.0, 'F': self.axialRapid })) self.commandlist.append( Path.Command('G0', { 'B': 0.0, 'F': self.axialRapid })) self.useTempJobClones( 'Delete') # Delete temp job clone group and contents self.guiMessage('title', None, show=True) # Process GUI messages to user PathLog.debug("obj.Name: " + str(obj.Name) + "\n\n") return sims
def circularHoleExecute(self, obj, holes): '''circularHoleExecute(obj, holes) ... generate drill operation for each hole in holes.''' PathLog.track() PathLog.debug("\ncircularHoleExecute() in PathDrilling.py") lastAxis = None lastAngle = 0.0 self.commandlist.append(Path.Command("(Begin Drilling)")) # rapid to clearance height self.commandlist.append(Path.Command('G0', {'Z': obj.ClearanceHeight.Value, 'F': self.vertRapid})) tiplength = 0.0 if obj.ExtraOffset == 'Drill Tip': tiplength = PathUtils.drillTipLength(self.tool) elif obj.ExtraOffset == '2x Drill Tip': tiplength = PathUtils.drillTipLength(self.tool) * 2 holes = PathUtils.sort_jobs(holes, ['x', 'y']) self.commandlist.append(Path.Command('G90')) self.commandlist.append(Path.Command(obj.ReturnLevel)) for p in holes: cmd = "G81" cmdParams = {} cmdParams['Z'] = p['trgtDep'] - tiplength cmdParams['F'] = self.vertFeed cmdParams['R'] = obj.RetractHeight.Value if obj.PeckEnabled and obj.PeckDepth.Value > 0: cmd = "G83" cmdParams['Q'] = obj.PeckDepth.Value elif obj.DwellEnabled and obj.DwellTime > 0: cmd = "G82" cmdParams['P'] = obj.DwellTime params = {} params['X'] = p['x'] params['Y'] = p['y'] if obj.EnableRotation != 'Off': angle = p['angle'] axis = p['axis'] # Rotate model to index for hole if axis == 'X': axisOfRot = 'A' elif axis == 'Y': axisOfRot = 'B' elif axis == 'Z': axisOfRot = 'C' else: axisOfRot = 'A' # Set initial values for last axis and angle if lastAxis is None: lastAxis = axisOfRot lastAngle = angle # Handle axial and angular transitions if axisOfRot != lastAxis: self.commandlist.append(Path.Command('G0', {'Z': obj.SafeHeight.Value, 'F': self.vertRapid})) self.commandlist.append(Path.Command('G0', {lastAxis: 0.0, 'F': self.axialRapid})) elif angle != lastAngle: self.commandlist.append(Path.Command('G0', {'Z': obj.SafeHeight.Value, 'F': self.vertRapid})) # Prepare for drilling cycle self.commandlist.append(Path.Command('G0', {axisOfRot: angle, 'F': self.axialRapid})) self.commandlist.append(Path.Command('G0', {'X': p['x'], 'Y': p['y'], 'F': self.horizRapid})) self.commandlist.append(Path.Command('G1', {'Z': obj.StartDepth.Value, 'F': self.vertFeed})) # Update retract height due to rotation self.opSetDefaultRetractHeight(obj) cmdParams['R'] = obj.RetractHeight.Value # Update changes to parameters params.update(cmdParams) # Perform canned drilling cycle self.commandlist.append(Path.Command(cmd, params)) # Cancel canned drilling cycle self.commandlist.append(Path.Command('G80')) self.commandlist.append(Path.Command('G0', {'Z': obj.SafeHeight.Value})) # shift axis and angle values if obj.EnableRotation != 'Off': lastAxis = axisOfRot lastAngle = angle if obj.EnableRotation != 'Off': self.commandlist.append(Path.Command('G0', {'Z': obj.SafeHeight.Value, 'F': self.vertRapid})) self.commandlist.append(Path.Command('G0', {lastAxis: 0.0, 'F': self.axialRapid}))
def execute(self, obj): from Part import Circle, Cylinder, Plane from PathScripts import PathUtils from math import sqrt output = '(helix cut operation' if obj.Comment: output += ', ' + str(obj.Comment) + ')\n' else: output += ')\n' if obj.Features: if not obj.Active: obj.Path = Path.Path("(helix cut operation inactive)") if obj.ViewObject: obj.ViewObject.Visibility = False return if not obj.ToolController: obj.ToolController = PathUtils.findToolController(obj) toolLoad = obj.ToolController if toolLoad is None or toolLoad.ToolNumber == 0: FreeCAD.Console.PrintError( "PathHelix: No tool selected for helix cut operation, insert a tool change operation first\n" ) obj.Path = Path.Path( "(ERROR: no tool selected for helix cut operation)") return tool = toolLoad.Proxy.getTool(toolLoad) zsafe = max( baseobj.Shape.BoundBox.ZMax for baseobj, features in obj.Features) + obj.Clearance.Value output += "G0 Z" + fmt(zsafe) drill_jobs = [] for base, features in obj.Features: for center, by_radius in features_by_centers(base, features).items(): radii = sorted(by_radius.keys(), reverse=True) cylinders = map( lambda radius: getattr(base.Shape, by_radius[radius]), radii) zsafe = max(cyl.BoundBox.ZMax for cyl in cylinders) + obj.Clearance.Value cur_z = cylinders[0].BoundBox.ZMax jobs = [] for cylinder in cylinders: # Find other edge of current cylinder other_edge = None for edge in cylinder.Edges: if isinstance( edge.Curve, Circle) and edge.Curve.Center.z != cur_z: other_edge = edge break next_z = other_edge.Curve.Center.z dz = next_z - cur_z r = cylinder.Surface.Radius if dz < 0: # This is a closed hole if the face connected to # the current cylinder at next_z has the cylinder's # edge as its OuterWire closed = None for face in base.Shape.Faces: if connected(other_edge, face) and not face.isSame( cylinder.Faces[0]): wire = face.OuterWire if len(wire.Edges) == 1 and wire.Edges[ 0].isSame(other_edge): closed = True else: closed = False if closed is None: raise Exception( "Cannot determine if this cylinder is closed on the z = {0} side" .format(next_z)) xc, yc, _ = cylinder.Surface.Center jobs.append( dict(xc=xc, yc=yc, zmin=next_z, zmax=cur_z, zsafe=zsafe, r_out=r, r_in=0.0, closed=closed)) elif dz > 0: new_jobs = [] for job in jobs: if job["zmin"] < next_z < job["zmax"]: # split this job job1 = dict(job) job2 = dict(job) job1["zmin"] = next_z job2["zmax"] = next_z job2["r_in"] = r new_jobs.append(job1) new_jobs.append(job2) else: new_jobs.append(job) jobs = new_jobs else: FreeCAD.Console.PrintError( "PathHelix: Encountered cylinder with zero height\n" ) break cur_z = next_z if obj.UseStartDepth: jobs = [ job for job in jobs if job["zmin"] < obj.StartDepth.Value ] if jobs: jobs[0]["zmax"] = obj.StartDepth.Value if obj.UseFinalDepth: jobs = [ job for job in jobs if job["zmax"] > obj.FinalDepth.Value ] if jobs: jobs[-1]["zmin"] = obj.FinalDepth.Value else: if not jobs[-1]["closed"]: jobs[-1]["zmin"] -= obj.ThroughDepth.Value drill_jobs.extend(jobs) if len(drill_jobs) > 0: drill_jobs = PathUtils.sort_jobs(drill_jobs, ['xc', 'yc']) for job in drill_jobs: output += helix_cut( (job["xc"], job["yc"]), job["r_out"], job["r_in"], obj.DeltaR.Value, job["zmax"], job["zmin"], obj.StepDown.Value, job["zsafe"], tool.Diameter, toolLoad.VertFeed.Value, toolLoad.HorizFeed.Value, obj.Direction, obj.StartSide) output += '\n' obj.Path = Path.Path(output) if obj.ViewObject: obj.ViewObject.Visibility = True
def execute(self, obj): PathLog.track() output = "" if obj.Comment != "": output += '(' + str(obj.Comment)+')\n' 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 if len(obj.Names) == 0: parentJob = PathUtils.findParentJob(obj) if parentJob is None: return baseobject = parentJob.Base if baseobject is None: return # Arch PanelSheet if hasattr(baseobject, "Proxy"): holes = [] if isinstance(baseobject.Proxy, ArchPanel.PanelSheet): baseobject.Proxy.execute(baseobject) i = 0 holeshapes = baseobject.Proxy.getHoles(baseobject, transform=True) tooldiameter = obj.ToolController.Proxy.getTool(obj.ToolController).Diameter for holeshape in holeshapes: PathLog.debug('Entering new HoleShape') for wire in holeshape.Wires: PathLog.debug('Entering new Wire') for edge in wire.Edges: if PathUtils.isDrillable(baseobject, edge, tooldiameter): PathLog.debug('Found drillable hole edges: {}'.format(edge)) x = edge.Curve.Center.x y = edge.Curve.Center.y diameter = edge.BoundBox.XLength holes.append({'x': x, 'y': y, 'featureName': baseobject.Name+'.'+'Drill'+str(i), 'd': diameter}) i = i + 1 else: holes = self.findHoles(obj, baseobject.Shape) for i in range(len(holes)): holes[i]['featureName'] = baseobject.Name + '.' + holes[i]['featureName'] names = [] positions = [] enabled = [] diameters = [] for h in holes: if len(names) == 0: self.findHeights(obj, baseobject, h) names.append(h['featureName']) positions.append(FreeCAD.Vector(h['x'], h['y'], 0)) enabled.append(1) diameters.append(h['d']) obj.Names = names obj.Positions = positions obj.Enabled = enabled obj.Diameters = diameters locations = [] output = "(Begin Drilling)\n" for i in range(len(obj.Names)): if obj.Enabled[i] > 0: locations.append({'x': obj.Positions[i].x, 'y': obj.Positions[i].y}) if len(locations) > 0: locations = PathUtils.sort_jobs(locations, ['x', 'y']) output += "G90 G98\n" # rapid to clearance height output += "G0 Z" + str(obj.ClearanceHeight.Value) + "F " + PathUtils.fmt(self.vertRapid) + "\n" # rapid to first hole location, with spindle still retracted: p0 = locations[0] output += "G0 X" + fmt(p0['x']) + " Y" + fmt(p0['y']) + "F " + PathUtils.fmt(self.horizRapid) + "\n" # move tool to clearance plane output += "G0 Z" + fmt(obj.ClearanceHeight.Value) + "F " + PathUtils.fmt(self.vertRapid) + "\n" pword = "" qword = "" if obj.PeckDepth.Value > 0 and obj.PeckEnabled: cmd = "G83" qword = " Q" + fmt(obj.PeckDepth.Value) elif obj.DwellTime > 0 and obj.DwellEnabled: cmd = "G82" pword = " P" + fmt(obj.DwellTime) else: cmd = "G81" for p in locations: output += cmd + \ " X" + fmt(p['x']) + \ " Y" + fmt(p['y']) + \ " Z" + fmt(obj.FinalDepth.Value) + qword + pword + \ " R" + str(obj.RetractHeight.Value) + \ " F" + str(self.vertFeed) + "\n" \ output += "G80\n" 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 execute(self, obj): from Part import Circle, Cylinder, Plane from PathScripts import PathUtils from math import sqrt output = '(helix cut operation' if obj.Comment: output += ', ' + str(obj.Comment) + ')\n' else: output += ')\n' if obj.Features: if not obj.Active: obj.Path = Path.Path("(helix cut operation inactive)") if obj.ViewObject: obj.ViewObject.Visibility = False return if not obj.ToolController: obj.ToolController = PathUtils.findToolController(obj) toolLoad = obj.ToolController if toolLoad is None or toolLoad.ToolNumber == 0: FreeCAD.Console.PrintError("PathHelix: No tool selected for helix cut operation, insert a tool change operation first\n") obj.Path = Path.Path("(ERROR: no tool selected for helix cut operation)") return tool = toolLoad.Proxy.getTool(toolLoad) zsafe = max(baseobj.Shape.BoundBox.ZMax for baseobj, features in obj.Features) + obj.Clearance.Value output += "G0 Z" + fmt(zsafe) drill_jobs = [] for base, features in obj.Features: for center, by_radius in features_by_centers(base, features).items(): radii = sorted(by_radius.keys(), reverse=True) cylinders = map(lambda radius: getattr(base.Shape, by_radius[radius]), radii) zsafe = max(cyl.BoundBox.ZMax for cyl in cylinders) + obj.Clearance.Value cur_z = cylinders[0].BoundBox.ZMax jobs = [] for cylinder in cylinders: # Find other edge of current cylinder other_edge = None for edge in cylinder.Edges: if isinstance(edge.Curve, Circle) and edge.Curve.Center.z != cur_z: other_edge = edge break next_z = other_edge.Curve.Center.z dz = next_z - cur_z r = cylinder.Surface.Radius if dz < 0: # This is a closed hole if the face connected to # the current cylinder at next_z has the cylinder's # edge as its OuterWire closed = None for face in base.Shape.Faces: if connected(other_edge, face) and not face.isSame(cylinder.Faces[0]): wire = face.OuterWire if len(wire.Edges) == 1 and wire.Edges[0].isSame(other_edge): closed = True else: closed = False if closed is None: raise Exception("Cannot determine if this cylinder is closed on the z = {0} side".format(next_z)) xc, yc, _ = cylinder.Surface.Center jobs.append(dict(xc=xc, yc=yc, zmin=next_z, zmax=cur_z, zsafe=zsafe, r_out=r, r_in=0.0, closed=closed)) elif dz > 0: new_jobs = [] for job in jobs: if job["zmin"] < next_z < job["zmax"]: # split this job job1 = dict(job) job2 = dict(job) job1["zmin"] = next_z job2["zmax"] = next_z job2["r_in"] = r new_jobs.append(job1) new_jobs.append(job2) else: new_jobs.append(job) jobs = new_jobs else: FreeCAD.Console.PrintError("PathHelix: Encountered cylinder with zero height\n") break cur_z = next_z if obj.UseStartDepth: jobs = [job for job in jobs if job["zmin"] < obj.StartDepth.Value] if jobs: jobs[0]["zmax"] = obj.StartDepth.Value if obj.UseFinalDepth: jobs = [job for job in jobs if job["zmax"] > obj.FinalDepth.Value] if jobs: jobs[-1]["zmin"] = obj.FinalDepth.Value else: if not jobs[-1]["closed"]: jobs[-1]["zmin"] -= obj.ThroughDepth.Value drill_jobs.extend(jobs) if len(drill_jobs) > 0: drill_jobs = PathUtils.sort_jobs(drill_jobs, ['xc', 'yc'], ['xc', 'zmax']) for job in drill_jobs: output += helix_cut((job["xc"], job["yc"]), job["r_out"], job["r_in"], obj.DeltaR.Value, job["zmax"], job["zmin"], obj.StepDown.Value, job["zsafe"], tool.Diameter, toolLoad.VertFeed.Value, toolLoad.HorizFeed.Value, obj.Direction, obj.StartSide) output += '\n' obj.Path = Path.Path(output) if obj.ViewObject: obj.ViewObject.Visibility = True
def opExecute(self, obj, getsim=False): '''opExecute(obj, getsim=False) ... implementation of Path.Area ops. determines the parameters for _buildPathArea(). Do not overwrite, implement areaOpAreaParams(obj, isHole) ... op specific area param dictionary areaOpPathParams(obj, isHole) ... op specific path param dictionary areaOpShapes(obj) ... the shape for path area to process areaOpUseProjection(obj) ... return true if operation can use projection instead.''' PathLog.track() self.endVector = None PathLog.debug("opExecute() in PathAreaOp.py") # Instantiate class variables for operation reference self.rotateFlag = False self.modelName = None self.leadIn = 2.0 # safOfset / 2.0 # Initialize depthparams finish_step = obj.FinishDepth.Value if hasattr(obj, "FinishDepth") else 0.0 self.depthparams = PathUtils.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=finish_step, final_depth=obj.FinalDepth.Value, user_depths=None) # Recalculate operation heights for rotational operation if obj.UseRotation != 'Off': # Calculate operation heights based upon rotation radii opHeights = self.opDetermineRotationRadii( obj ) # return is [(xRotRad, yRotRad, zRotRad), (clrOfst, safOfst)] (xRotRad, yRotRad, zRotRad) = opHeights[0] (clrOfst, safOfset) = opHeights[1] # self.leadIn = 0.0 #safOfset / 2.0 # Set clearnance and safe heights based upon rotation radii obj.ClearanceHeight.Value = xRotRad + clrOfst obj.SafeHeight.Value = xRotRad + safOfset if yRotRad > xRotRad: obj.ClearanceHeight.Value = yRotRad + clrOfst obj.SafeHeight.Value = yRotRad + safOfset # Set axial feed rates based upon horizontal feed rates safeCircum = 2 * math.pi * obj.SafeHeight.Value self.axialFeed = 360 / safeCircum * self.horizFeed self.axialRapid = 360 / safeCircum * self.horizRapid # Set start point if PathOp.FeatureStartPoint & self.opFeatures( obj) and obj.UseStartPoint: start = obj.StartPoint else: start = None aOS = self.areaOpShapes( obj) # list of tuples (shape, isHole, sub, angle, axis, tag) # Adjust tuples length received from other PathWB tools/operations beside PathPocketShape shapes = [] for shp in aOS: if len(shp) == 2: (fc, iH) = shp tup = fc, iH, 'notPocket', 0.0, 'X' shapes.append(tup) else: shapes.append(shp) if len(shapes) > 1: jobs = [{ 'x': s[0].BoundBox.XMax, 'y': s[0].BoundBox.YMax, 'shape': s } for s in shapes] jobs = PathUtils.sort_jobs(jobs, ['x', 'y']) shapes = [j['shape'] for j in jobs] sims = [] for (shape, isHole, sub, angle, axis) in shapes: startDep = obj.StartDepth.Value # + safOfset safeDep = obj.SafeHeight.Value clearDep = obj.ClearanceHeight.Value finalDep = obj.FinalDepth.Value # finDep finish_step = obj.FinishDepth.Value if hasattr( obj, "FinishDepth") else 0.0 self.depthparams = PathUtils.depth_params( clearance_height=clearDep, # obj.ClearanceHeight.Value safe_height=safeDep, # obj.SafeHeight.Value start_depth=startDep, step_down=obj.StepDown.Value, z_finish_step=finish_step, # obj.FinalDepth.Value final_depth=finalDep, user_depths=None) try: (pp, sim) = self._buildPathArea(obj, shape, isHole, start, getsim) ppCmds = pp.Commands if obj.UseRotation != 'Off' and self.rotateFlag is True: # Rotate model to index for cut axisOfRot = 'A' if axis == 'Y': axisOfRot = 'B' # Reverse angle temporarily to match model. Error in FreeCAD render of B axis rotations if obj.B_AxisErrorOverride is True: angle = -1 * angle # Rotate Model to correct angle ppCmds.insert( 0, Path.Command('G0', { axisOfRot: angle, 'F': self.axialFeed })) # Raise cutter to safe depth and return index to starting position ppCmds.append( Path.Command('G0', { 'Z': safeDep, 'F': self.vertRapid })) ppCmds.append( Path.Command('G0', { axisOfRot: 0.0, 'F': self.axialFeed })) # Save gcode commands to object command list self.commandlist.extend(ppCmds) sims.append(sim) except Exception as e: FreeCAD.Console.PrintError(e) FreeCAD.Console.PrintError( "Something unexpected happened. Check project and tool config." ) if self.areaOpRetractTool(obj): self.endVector = None # Raise cutter to safe height and rotate back to original orientation if self.rotateFlag is True: self.commandlist.append( Path.Command('G0', { 'Z': obj.SafeHeight.Value, 'F': self.vertRapid })) self.commandlist.append( Path.Command('G0', { 'A': 0.0, 'F': self.axialFeed })) self.commandlist.append( Path.Command('G0', { 'B': 0.0, 'F': self.axialFeed })) FreeCAD.ActiveDocument.getObject(self.modelName).purgeTouched() PathLog.debug("obj.Name: " + str(obj.Name)) return sims