def __init__(self, obj, viewProvider, jvoVisibility=None): self.obj = obj self.obj.Proxy.obj = obj self.viewProvider = viewProvider self.form = QtGui.QWidget() self.formTags = FreeCADGui.PySideUic.loadUi(":/panels/HoldingTagsEdit.ui") self.formPoint = FreeCADGui.PySideUic.loadUi(":/panels/PointEdit.ui") self.layout = QtGui.QVBoxLayout(self.form) #self.form.setGeometry(self.formTags.geometry()) self.form.setWindowTitle(self.formTags.windowTitle()) self.form.setSizePolicy(self.formTags.sizePolicy()) self.formTags.setParent(self.form) self.formPoint.setParent(self.form) self.layout.addWidget(self.formTags) self.layout.addWidget(self.formPoint) self.formPoint.hide() self.jvo = PathUtils.findParentJob(obj).ViewObject if jvoVisibility is None: FreeCAD.ActiveDocument.openTransaction(translate("PathDressup_HoldingTags", "Edit HoldingTags Dress-up")) self.jvoVisible = self.jvo.isVisible() if self.jvoVisible: self.jvo.hide() else: self.jvoVisible = jvoVisibility self.pt = FreeCAD.Vector(0, 0, 0) closeButton = self.formPoint.buttonBox.button(QtGui.QDialogButtonBox.StandardButton.Close) closeButton.setText(translate("PathDressup_HoldingTags", "Done")) self.isDirty = True
def adjustWirePlacement(obj, shape, wires): job = PathUtils.findParentJob(obj) if hasattr(shape, 'MapMode') and 'Deactivated' != shape.MapMode: if hasattr(shape, 'Support') and 1 == len(shape.Support) and 1 == len(shape.Support[0][1]): pmntShape = shape.Placement pmntSupport = shape.Support[0][0].getGlobalPlacement() #pmntSupport = shape.Support[0][0].Placement pmntBase = job.Base.Placement pmnt = pmntBase.multiply(pmntSupport.inverse().multiply(pmntShape)) #PathLog.debug("pmnt = %s" % pmnt) newWires = [] for w in wires: edges = [] for e in w.Edges: e = e.copy() e.Placement = FreeCAD.Placement() edges.append(e) w = Part.Wire(edges) w.Placement = pmnt newWires.append(w) wires = newWires else: PathLog.warning(translate("PathEngrave", "Attachment not supported by engraver")) else: PathLog.debug("MapMode: %s" % (shape.MapMode if hasattr(shape, 'MapMode') else 'None')) return wires
def Activated(self): FreeCAD.ActiveDocument.openTransaction( translate("Path_Post", "Post Process the Selected path(s)")) FreeCADGui.addModule("PathScripts.PathPost") # select the Path Job that you want to post output from selected = FreeCADGui.Selection.getCompleteSelection() print "in activated %s" %(selected) # try to find the job, if it's not directly selected ... jobs = set() for obj in selected: if hasattr(obj, 'OutputFile') or hasattr(obj, 'PostProcessor'): jobs.add(obj) elif hasattr(obj, 'Path') or hasattr(obj, 'ToolNumber'): job = PathUtils.findParentJob(obj) if job: jobs.add(job) fail = True rc = '' if len(jobs) != 1: FreeCAD.Console.PrintError("Please select a single job or other path object\n") else: job = jobs.pop() print("Job for selected objects = %s" % job.Name) (fail, rc) = self.exportObjectsWith(selected, job) if fail: FreeCAD.ActiveDocument.abortTransaction() else: FreeCAD.ActiveDocument.commitTransaction() FreeCAD.ActiveDocument.recompute()
def areaOpOnChanged(self, obj, prop): '''areaOpOnChanged(obj, prop) ... facing specific depths calculation.''' PathLog.track(prop) if prop == "StepOver" and obj.StepOver == 0: obj.StepOver = 1 # default depths calculation not correct for facing if prop == "Base": job = PathUtils.findParentJob(obj) obj.OpStartDepth = job.Stock.Shape.BoundBox.ZMax if len(obj.Base) >= 1: print('processing') sublist = [] for i in obj.Base: o = i[0] for s in i[1]: sublist.append(o.Shape.getElement(s)) # If the operation has a geometry identified the Finaldepth # is the top of the bboundbox which includes all features. # Otherwise, top of part. obj.OpFinalDepth = Part.makeCompound(sublist).BoundBox.ZMax else: obj.OpFinalDepth = job.Base.Shape.BoundBox.ZMax
def _OpenCloseResourceEditor(obj, vobj, edit): job = PathUtils.findParentJob(obj) if job and job.ViewObject and job.ViewObject.Proxy: if edit: job.ViewObject.Proxy.editObject(obj) else: job.ViewObject.Proxy.uneditObject(obj)
def areaOpShapes(self, obj): '''areaOpShapes(obj) ... return top face''' # 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.debug("Working on a collection of faces {}".format(faces)) # If no base object, do planing of top surface of entire model else: planeshape = self.baseobject.Shape PathLog.debug("Working on a shape {}".format(self.baseobject.Name)) # Find the correct shape depending on Boundary shape. PathLog.debug("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) elif obj.BoundaryShape == 'Stock': stock = PathUtils.findParentJob(obj).Stock.Shape env = stock else: env = PathUtils.getEnvelope(partshape=planeshape, depthparams=self.depthparams) return [(env, False)]
def addBaseGeometry(self, selection): added = False shapes = self.obj.BaseShapes for sel in selection: job = PathUtils.findParentJob(self.obj) base = job.Proxy.resourceClone(job, sel.Object) if not base: PathLog.notice((translate("Path", "%s is not a Base Model object of the job %s")+"\n") % (sel.Object.Label, job.Label)) continue if base in shapes: PathLog.notice((translate("Path", "Base shape %s already in the list")+"\n") % (sel.Object.Label)) continue if base.isDerivedFrom('Part::Part2DObject'): if sel.HasSubObjects: # selectively add some elements of the drawing to the Base for sub in sel.SubElementNames: if 'Vertex' in sub: PathLog.info(translate("Path", "Ignoring vertex")) else: self.obj.Proxy.addBase(self.obj, base, sub) else: # when adding an entire shape to BaseShapes we can take its sub shapes out of Base self.obj.Base = [(p,el) for p,el in self.obj.Base if p != base] shapes.append(base) self.obj.BaseShapes = shapes added = True else: # user wants us to engrave an edge of face of a base model base = self.super().addBaseGeometry(selection) added = added or base return added
def onDelete(self, arg1=None, arg2=None): '''this makes sure that the base operation is added back to the project and visible''' FreeCADGui.ActiveDocument.getObject(arg1.Object.Base.Name).Visibility = True job = PathUtils.findParentJob(arg1.Object) job.Proxy.addOperation(arg1.Object.Base) arg1.Object.Base = None return True
def execute(self, obj): import Part # math #DraftGeomUtils output = "" toolLoad = PathUtils.getLastToolLoad(obj) if toolLoad is None or toolLoad.ToolNumber == 0: self.vertFeed = 100 self.horizFeed = 100 self.vertRapid = 100 self.horizRapid = 100 self.radius = 0.25 obj.ToolNumber = 0 obj.ToolDescription = "UNDEFINED" else: self.vertFeed = toolLoad.VertFeed.Value self.horizFeed = toolLoad.HorizFeed.Value self.vertRapid = toolLoad.VertRapid.Value self.horizRapid = toolLoad.HorizRapid.Value tool = PathUtils.getTool(obj, toolLoad.ToolNumber) if not tool or tool.Diameter == 0: self.radius = 0.25 else: self.radius = tool.Diameter/2 obj.ToolNumber = toolLoad.ToolNumber obj.ToolDescription = toolLoad.Name self.setLabel(obj) output += "(" + obj.Label + ")" if not obj.UseComp: output += "(Compensated Tool Path. Diameter: " + str(self.radius * 2) + ")" else: output += "(Uncompensated Tool Path)" parentJob = PathUtils.findParentJob(obj) if parentJob is None: return baseobject = parentJob.Base if baseobject is None: return contourwire = TechDraw.findShapeOutline(baseobject.Shape,1, Vector(0,0,1)) edgelist = contourwire.Edges edgelist = Part.__sortEdges__(edgelist) try: output += self._buildPathLibarea(obj, edgelist) except: FreeCAD.Console.PrintError("Something unexpected happened. Unable to generate a contour path. Check project and tool config.") if obj.Active: path = Path.Path(output) obj.Path = path if obj.ViewObject: obj.ViewObject.Visibility = True else: path = Path.Path("(inactive operation)") obj.Path = path obj.ViewObject.Visibility = False
def opOnChanged(self, obj, prop): '''opOnChanged(obj, prop) ... if Locations changed, check if depths should be calculated.''' super(self.__class__, self).opOnChanged(obj, prop) if prop == 'Locations' and not 'Restore' in obj.State and obj.Locations and not obj.Base: if not hasattr(self, 'baseobject'): job = PathUtils.findParentJob(obj) if job and job.Base: self.setupDepthsFrom(obj, [], job.Base)
def opSetDefaultValues(self, obj): '''opSetDefaultValues(obj) ... set depths for engraving''' job = PathUtils.findParentJob(obj) if job and job.Base: bb = job.Base.Shape.BoundBox obj.OpStartDepth = bb.ZMax obj.OpFinalDepth = bb.ZMax - max(obj.StepDown.Value, 0.1) else: obj.OpFinalDepth = -0.1
def areaOpSetDefaultValues(self, obj): '''areaOpSetDefaultValues(obj) ... set default values''' obj.StepOver = 100 obj.ZigZagAngle = 45 job = PathUtils.findParentJob(obj) if job and job.Stock: bb = job.Stock.Shape.BoundBox obj.OpFinalDepth = bb.ZMin obj.OpStartDepth = bb.ZMax
def opExecute(self, obj): '''opExecute(obj) ... process engraving operation''' PathLog.track() output = "" if obj.Comment != "": output += '(' + str(obj.Comment)+')\n' output += "(" + obj.Label + ")" output += "(Compensated Tool Path. Diameter: " + str(obj.ToolController.Tool.Diameter) + ")" parentJob = PathUtils.findParentJob(obj) if parentJob is None: return print("base object: " + self.baseobject.Name) if obj.Algorithm in ['OCL Dropcutter', 'OCL Waterline']: try: import ocl except: FreeCAD.Console.PrintError( translate("Path_Surface", "This operation requires OpenCamLib to be installed.\n")) return if self.baseobject.TypeId.startswith('Mesh'): mesh = self.baseobject.Mesh else: # try/except is for Path Jobs created before GeometryTolerance try: deflection = parentJob.GeometryTolerance except AttributeError: from PathScripts.PathPreferences import PathPreferences deflection = PathPreferences.defaultGeometryTolerance() self.baseobject.Shape.tessellate(0.5) mesh = MeshPart.meshFromShape(self.baseobject.Shape, Deflection=deflection) bb = mesh.BoundBox s = ocl.STLSurf() for f in mesh.Facets: p = f.Points[0] q = f.Points[1] r = f.Points[2] t = ocl.Triangle(ocl.Point(p[0], p[1], p[2]), ocl.Point( q[0], q[1], q[2]), ocl.Point(r[0], r[1], r[2])) s.addTriangle(t) if obj.Algorithm == 'OCL Dropcutter': output = self._dropcutter(obj, s, bb) elif obj.Algorithm == 'OCL Waterline': output = self._waterline(obj, s, bb) self.commandlist.extend(output)
def updateDepths(self, obj, ignoreErrors=False): '''updateDepths(obj) ... base implementation calculating depths depending on base geometry. Should not be overwritten.''' def faceZmin(bb, fbb): if fbb.ZMax == fbb.ZMin and fbb.ZMax == bb.ZMax: # top face return fbb.ZMin elif fbb.ZMax > fbb.ZMin and fbb.ZMax == bb.ZMax: # vertical face, full cut return fbb.ZMin elif fbb.ZMax > fbb.ZMin and fbb.ZMin > bb.ZMin: # internal vertical wall return fbb.ZMin elif fbb.ZMax == fbb.ZMin and fbb.ZMax > bb.ZMin: # face/shelf return fbb.ZMin return bb.ZMin if not self._setBaseAndStock(obj, ignoreErrors): return False stockBB = self.stock.Shape.BoundBox zmin = stockBB.ZMin zmax = stockBB.ZMax if hasattr(obj, 'Base') and obj.Base: for base, sublist in obj.Base: bb = base.Shape.BoundBox zmax = max(zmax, bb.ZMax) for sub in sublist: fbb = base.Shape.getElement(sub).BoundBox zmin = max(zmin, faceZmin(bb, fbb)) zmax = max(zmax, fbb.ZMax) else: # clearing with stock boundaries job = PathUtils.findParentJob(obj) zmax = stockBB.ZMax zmin = job.Base.Shape.BoundBox.ZMax if FeatureDepths & self.opFeatures(obj): # first set update final depth, it's value is not negotiable if not PathGeom.isRoughly(obj.OpFinalDepth.Value, zmin): obj.OpFinalDepth = zmin zmin = obj.OpFinalDepth.Value def minZmax(z): if hasattr(obj, 'StepDown') and not PathGeom.isRoughly(obj.StepDown.Value, 0): return z + obj.StepDown.Value else: return z + 1 # ensure zmax is higher than zmin if (zmax - 0.0001) <= zmin: zmax = minZmax(zmin) # update start depth if requested and required if not PathGeom.isRoughly(obj.OpStartDepth.Value, zmax): obj.OpStartDepth = zmax
def areaOpSetDefaultValues(self, obj): '''areaOpSetDefaultValues(obj) ... initialize mill facing properties''' # obj.StepOver = 50 # obj.ZigZagAngle = 45.0 # need to overwrite the default depth calculations for facing job = PathUtils.findParentJob(obj) if job and job.Base: d = PathUtils.guessDepths(job.Base.Shape, None) obj.OpStartDepth = d.safe_height obj.OpFinalDepth = d.start_depth
def areaOpShapeForDepths(self, obj): '''areaOpShapeForDepths(obj) ... returns the shape used to make an initial calculation for the depths being used. The default implementation returns the job's Base.Shape''' job = PathUtils.findParentJob(obj) if job and job.Base: PathLog.debug("job=%s base=%s shape=%s" % (job, job.Base, job.Base.Shape)) return job.Base.Shape if job: PathLog.warning(translate("PathAreaOp", "job %s has no Base.") % job.Label) else: PathLog.warning(translate("PathAreaOp", "no job for op %s found.") % obj.Label) return None
def opSetDefaultValues(self, obj): '''opSetDefaultValues(obj) ... set depths for engraving''' job = PathUtils.findParentJob(obj) if job and job.Base: bb = job.Base.Shape.BoundBox obj.OpStartDepth = bb.ZMax obj.OpFinalDepth = bb.ZMin else: obj.OpFinalDepth = -0.1 if obj.OpStartDepth.Value != obj.OpFinalDepth.Value: # maintain behaviour of a single run obj.OpStepDown = obj.OpStartDepth.Value - obj.OpFinalDepth.Value
def opSetDefaultValues(self, obj, job): '''opSetDefaultValues(obj, job) ... initialize defaults''' # obj.ZigZagAngle = 45.0 obj.StepOver = 50 obj.Optimize = True # need to overwrite the default depth calculations for facing job = PathUtils.findParentJob(obj) if job and job.Stock: d = PathUtils.guessDepths(job.Stock.Shape, None) obj.OpStartDepth = d.start_depth obj.OpFinalDepth = d.final_depth
def onDelete(self, arg1=None, arg2=None): PathLog.track() '''this makes sure that the base operation is added back to the job and visible''' if self.obj.Base.ViewObject: self.obj.Base.ViewObject.Visibility = True job = PathUtils.findParentJob(self.obj) job.Proxy.addOperation(arg1.Object.Base) arg1.Object.Base = None # if self.debugDisplay(): # self.vobj.Debug.removeObjectsFromDocument() # self.vobj.Debug.Document.removeObject(self.vobj.Debug.Name) # self.vobj.Debug = None return True
def _setBaseAndStock(self, obj, ignoreErrors=False): job = PathUtils.findParentJob(obj) if not job: if not ignoreErrors: PathLog.error(translate("Path", "No parent job found for operation.")) return False if not job.Model.Group: if not ignoreErrors: PathLog.error(translate("Path", "Parent job %s doesn't have a base object") % job.Label) return False self.job = job self.model = job.Model.Group self.stock = job.Stock return True
def opOnChanged(self, obj, prop): '''opOnChange(obj, prop) ... implements depth calculation if Base changes. Do not overwrite.''' if 'Base' == prop and not 'Restore' in obj.State and obj.Base: features = [] for base, subs in obj.Base: for sub in subs: features.append((base, sub)) job = PathUtils.findParentJob(obj) if not job or not job.Base: return self.setupDepthsFrom(obj, features, job.Base)
def opSetDefaultValues(self, obj, job): '''opSetDefaultValues(obj, job) ... set default value for RetractHeight''' obj.RetractHeight = 10.0 obj.ReverseDirection = False obj.InverseAngle = False obj.B_AxisErrorOverride = False obj.AttemptInverseAngle = False # Initial setting for EnableRotation is taken from Job SetupSheet # User may override on per-operation basis as needed. parentJob = PathUtils.findParentJob(obj) if hasattr(parentJob.SetupSheet, 'SetupEnableRotation'): obj.EnableRotation = parentJob.SetupSheet.SetupEnableRotation else: obj.EnableRotation = 'Off'
def areaOpShapeForDepths(self, obj): '''areaOpShapeForDepths(obj) ... returns the shape used to make an initial calculation for the depths being used. The default implementation returns the job's Base.Shape''' job = PathUtils.findParentJob(obj) if job and job.Base: PathLog.debug("job=%s base=%s shape=%s" % (job, job.Base, job.Base.Shape)) return job.Base.Shape if job: PathLog.warning( translate("PathAreaOp", "job %s has no Base.") % job.Label) else: PathLog.warning( translate("PathAreaOp", "no job for op %s found.") % obj.Label) return None
def Create(base, name='DogboneDressup'): ''' Create(obj, name='DogboneDressup') ... dresses the given PathProfile/PathContour object with dogbones. ''' obj = FreeCAD.ActiveDocument.addObject('Path::FeaturePython', name) dbo = ObjectDressup(obj, base) job = PathUtils.findParentJob(base) job.Proxy.addOperation(obj, base) if FreeCAD.GuiUp: obj.ViewObject.Proxy = ViewProviderDressup(obj.ViewObject) obj.Base.ViewObject.Visibility = False dbo.setup(obj, True) return obj
def execute(self, obj): fixlist = ['G53', 'G54', 'G55', 'G56', 'G57', 'G58', 'G59', 'G59.1', 'G59.2', 'G59.3', 'G59.4', 'G59.5', 'G59.6', 'G59.7', 'G59.8', 'G59.9'] fixture = fixlist.index(obj.Fixture) obj.Path = Path.Path(str(obj.Fixture)) obj.Label = "Fixture" + str(fixture) if obj.Active: job = PathUtils.findParentJob(obj) c1 = Path.Command(str(obj.Fixture)) c2 = Path.Command("G0" + str(job.Stock.Shape.BoundBox.ZMax)) obj.Path = Path.Path([c1, c2]) obj.ViewObject.Visibility = True else: obj.Path = Path.Path("(inactive operation)") obj.ViewObject.Visibility = False
def onDelete(self, arg1=None, arg2=None): '''this makes sure that the base operation is added back to the job and visible''' # pylint: disable=unused-argument PathLog.track() if self.obj.Base.ViewObject: self.obj.Base.ViewObject.Visibility = True job = PathUtils.findParentJob(self.obj) if arg1.Object and arg1.Object.Base and job: job.Proxy.addOperation(arg1.Object.Base, arg1.Object) arg1.Object.Base = None # if self.debugDisplay(): # self.vobj.Debug.removeObjectsFromDocument() # self.vobj.Debug.Document.removeObject(self.vobj.Debug.Name) # self.vobj.Debug = None return True
def Create(base, name='DogboneDressup'): ''' Create(obj, name='DogboneDressup') ... dresses the given PathProfile/PathContour object with dogbones. ''' obj = FreeCAD.ActiveDocument.addObject('Path::FeaturePython', 'DogboneDressup') dbo = ObjectDressup(obj, base) job = PathUtils.findParentJob(base) job.Proxy.addOperation(obj) if FreeCAD.GuiUp: ViewProviderDressup(obj.ViewObject) obj.Base.ViewObject.Visibility = False dbo.setup(obj, True) return obj
def process_looped_sublist(self, obj, norm, surf): '''process_looped_sublist(obj, norm, surf)... Process set of looped faces when rotation is enabled. ''' PathLog.debug(translate("Path", "Selected faces form loop. Processing looped faces.")) rtn = False (rtn, angle, axis, praInfo) = self.faceRotationAnalysis(obj, norm, surf) # pylint: disable=unused-variable if rtn is True: faceNums = "" for f in subsList: faceNums += '_' + f.replace('Face', '') (clnBase, angle, clnStock, tag) = self.applyRotationalAnalysis(obj, base, angle, axis, faceNums) # pylint: disable=unused-variable # Verify faces are correctly oriented - InverseAngle might be necessary PathLog.debug("Checking if faces are oriented correctly after rotation.") for sub in subsList: face = clnBase.Shape.getElement(sub) if type(face.Surface) == Part.Plane: if not PathGeom.isHorizontal(face.Surface.Axis): rtn = False PathLog.warning(translate("PathPocketShape", "Face appears to NOT be horizontal AFTER rotation applied.")) break if rtn is False: PathLog.debug(translate("Path", "Face appears misaligned after initial rotation.") + ' 1') if obj.InverseAngle: (clnBase, clnStock, angle) = self.applyInverseAngle(obj, clnBase, clnStock, axis, angle) else: if obj.AttemptInverseAngle is True: (clnBase, clnStock, angle) = self.applyInverseAngle(obj, clnBase, clnStock, axis, angle) else: msg = translate("Path", "Consider toggling the 'InverseAngle' property and recomputing.") PathLog.warning(msg) if angle < 0.0: angle += 360.0 tup = clnBase, subsList, angle, axis, clnStock else: if self.warnDisabledAxis(obj, axis) is False: PathLog.debug("No rotation used") axis = 'X' angle = 0.0 stock = PathUtils.findParentJob(obj).Stock tup = base, subsList, angle, axis, stock # Eif return tup
def areaOpSetDefaultValues(self, obj): '''areaOpSetDefaultValues(obj) ... initialize mill facing properties''' obj.StepOver = 50 obj.ZigZagAngle = 45.0 job = PathUtils.findParentJob(obj) # need to overwrite the default depth calculations for facing if job and job.Base: obj.OpStartDepth = job.Stock.Shape.BoundBox.ZMax obj.OpFinalDepth = job.Base.Shape.BoundBox.ZMax # If the operation has a geometry identified the Finaldepth # is the top of the bboundbox which includes all features. if len(obj.Base) >= 1: obj.OpFinalDepth = Part.makeCompound(obj.Base).BoundBox.ZMax
def areaOpSetDefaultValues(self, obj): '''areaOpSetDefaultValues(obj) ... initialize mill facing properties''' obj.StepOver = 50 obj.ZigZagAngle = 45.0 job = PathUtils.findParentJob(obj) # need to overwrite the default depth calculations for facing if job and job.Base: obj.OpStartDepth = job.Stock.Shape.BoundBox.ZMax obj.OpFinalDepth = job.Base.Shape.BoundBox.ZMax # If the operation has a geometry identified the Finaldepth # is the top of the bboundbox which includes all features. if len(obj.Base) >= 1: obj.OpFinalDepth = Part.makeCompound(obj.Base).BoundBox.ZMax
def addFacebase(self, obj, ss, sub=""): baselist = obj.Base if baselist is None: baselist = [] if len(baselist) == 0: # When adding the first base object, guess at heights self._guessDepths(obj, ss, sub) item = (ss, sub) if item in baselist: FreeCAD.Console.PrintWarning(translate("Path", "this object already in the list" + "\n")) elif PathUtils.findParentJob(obj).Base.Name != ss.Name: FreeCAD.Console.PrintWarning(translate("Path", "Please select features from the Job model object" + "\n")) else: baselist.append(item) obj.Base = baselist self.execute(obj)
def Create(base, name='DogboneDressup'): ''' Create(obj, name='DogboneDressup') ... dresses the given PathProfile/PathContour object with dogbones. ''' obj = FreeCAD.ActiveDocument.addObject('Path::FeaturePython', 'DogboneDressup') dbo = ObjectDressup(obj, base) job = PathUtils.findParentJob(base) PathUtils.addObjectToJob(obj, job) if FreeCAD.GuiUp: ViewProviderDressup(obj.ViewObject) obj.Base.ViewObject.Visibility = False dbo.setup(obj, True) obj.ToolController = base.ToolController return obj
def _setBaseAndStock(self, obj, ignoreErrors=False): job = PathUtils.findParentJob(obj) if not job: if not ignoreErrors: PathLog.error(translate("Path", "No parent job found for operation.")) return False if not job.Model.Group: if not ignoreErrors: PathLog.error( translate("Path", "Parent job %s doesn't have a base object") % job.Label ) return False self.job = job self.model = job.Model.Group self.stock = job.Stock return True
def __init__(self, obj, viewProvider, jvoVisibility=None): self.obj = obj self.obj.Proxy.obj = obj self.viewProvider = viewProvider self.form = FreeCADGui.PySideUic.loadUi(":/panels/HoldingTagsEdit.ui") self.getPoint = PathGetPoint.TaskPanel(self.form.removeEditAddGroup, True) self.jvo = PathUtils.findParentJob(obj).ViewObject if jvoVisibility is None: FreeCAD.ActiveDocument.openTransaction(translate("PathDressup_HoldingTags", "Edit HoldingTags Dress-up")) self.jvoVisible = self.jvo.isVisible() if self.jvoVisible: self.jvo.hide() else: self.jvoVisible = jvoVisibility self.pt = FreeCAD.Vector(0, 0, 0) self.isDirty = True
def Activated(self): FreeCAD.ActiveDocument.openTransaction( translate("Path_Post", "Post Process the Selected path(s)")) FreeCADGui.addModule("PathScripts.PathPost") # select the Path Job that you want to post output from selected = FreeCADGui.Selection.getCompleteSelection() print "in activated %s" % (selected) # try to find the job, if it's not directly selected ... jobs = set() for obj in selected: if hasattr(obj, 'OutputFile') or hasattr(obj, 'PostProcessor'): jobs.add(obj) elif hasattr(obj, 'Path') or hasattr(obj, 'ToolNumber'): job = PathUtils.findParentJob(obj) if job: jobs.add(job) if len(jobs) != 1: FreeCAD.Console.PrintError( "Please select a single job or other path object\n") FreeCAD.ActiveDocument.abortTransaction() else: job = jobs.pop() print("Job for selected objects = %s" % job.Name) # check if the user has a project and has set the default post and # output filename postArgs = PathPreferences.defaultPostProcessorArgs() if hasattr(job, "PostProcessorArgs") and job.PostProcessorArgs: postArgs = job.PostProcessorArgs elif hasattr(job, "PostProcessor") and job.PostProcessor: postArgs = '' postname = self.resolvePostProcessor(job) if postname: filename = self.resolveFileName(job) if postname and filename: print("post: %s(%s, %s)" % (postname, filename, postArgs)) processor = PostProcessor.load(postname) processor.export(selected, filename, postArgs) FreeCAD.ActiveDocument.commitTransaction() else: FreeCAD.ActiveDocument.abortTransaction() FreeCAD.ActiveDocument.recompute()
def execute(self, obj): fixlist = [ 'G53', 'G54', 'G55', 'G56', 'G57', 'G58', 'G59', 'G59.1', 'G59.2', 'G59.3', 'G59.4', 'G59.5', 'G59.6', 'G59.7', 'G59.8', 'G59.9' ] fixture = fixlist.index(obj.Fixture) obj.Path = Path.Path(str(obj.Fixture)) obj.Label = "Fixture" + str(fixture) if obj.Active: job = PathUtils.findParentJob(obj) c1 = Path.Command(str(obj.Fixture)) c2 = Path.Command("G0" + str(job.Stock.Shape.BoundBox.ZMax)) obj.Path = Path.Path([c1, c2]) obj.ViewObject.Visibility = True else: obj.Path = Path.Path("(inactive operation)") obj.ViewObject.Visibility = False
def Activated(self): FreeCAD.ActiveDocument.openTransaction( translate("Path_Post", "Post Process the Selected path(s)")) FreeCADGui.addModule("PathScripts.PathPost") # select the Path Job that you want to post output from selected = FreeCADGui.Selection.getCompleteSelection() print "in activated %s" %(selected) # try to find the job, if it's not directly selected ... jobs = set() for obj in selected: if hasattr(obj, 'OutputFile') or hasattr(obj, 'PostProcessor'): jobs.add(obj) elif hasattr(obj, 'Path') or hasattr(obj, 'ToolNumber'): job = PathUtils.findParentJob(obj) if job: jobs.add(job) if len(jobs) != 1: FreeCAD.Console.PrintError("Please select a single job or other path object\n") FreeCAD.ActiveDocument.abortTransaction() else: job = jobs.pop() print("Job for selected objects = %s" % job.Name) # check if the user has a project and has set the default post and # output filename postArgs = PathPreferences.defaultPostProcessorArgs() if hasattr(job, "PostProcessorArgs") and job.PostProcessorArgs: postArgs = job.PostProcessorArgs elif hasattr(job, "PostProcessor") and job.PostProcessor: postArgs = '' postname = self.resolvePostProcessor(job) if postname: filename = self.resolveFileName(job) if postname and filename: print("post: %s(%s, %s)" % (postname, filename, postArgs)) processor = PostProcessor.load(postname) processor.export(selected, filename, postArgs) FreeCAD.ActiveDocument.commitTransaction() else: FreeCAD.ActiveDocument.abortTransaction() FreeCAD.ActiveDocument.recompute()
def get_stock_silhoutte(self, obj): ''' Get Stock Silhoutte ''' stockBB = self.stock.Shape.BoundBox stock_z_pos = stockBB.ZMax parentJob = PathUtils.findParentJob(obj) self.startOffset = obj.StartDepth.Value - stockBB.ZMax + parentJob.SetupSheet.SafeHeightOffset.Value self.endOffset = stockBB.ZMin - obj.FinalDepth.Value stock_plane_length = obj.StartDepth.Value - obj.FinalDepth.Value stock_plane_width = stockBB.XLength / 2 stock_plane = Part.makePlane( stock_plane_length, stock_plane_width, FreeCAD.Vector(-stock_plane_width, 0, stock_z_pos), FreeCAD.Vector(0, -1, 0)) return stock_plane
def setDepths(proxy, obj): parentJob = PathUtils.findParentJob(obj) if parentJob is None: return baseobject = parentJob.Base if baseobject is None: return try: bb = baseobject.Shape.BoundBox # parent boundbox obj.StartDepth = bb.ZMax obj.ClearanceHeight = bb.ZMax + 5.0 obj.SafeHeight = bb.ZMax + 3.0 except: obj.StartDepth = 5.0 obj.ClearanceHeight = 10.0 obj.SafeHeight = 8.0
def setDepths(proxy, obj): parentJob = PathUtils.findParentJob(obj) if parentJob is None: return baseobject = parentJob.Base if baseobject is None: return try: bb = baseobject.Shape.BoundBox # parent boundbox obj.StartDepth = bb.ZMax obj.ClearanceHeight = bb.ZMax + 5.0 obj.SafeHeight = bb.ZMax + 3.0 except: obj.StartDepth = 5.0 obj.ClearanceHeight = 10.0 obj.SafeHeight = 8.0
def Create(baseObject, name='DressupTag'): ''' Create(basePath, name='DressupTag') ... create tag dressup object for the given base path. ''' if not baseObject.isDerivedFrom('Path::Feature'): PathLog.error(translate('Path_DressupTag', 'The selected object is not a path')+'\n') return None if baseObject.isDerivedFrom('Path::FeatureCompoundPython'): PathLog.error(translate('Path_DressupTag', 'Please select a Profile object')) return None obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", "TagDressup") dbo = ObjectTagDressup(obj, baseObject) job = PathUtils.findParentJob(baseObject) job.Proxy.addOperation(obj) dbo.setup(obj, True) return obj
def Create(baseObject, name='DressupTag'): ''' Create(basePath, name='DressupTag') ... create tag dressup object for the given base path. ''' if not baseObject.isDerivedFrom('Path::Feature'): PathLog.error(translate('Path_DressupTag', 'The selected object is not a path')+'\n') return None if baseObject.isDerivedFrom('Path::FeatureCompoundPython'): PathLog.error(translate('Path_DressupTag', 'Please select a Profile object')) return None obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", "TagDressup") dbo = ObjectTagDressup(obj, baseObject) job = PathUtils.findParentJob(baseObject) job.Proxy.addOperation(obj, baseObject) dbo.setup(obj, True) return obj
def getProps(self, obj): # TODO: use the start and final depths # print('getProps - Start Depth: ', obj.OpStartDepth.Value, 'Final Depth: ', obj.OpFinalDepth.Value) parentJob = PathUtils.findParentJob(obj) props = {} props['min_dia'] = self.minDia props['extra_dia'] = self.maxDia - self.stock.Shape.BoundBox.XLength props['start_offset'] = self.startOffset props['end_offset'] = self.endOffset props['allow_grooving'] = self.allowGrooving props['step_over'] = self.stepOver props['finish_passes'] = self.finishPasses props['stock_to_leave'] = self.stockToLeave props['hfeed'] = obj.ToolController.HorizFeed.Value props['vfeed'] = obj.ToolController.VertFeed.Value props['clearance'] = parentJob.SetupSheet.SafeHeightOffset.Value return props
def opDetermineRotationRadii(self, obj): '''opDetermineRotationRadii(obj) Determine rotational radii for 4th-axis rotations, for clearance/safe heights ''' parentJob = PathUtils.findParentJob(obj) # bb = parentJob.Stock.Shape.BoundBox xlim = 0.0 ylim = 0.0 zlim = 0.0 xRotRad = 0.01 yRotRad = 0.01 xRotRad = 0.01 # Determine boundbox radius based upon xzy limits data if math.fabs(self.stockBB.ZMin) > math.fabs(self.stockBB.ZMax): zlim = self.stockBB.ZMin else: zlim = self.stockBB.ZMax if obj.EnableRotation != 'B(y)': # Rotation is around X-axis, cutter moves along same axis if math.fabs(self.stockBB.YMin) > math.fabs(self.stockBB.YMax): ylim = self.stockBB.YMin else: ylim = self.stockBB.YMax if obj.EnableRotation != 'A(x)': # Rotation is around Y-axis, cutter moves along same axis if math.fabs(self.stockBB.XMin) > math.fabs(self.stockBB.XMax): xlim = self.stockBB.XMin else: xlim = self.stockBB.XMax if ylim != 0.0: xRotRad = math.sqrt(ylim**2 + zlim**2) if xlim != 0.0: yRotRad = math.sqrt(xlim**2 + zlim**2) zRotRad = math.sqrt(xlim**2 + ylim**2) clrOfst = parentJob.SetupSheet.ClearanceHeightOffset.Value safOfst = parentJob.SetupSheet.SafeHeightOffset.Value return [(xRotRad, yRotRad, zRotRad), (clrOfst, safOfst)]
def opExecute(self, obj): '''opExecute(obj) ... generate probe locations.''' PathLog.track() self.commandlist.append(Path.Command("(Begin Probing)")) stock = PathUtils.findParentJob(obj).Stock bb = stock.Shape.BoundBox openstring = '(PROBEOPEN {})'.format(obj.OutputFileName) self.commandlist.append(Path.Command(openstring)) self.commandlist.append(Path.Command("G0", {"Z": obj.ClearanceHeight.Value})) for y in self.nextpoint(bb.YMin, bb.YMax, obj.PointCountY): for x in self.nextpoint(bb.XMin, bb.XMax, obj.PointCountX): self.commandlist.append(Path.Command("G0", {"X": x + obj.Xoffset.Value, "Y": y + obj.Yoffset.Value, "Z": obj.SafeHeight.Value})) self.commandlist.append(Path.Command("G38.2", {"Z": obj.FinalDepth.Value, "F": obj.ToolController.VertFeed.Value})) self.commandlist.append(Path.Command("G0", {"Z": obj.SafeHeight.Value})) self.commandlist.append(Path.Command("(PROBECLOSE)"))
def Create(baseObject, name="DressupTag"): """ Create(basePath, name = 'DressupTag') ... create tag dressup object for the given base path. """ if not baseObject.isDerivedFrom("Path::Feature"): PathLog.error( translate("Path_DressupTag", "The selected object is not a path") + "\n") return None if baseObject.isDerivedFrom("Path::FeatureCompoundPython"): PathLog.error( translate("Path_DressupTag", "Please select a Profile object")) return None obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", name) dbo = ObjectDressup(obj, baseObject) job = PathUtils.findParentJob(baseObject) job.addOperation(obj) dbo.assignDefaultValues() return obj
def Create(baseObject, name='DressupTag'): ''' Create(basePath, name = 'DressupTag') ... create tag dressup object for the given base path. ''' if not baseObject.isDerivedFrom('Path::Feature'): PathLog.error( translate('PathDressup_Tag', 'The selected object is not a path\n')) return None if baseObject.isDerivedFrom('Path::FeatureCompoundPython'): PathLog.error( translate('PathDressup_Tag', 'Please select a Profile object')) return None obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", "TagDressup") dbo = ObjectDressup(obj, baseObject) job = PathUtils.findParentJob(baseObject) PathUtils.addObjectToJob(obj, job) dbo.assignDefaultValues() return obj
def _extractFaceOffset(self, obj, fcShape, isHole): '''_extractFaceOffset(obj, fcShape, isHole) ... internal function. Original _buildPathArea() version copied from PathAreaOp.py module. This version is modified. Adjustments made based on notes by @sliptonic - https://github.com/sliptonic/FreeCAD/wiki/PathArea-notes.''' PathLog.debug('_extractFaceOffset()') areaParams = {} JOB = PathUtils.findParentJob(obj) tolrnc = JOB.GeometryTolerance.Value if self.useComp is True: offset = self.ofstRadius # + tolrnc else: offset = self.offsetExtra # + tolrnc if isHole is False: offset = 0 - offset areaParams['Offset'] = offset areaParams['Fill'] = 1 areaParams['Coplanar'] = 0 areaParams[ 'SectionCount'] = 1 # -1 = full(all per depthparams??) sections areaParams['Reorient'] = True areaParams['OpenMode'] = 0 areaParams['MaxArcPoints'] = 400 # 400 areaParams['Project'] = True # areaParams['JoinType'] = 1 area = Path.Area() # Create instance of Area() class object area.setPlane(PathUtils.makeWorkplane(fcShape)) # Set working plane area.add(fcShape) # obj.Shape to use for extracting offset area.setParams(**areaParams) # set parameters # Save parameters for debugging # obj.AreaParams = str(area.getParams()) # PathLog.debug("Area with params: {}".format(area.getParams())) offsetShape = area.getShape() return offsetShape
def addBaseGeometry(self, selection): PathLog.track(selection) added = False shapes = self.obj.BaseShapes for sel in selection: job = PathUtils.findParentJob(self.obj) base = job.Proxy.resourceClone(job, sel.Object) if not base: PathLog.notice((translate( "Path", "%s is not a Base Model object of the job %s") + "\n") % (sel.Object.Label, job.Label)) continue if base in shapes: PathLog.notice( (translate("Path", "Base shape %s already in the list") + "\n") % (sel.Object.Label)) continue if base.isDerivedFrom('Part::Part2DObject'): if sel.HasSubObjects: # selectively add some elements of the drawing to the Base for sub in sel.SubElementNames: if 'Vertex' in sub: PathLog.info(translate("Path", "Ignoring vertex")) else: self.obj.Proxy.addBase(self.obj, base, sub) else: # when adding an entire shape to BaseShapes we can take its sub shapes out of Base self.obj.Base = [(p, el) for p, el in self.obj.Base if p != base] shapes.append(base) self.obj.BaseShapes = shapes added = True if not added: # user wants us to engrave an edge of face of a base model PathLog.info(" call default") base = self.super().addBaseGeometry(selection) added = added or base return added
def areaOpShapes(self, obj): '''areaOpShapes(obj) ... return top face''' # 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.debug("Working on a collection of faces {}".format(faces)) # If no base object, do planing of top surface of entire model else: planeshape = self.baseobject.Shape PathLog.debug("Working on a shape {}".format(self.baseobject.Name)) # Find the correct shape depending on Boundary shape. PathLog.debug("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) elif obj.BoundaryShape == 'Stock': stock = PathUtils.findParentJob(obj).Stock.Shape env = stock else: env = PathUtils.getEnvelope(partshape=planeshape, depthparams=self.depthparams) return [(env, False)]
def create(baseFace): model = baseFace[0] job = PathUtils.findParentJob(model) doc = model.Document helperGrpName = job.Name + '_HelperGeometry' helperGrp = doc.getObject(helperGrpName) if not helperGrp: helperGrp = doc.addObject("App::DocumentObjectGroup", helperGrpName) objName = model.Name faceName = baseFace[1] modelFaceName = objName + '.' + faceName helperFaceName = modelFaceName + '_Helper' obj = helperGrp.newObject('Part::FeaturePython', helperFaceName) HelperFace(obj, baseFace) ViewProviderHelperFace(obj.ViewObject) FreeCAD.ActiveDocument.recompute() return obj
def cloneBaseAndStock(self, obj, base, angle, axis, subCount): '''cloneBaseAndStock(obj, base, angle, axis, subCount) Method called to create a temporary clone of the base and parent Job stock. Clones are destroyed after usage for calculations related to rotational operations.''' # Create a temporary clone and stock of model for rotational use. rndAng = round(angle, 8) if rndAng < 0.0: # neg sign is converted to underscore in clone name creation. tag = axis + '_' + axis + '_' + str(math.fabs(rndAng)).replace( '.', '_') else: tag = axis + str(rndAng).replace('.', '_') clnNm = obj.Name + '_base_' + '_' + str(subCount) + '_' + tag stckClnNm = obj.Name + '_stock_' + '_' + str(subCount) + '_' + tag if clnNm not in self.cloneNames: self.cloneNames.append(clnNm) self.cloneNames.append(stckClnNm) if FreeCAD.ActiveDocument.getObject(clnNm): FreeCAD.ActiveDocument.removeObject(clnNm) if FreeCAD.ActiveDocument.getObject(stckClnNm): FreeCAD.ActiveDocument.removeObject(stckClnNm) FreeCAD.ActiveDocument.addObject('Part::Feature', clnNm).Shape = base.Shape FreeCAD.ActiveDocument.addObject( 'Part::Feature', stckClnNm).Shape = PathUtils.findParentJob(obj).Stock.Shape if FreeCAD.GuiUp: FreeCADGui.ActiveDocument.getObject( stckClnNm).Transparency = 90 FreeCADGui.ActiveDocument.getObject(clnNm).ShapeColor = (1.000, 0.667, 0.000) self.useTempJobClones(clnNm) self.useTempJobClones(stckClnNm) clnBase = FreeCAD.ActiveDocument.getObject(clnNm) clnStock = FreeCAD.ActiveDocument.getObject(stckClnNm) tag = base.Name + '_' + tag return (clnBase, clnStock, tag)
def mkHeader(selection): job = PathUtils.findParentJob(selection[0]) # this is within a function, because otherwise filename and time don't change when changing the FreeCAD project # now = datetime.datetime.now() now = time.strftime("%Y-%m-%d %H:%M") originfile = FreeCAD.ActiveDocument.FileName headerNoNumber = "%PM\n" # this line gets no linenumber if hasattr(job, "Description"): description = job.Description else: description = "" # this line gets no linenumber, it is already a specially numbered headerNoNumber += "N9XXX (" + description + ", " + now + ")\n" header = "" # header += "(Output Time:" + str(now) + ")\n" header += "(" + originfile + ")\n" # header += "(Exported by FreeCAD)\n" header += "(Post Processor: " + __name__ + ")\n" # header += "(Target machine: " + MACHINE_NAME + ")\n" header += "G18\n" # Select XY plane header += "G90\n" # Absolute coordinates header += "G51\n" # Reset Zero header += "G52 (ersetze G55-G59)" # set zero return headerNoNumber + linenumberify(header)
def mkHeader(selection): job = PathUtils.findParentJob(selection[0]) # this is within a function, because otherwise filename and time don't change when changing the FreeCAD project # now = datetime.datetime.now() now = time.strftime("%Y-%m-%d %H:%M") originfile = FreeCAD.ActiveDocument.FileName headerNoNumber = "%PM\n" # this line gets no linenumber if hasattr(job, "Description"): description = job.Description else: description = "" # this line gets no linenumber, it is already a specially numbered headerNoNumber += "N9XXX (" + description + ", " + now + ")\n" header = "" # header += "(Output Time:" + str(now) + ")\n" header += "(" + originfile + ")\n" # header += "(Exported by FreeCAD)\n" header += "(Post Processor: " + __name__ + ")\n" # header += "(Target machine: " + MACHINE_NAME + ")\n" header += "G18\n" # Select XY plane header += "G90\n" # Absolute coordinates header += "G51\n" # Reset Zero header += "G52 (ersetze G55-G59)" # set zero return headerNoNumber + linenumberify(header)
def areaOpShapes(self, obj): '''areaOpShapes(obj) ... returns envelope for all base shapes or wires for Arch.Panels.''' PathLog.track() PathLog.debug("----- areaOpShapes() in PathProfileFaces.py") if obj.UseComp: self.commandlist.append( Path.Command("(Compensated Tool Path. Diameter: " + str(self.radius * 2) + ")")) else: self.commandlist.append(Path.Command("(Uncompensated Tool Path)")) shapes = [] self.profileshape = [] finalDepths = [] baseSubsTuples = [] subCount = 0 allTuples = [] if obj.Base: # The user has selected subobjects from the base. Process each. if obj.EnableRotation != 'Off': for p in range(0, len(obj.Base)): (base, subsList) = obj.Base[p] for sub in subsList: subCount += 1 shape = getattr(base.Shape, sub) if isinstance(shape, Part.Face): rtn = False (norm, surf) = self.getFaceNormAndSurf(shape) (rtn, angle, axis, praInfo) = self.faceRotationAnalysis( obj, norm, surf) if rtn is True: (clnBase, angle, clnStock, tag) = self.applyRotationalAnalysis( obj, base, angle, axis, subCount) # Verify faces are correctly oriented - InverseAngle might be necessary faceIA = getattr(clnBase.Shape, sub) (norm, surf) = self.getFaceNormAndSurf(faceIA) (rtn, praAngle, praAxis, praInfo) = self.faceRotationAnalysis( obj, norm, surf) if rtn is True: PathLog.error( translate( "Path", "Face appears misaligned after initial rotation." )) if obj.AttemptInverseAngle is True and obj.InverseAngle is False: (clnBase, clnStock, angle) = self.applyInverseAngle( obj, clnBase, clnStock, axis, angle) else: msg = translate( "Path", "Consider toggling the 'InverseAngle' property and recomputing." ) PathLog.error(msg) # title = translate("Path", 'Rotation Warning') # self.guiMessage(title, msg, False) else: PathLog.debug( "Face appears to be oriented correctly." ) tup = clnBase, sub, tag, angle, axis, clnStock else: if self.warnDisabledAxis(obj, axis) is False: PathLog.debug( str(sub) + ": No rotation used") axis = 'X' angle = 0.0 tag = base.Name + '_' + axis + str( angle).replace('.', '_') stock = PathUtils.findParentJob(obj).Stock tup = base, sub, tag, angle, axis, stock # Eif allTuples.append(tup) # Eif # Efor # Efor if subCount > 1: msg = translate('Path', "Multiple faces in Base Geometry.") + " " msg += translate( 'Path', "Depth settings will be applied to all faces.") PathLog.warning(msg) # title = translate("Path", "Depth Warning") # self.guiMessage(title, msg) (Tags, Grps) = self.sortTuplesByIndex( allTuples, 2) # return (TagList, GroupList) subList = [] for o in range(0, len(Tags)): subList = [] for (base, sub, tag, angle, axis, stock) in Grps[o]: subList.append(sub) pair = base, subList, angle, axis, stock baseSubsTuples.append(pair) # Efor else: PathLog.info( translate("Path", "EnableRotation property is 'Off'.")) stock = PathUtils.findParentJob(obj).Stock for (base, subList) in obj.Base: baseSubsTuples.append((base, subList, 0.0, 'X', stock)) # for base in obj.Base: for (base, subsList, angle, axis, stock) in baseSubsTuples: holes = [] faces = [] for sub in subsList: shape = getattr(base.Shape, sub) if isinstance(shape, Part.Face): faces.append(shape) if numpy.isclose(abs(shape.normalAt(0, 0).z), 1): # horizontal face for wire in shape.Wires[1:]: holes.append((base.Shape, wire)) else: ignoreSub = base.Name + '.' + sub msg = translate( 'Path', "Found a selected object which is not a face. Ignoring: {}" .format(ignoreSub)) PathLog.error(msg) FreeCAD.Console.PrintWarning(msg) # return for shape, wire in holes: f = Part.makeFace(wire, 'Part::FaceMakerSimple') drillable = PathUtils.isDrillable(shape, wire) if (drillable and obj.processCircles) or (not drillable and obj.processHoles): PathLog.track() # Recalculate depthparams (strDep, finDep) = self.calculateStartFinalDepths( obj, shape, stock) finalDepths.append(finDep) PathLog.debug( "Adjusted face depths strDep: {}, and finDep: {}". format(self.strDep, self.finDep)) 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=strDep, # obj.StartDepth.Value, step_down=obj.StepDown.Value, z_finish_step=finish_step, final_depth=finDep, # obj.FinalDepth.Value, user_depths=None) env = PathUtils.getEnvelope( shape, subshape=f, depthparams=self.depthparams) # shapes.append((env, True)) tup = env, True, 'pathProfileFaces', angle, axis, strDep, finDep shapes.append(tup) if len(faces) > 0: profileshape = Part.makeCompound(faces) self.profileshape.append(profileshape) if obj.processPerimeter: PathLog.track() if profileshape: # Recalculate depthparams (strDep, finDep) = self.calculateStartFinalDepths( obj, profileshape, stock) finalDepths.append(finDep) PathLog.debug( "Adjusted face depths strDep: {}, and finDep: {}". format(self.strDep, self.finDep)) 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=strDep, # obj.StartDepth.Value, step_down=obj.StepDown.Value, z_finish_step=finish_step, final_depth=finDep, # obj.FinalDepth.Value, user_depths=None) else: strDep = obj.StartDepth.Value finDep = obj.FinalDepth.Value try: env = PathUtils.getEnvelope( base.Shape, subshape=profileshape, depthparams=self.depthparams) except Exception: # PathUtils.getEnvelope() failed to return an object. PathLog.error( translate('Path', 'Unable to create path for face(s).')) else: # shapes.append((env, False)) tup = env, False, 'pathProfileFaces', angle, axis, strDep, finDep shapes.append(tup) else: for shape in faces: # Recalculate depthparams (strDep, finDep) = self.calculateStartFinalDepths( obj, shape, stock) finalDepths.append(finDep) 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=strDep, # obj.StartDepth.Value, step_down=obj.StepDown.Value, z_finish_step=finish_step, final_depth=finDep, # obj.FinalDepth.Value, user_depths=None) env = PathUtils.getEnvelope( base.Shape, subshape=shape, depthparams=self.depthparams) tup = env, False, 'pathProfileFaces', angle, axis, strDep, finDep shapes.append(tup) # Eif # adjust FinalDepth as needed finalDepth = min(finalDepths) if obj.FinalDepth.Value < finalDepth: obj.FinalDepth.Value = finalDepth else: # Try to build targets from the job base if 1 == len(self.model) and hasattr(self.model[0], "Proxy"): if isinstance(self.model[0].Proxy, ArchPanel.PanelSheet): # process the sheet if obj.processCircles or obj.processHoles: for shape in self.model[0].Proxy.getHoles( self.model[0], transform=True): for wire in shape.Wires: drillable = PathUtils.isDrillable( self.model[0].Proxy, wire) if (drillable and obj.processCircles) or ( not drillable and obj.processHoles): f = Part.makeFace(wire, 'Part::FaceMakerSimple') env = PathUtils.getEnvelope( self.model[0].Shape, subshape=f, depthparams=self.depthparams) # shapes.append((env, True)) tup = env, True, 'pathProfileFaces', 0.0, 'X', obj.StartDepth.Value, obj.FinalDepth.Value shapes.append(tup) if obj.processPerimeter: for shape in self.model[0].Proxy.getOutlines( self.model[0], transform=True): for wire in shape.Wires: f = Part.makeFace(wire, 'Part::FaceMakerSimple') env = PathUtils.getEnvelope( self.model[0].Shape, subshape=f, depthparams=self.depthparams) # shapes.append((env, False)) tup = env, False, 'pathProfileFaces', 0.0, 'X', obj.StartDepth.Value, obj.FinalDepth.Value shapes.append(tup) self.removalshapes = shapes PathLog.debug("%d shapes" % len(shapes)) return shapes
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): '''opExecute(obj) ... process engraving operation''' PathLog.track() job = PathUtils.findParentJob(obj) if job and job.Base: obj.BaseObject = job.Base zValues = [] if obj.StepDown.Value != 0: z = obj.StartDepth.Value - obj.StepDown.Value while z > obj.FinalDepth.Value: zValues.append(z) z -= obj.StepDown.Value zValues.append(obj.FinalDepth.Value) self.zValues = zValues try: if self.baseobject.isDerivedFrom('Sketcher::SketchObject') or \ self.baseobject.isDerivedFrom('Part::Part2DObject') or \ hasattr(self.baseobject, 'ArrayType'): PathLog.track() self.commandlist.append(Path.Command('G0', {'Z': obj.ClearanceHeight.Value, 'F': self.vertRapid})) # we only consider the outer wire if this is a Face wires = [] for w in self.baseobject.Shape.Wires: wires.append(Part.Wire(w.Edges)) self.buildpathocc(obj, wires, zValues) self.wires = wires elif isinstance(self.baseobject.Proxy, ArchPanel.PanelSheet): # process the sheet PathLog.track() wires = [] for tag in self.baseobject.Proxy.getTags(self.baseobject, transform=True): self.commandlist.append(Path.Command('G0', {'Z': obj.ClearanceHeight.Value, 'F': self.vertRapid})) tagWires = [] for w in tag.Wires: tagWires.append(Part.Wire(w.Edges)) self.buildpathocc(obj, tagWires, zValues) wires.extend(tagWires) self.wires = wires elif obj.Base: PathLog.track() wires = [] for base, subs in obj.Base: #edges = [] #for sub in subs: # edges.extend(base.Shape.getElement(sub).Edges) #shapeWires = adjustWirePlacement(obj, base, TechDraw.edgeWalker(edges)) #wires.extend(shapeWires) for feature in subs: sub = base.Shape.getElement(feature) if sub.Wires: shapeWires = sub.Wires else: shapeWires = [Part.Wire(sub.Edges)] wires.extend(adjustWirePlacement(obj, base, shapeWires)) self.buildpathocc(obj, wires, zValues) self.wires = wires elif not obj.BaseShapes: PathLog.track() raise ValueError(translate('PathEngrave', "Unknown baseobject type for engraving (%s)") % (obj.Base)) if obj.BaseShapes: PathLog.track() wires = [] for shape in obj.BaseShapes: shapeWires = adjustWirePlacement(obj, shape, shape.Shape.Wires) self.buildpathocc(obj, shapeWires, zValues) wires.extend(shapeWires) self.wires = wires except Exception as e: PathLog.error(e) traceback.print_exc() PathLog.error(translate('PathEngrave', 'The Job Base Object has no engraveable element. Engraving operation will produce no output.'))
def Activated(self): PathLog.track() FreeCAD.ActiveDocument.openTransaction( translate("Path_Post", "Post Process the Selected path(s)")) FreeCADGui.addModule("PathScripts.PathPost") # Attempt to figure out what the user wants to post-process # If a job is selected, post that. # If there's only one job in a document, post it. # If a user has selected a subobject of a job, post the job. # If multiple jobs and can't guess, ask them. selected = FreeCADGui.Selection.getSelectionEx() if len(selected) > 1: FreeCAD.Console.PrintError( "Please select a single job or other path object\n") return elif len(selected) == 1: sel = selected[0].Object if sel.Name[:3] == "Job": job = sel elif hasattr(sel, "Path"): try: job = PathUtils.findParentJob(sel) except Exception: # pylint: disable=broad-except job = None else: job = None if job is None: targetlist = [] for o in FreeCAD.ActiveDocument.Objects: if hasattr(o, "Proxy"): if isinstance(o.Proxy, PathJob.ObjectJob): targetlist.append(o.Label) PathLog.debug("Possible post objects: {}".format(targetlist)) if len(targetlist) > 1: form = FreeCADGui.PySideUic.loadUi(":/panels/DlgJobChooser.ui") form.cboProject.addItems(targetlist) r = form.exec_() if r is False: return else: jobname = form.cboProject.currentText() else: jobname = targetlist[0] job = FreeCAD.ActiveDocument.getObject(jobname) PathLog.debug("about to postprocess job: {}".format(job.Name)) # Build up an ordered list of operations and tool changes. # Then post-the ordered list if hasattr(job, "Fixtures"): wcslist = job.Fixtures else: wcslist = ['G54'] if hasattr(job, "OrderOutputBy"): orderby = job.OrderOutputBy else: orderby = "Operation" if hasattr(job, "SplitOutput"): split = job.SplitOutput else: split = False if hasattr(job, "OperatorSetupsheet"): ossheet = job.OperatorSetupsheet else: ossheet = False postlist = [] if orderby == 'Fixture': PathLog.debug("Ordering by Fixture") # Order by fixture means all operations and tool changes will be completed in one # fixture before moving to the next. currTool = None for index, f in enumerate(wcslist): # create an object to serve as the fixture path fobj = _TempObject() c1 = Path.Command(f) fobj.Path = Path.Path([c1]) if index != 0: c2 = Path.Command("G0 Z" + str(job.Stock.Shape.BoundBox.ZMax + job.SetupSheet.ClearanceHeightOffset.Value)) fobj.Path.addCommands(c2) fobj.InList.append(job) sublist = [fobj] # Now generate the gcode for obj in job.Operations.Group: tc = PathUtil.toolControllerForOp(obj) if tc is not None and PathUtil.opProperty(obj, 'Active'): if tc.ToolNumber != currTool: sublist.append(tc) PathLog.debug("Appending TC: {}".format(tc.Name)) currTool = tc.ToolNumber sublist.append(obj) postlist.append(sublist) elif orderby == 'Tool': PathLog.debug("Ordering by Tool") # Order by tool means tool changes are minimized. # all operations with the current tool are processed in the current # fixture before moving to the next fixture. currTool = None fixturelist = [] for f in wcslist: # create an object to serve as the fixture path fobj = _TempObject() c1 = Path.Command(f) c2 = Path.Command("G0 Z" + str(job.Stock.Shape.BoundBox.ZMax + job.SetupSheet.ClearanceHeightOffset.Value)) fobj.Path = Path.Path([c1, c2]) fobj.InList.append(job) fixturelist.append(fobj) # Now generate the gcode curlist = [] # list of ops for tool, will repeat for each fixture sublist = [] # list of ops for output splitting for idx, obj in enumerate(job.Operations.Group): # check if the operation is active active = PathUtil.opProperty(obj, 'Active') tc = PathUtil.toolControllerForOp(obj) if tc is None or tc.ToolNumber == currTool and active: curlist.append(obj) elif tc.ToolNumber != currTool and currTool is None and active: # first TC sublist.append(tc) curlist.append(obj) currTool = tc.ToolNumber elif tc.ToolNumber != currTool and currTool is not None and active: # TC for fixture in fixturelist: sublist.append(fixture) sublist.extend(curlist) postlist.append(sublist) sublist = [tc] curlist = [obj] currTool = tc.ToolNumber if idx == len(job.Operations.Group) - 1: # Last operation. for fixture in fixturelist: sublist.append(fixture) sublist.extend(curlist) postlist.append(sublist) elif orderby == 'Operation': PathLog.debug("Ordering by Operation") # Order by operation means ops are done in each fixture in # sequence. currTool = None firstFixture = True # Now generate the gcode for obj in job.Operations.Group: if PathUtil.opProperty(obj, 'Active'): sublist = [] PathLog.debug("obj: {}".format(obj.Name)) for f in wcslist: fobj = _TempObject() c1 = Path.Command(f) fobj.Path = Path.Path([c1]) if not firstFixture: c2 = Path.Command("G0 Z" + str(job.Stock.Shape.BoundBox.ZMax + job.SetupSheet.ClearanceHeightOffset.Value)) fobj.Path.addCommands(c2) fobj.InList.append(job) sublist.append(fobj) firstFixture = False tc = PathUtil.toolControllerForOp(obj) if tc is not None: if tc.ToolNumber != currTool: sublist.append(tc) currTool = tc.ToolNumber sublist.append(obj) postlist.append(sublist) fail = True rc = '' # pylint: disable=unused-variable if split: for slist in postlist: (fail, rc) = self.exportObjectsWith(slist, job) else: finalpostlist = [item for slist in postlist for item in slist] (fail, rc) = self.exportObjectsWith(finalpostlist, job) self.subpart = 1 if fail: FreeCAD.ActiveDocument.abortTransaction() else: FreeCAD.ActiveDocument.commitTransaction() if ossheet: FreeCAD.Console.PrintLog("Create OperatorSetupsheet\n") else: FreeCAD.Console.PrintLog("Do not create OperatorSetupsheet\n") FreeCAD.ActiveDocument.recompute()