예제 #1
0
 def setFromTemplate(self, obj, template):
     '''setFromTemplate(obj, xmlItem) ... extract properties from xmlItem and assign to receiver.'''
     PathLog.track(obj.Name, template)
     if template.get(ToolControllerTemplate.Version) and 1 == int(template.get(ToolControllerTemplate.Version)):
         if template.get(ToolControllerTemplate.Label):
             obj.Label = template.get(ToolControllerTemplate.Label)
         if template.get(ToolControllerTemplate.VertFeed):
             obj.VertFeed = template.get(ToolControllerTemplate.VertFeed)
         if template.get(ToolControllerTemplate.HorizFeed):
             obj.HorizFeed = template.get(ToolControllerTemplate.HorizFeed)
         if template.get(ToolControllerTemplate.VertRapid):
             obj.VertRapid = template.get(ToolControllerTemplate.VertRapid)
         if template.get(ToolControllerTemplate.HorizRapid):
             obj.HorizRapid = template.get(ToolControllerTemplate.HorizRapid)
         if template.get(ToolControllerTemplate.SpindleSpeed):
             obj.SpindleSpeed = float(template.get(ToolControllerTemplate.SpindleSpeed))
         if template.get(ToolControllerTemplate.SpindleDir):
             obj.SpindleDir = template.get(ToolControllerTemplate.SpindleDir)
         if template.get(ToolControllerTemplate.ToolNumber):
             obj.ToolNumber = int(template.get(ToolControllerTemplate.ToolNumber))
         if template.get(ToolControllerTemplate.Tool):
             obj.Tool.setFromTemplate(template.get(ToolControllerTemplate.Tool))
         if template.get(ToolControllerTemplate.Expressions):
             for exprDef in template.get(ToolControllerTemplate.Expressions):
                 obj.setExpression(exprDef[ToolControllerTemplate.ExprProp], exprDef[ToolControllerTemplate.ExprExpr])
     else:
         PathLog.error(translate('PathToolController', "Unsupported PathToolController template version %s") % template.get(ToolControllerTemplate.Version))
예제 #2
0
def isVertical(obj):
    '''isVertical(obj) ... answer True if obj points into Z'''
    if type(obj) == FreeCAD.Vector:
        return isRoughly(obj.x, 0) and isRoughly(obj.y, 0)

    if obj.ShapeType == 'Face':
        if type(obj.Surface) == Part.Plane:
            return isHorizontal(obj.Surface.Axis)
        if type(obj.Surface) == Part.Cylinder or type(obj.Surface) == Part.Cone:
            return isVertical(obj.Surface.Axis)
        if type(obj.Surface) == Part.Sphere:
            return True
        if type(obj.Surface) == Part.SurfaceOfExtrusion:
            return isVertical(obj.Surface.Direction)
        if type(obj.Surface) == Part.SurfaceOfRevolution:
            return isHorizontal(obj.Surface.Direction)
        if type(obj.Surface) != Part.BSplineSurface:
            PathLog.info(translate('PathGeom', "face %s not handled, assuming not vertical") % type(obj.Surface))
        return None

    if obj.ShapeType == 'Edge':
        if type(obj.Curve) == Part.Line or type(obj.Curve) == Part.LineSegment:
            return isVertical(obj.Vertexes[1].Point - obj.Vertexes[0].Point)
        if type(obj.Curve) == Part.Circle or type(obj.Curve) == Part.Ellipse: # or type(obj.Curve) == Part.BSplineCurve:
            return isHorizontal(obj.Curve.Axis)
        if type(obj.Curve) == Part.BezierCurve:
            # the current assumption is that a bezier curve is vertical if its end points are vertical
            return isVertical(obj.Curve.EndPoint - obj.Curve.StartPoint)
        if type(obj.Curve) != Part.BSplineCurve:
            PathLog.info(translate('PathGeom', "edge %s not handled, assuming not vertical") % type(obj.Curve))
        return None

    PathLog.error(translate('PathGeom', "isVertical(%s) not supported") % obj)
    return None
예제 #3
0
파일: PathJobCmd.py 프로젝트: itain/FreeCAD
    def __init__(self, job, parent=None):
        self.job = job
        self.dialog = FreeCADGui.PySideUic.loadUi(":/panels/DlgJobTemplateExport.ui")

        if job.PostProcessor:
            ppHint = "%s %s %s" % (job.PostProcessor, job.PostProcessorArgs, job.PostProcessorOutputFile)
            self.dialog.postProcessingHint.setText(ppHint)
        else:
            self.dialog.postProcessingGroup.setEnabled(False)
            self.dialog.postProcessingGroup.setChecked(False)

        if job.Stock and not PathJob.isResourceClone(job, 'Stock', 'Stock'):
            if hasattr(job.Stock, 'ExtXNeg'):
                seHint = translate('PathJob', "Base -/+ %.2f/%.2f %.2f/%.2f %.2f/%.2f") % (job.Stock.ExtXneg, job.Stock.ExtXpos, job.Stock.ExtYneg, job.Stock.ExtYpos, job.Stock.ExtZneg, job.Stock.ExtZpos)
                self.dialog.stockPlacement.setChecked(False)
            elif hasattr(job.Stock, 'Length') and hasattr(job.Stock, 'Width'):
                seHint = translate('PathJob', "Box: %.2f x %.2f x %.2f") % (job.Stock.Length, job.Stock.Width, job.Stock.Height)
            elif hasattr(job.Stock, 'Radius'):
                seHint = translate('PathJob', "Cylinder: %.2f x %.2f") % (job.Stock.Radius, job.Stock.Height)
            else:
                seHint = '-'
                PathLog.error(translate('PathJob', 'Unsupported stock type'))
            self.dialog.stockExtentHint.setText(seHint)
            spHint = "%s" % job.Stock.Placement
            self.dialog.stockPlacementHint.setText(spHint)

        for tc in sorted(job.ToolController, key=lambda o: o.Label):
            item = QtGui.QListWidgetItem(tc.Label)
            item.setData(self.DataObject, tc)
            item.setCheckState(QtCore.Qt.CheckState.Checked)
            self.dialog.toolsList.addItem(item)

        self.dialog.toolsGroup.clicked.connect(self.checkUncheckTools)
 def commandsForEdges(self):
     global failures
     if self.edges:
         try:
             shape = self.shell().common(self.tag.solid)
             commands = []
             rapid = None
             for e, flip in self.orderAndFlipEdges(self.cleanupEdges(shape.Edges)):
                 debugEdge(e, '++++++++ %s' % ('<' if flip else '>'), False)
                 p1 = e.valueAt(e.FirstParameter)
                 p2 = e.valueAt(e.LastParameter)
                 if self.tag.isSquare and (PathGeom.isRoughly(p1.z, self.maxZ) or p1.z > self.maxZ) and (PathGeom.isRoughly(p2.z, self.maxZ) or p2.z > self.maxZ):
                     rapid = p1 if flip else p2
                 else:
                     if rapid:
                         commands.append(Path.Command('G0', {'X': rapid.x, 'Y': rapid.y, 'Z': rapid.z}))
                         rapid = None
                     commands.extend(PathGeom.cmdsForEdge(e, False, False, self.segm))
             if rapid:
                 commands.append(Path.Command('G0', {'X': rapid.x, 'Y': rapid.y, 'Z': rapid.z}))
                 rapid = None
             return commands
         except Exception as e:
             PathLog.error("Exception during processing tag @(%.2f, %.2f) (%s) - disabling the tag" % (self.tag.x, self.tag.y, e.args[0]))
             #traceback.print_exc(e)
             self.tag.enabled = False
             commands = []
             for e in self.edges:
                 commands.extend(PathGeom.cmdsForEdge(e))
             failures.append(self)
             return commands
     return []
예제 #5
0
 def selectionSupportedAsBaseGeometry(self, selection, ignoreErrors):
     if len(selection) != 1:
         if not ignoreErrors:
             PathLog.error(translate("PathProject", "Please select %s from a single solid" % self.featureName()))
         return False
     sel = selection[0]
     if sel.HasSubObjects:
         if not self.supportsVertexes() and selection[0].SubObjects[0].ShapeType == "Vertex":
             if not ignoreErrors:
                 PathLog.error(translate("PathProject", "Vertexes are not supported"))
             return False
         if not self.supportsEdges() and selection[0].SubObjects[0].ShapeType == "Edge":
             if not ignoreErrors:
                 PathLog.error(translate("PathProject", "Edges are not supported"))
             return False
         if not self.supportsFaces() and selection[0].SubObjects[0].ShapeType == "Face":
             if not ignoreErrors:
                 PathLog.error(translate("PathProject", "Faces are not supported"))
             return False
     else:
         if not self.supportsPanels() or not 'Panel' in sel.Object.Name:
             if not ignoreErrors:
                 PathLog.error(translate("PathProject", "Please select %s of a solid" % self.featureName()))
             return False
     return True
예제 #6
0
def isHorizontal(obj):
    '''isHorizontal(obj) ... answer True if obj points into X or Y'''
    if type(obj) == FreeCAD.Vector:
        return isRoughly(obj.z, 0)

    if obj.ShapeType == 'Face':
        if type(obj.Surface) == Part.Plane:
            return isVertical(obj.Surface.Axis)
        if type(obj.Surface) == Part.Cylinder or type(obj.Surface) == Part.Cone:
            return isHorizontal(obj.Surface.Axis)
        if type(obj.Surface) == Part.Sphere:
            return True
        if type(obj.Surface) == Part.SurfaceOfExtrusion:
            return isHorizontal(obj.Surface.Direction)
        if type(obj.Surface) == Part.SurfaceOfRevolution:
            return isVertical(obj.Surface.Direction)
        return isRoughly(obj.BoundBox.ZLength, 0.0)

    if obj.ShapeType == 'Edge':
        if type(obj.Curve) == Part.Line or type(obj.Curve) == Part.LineSegment:
            return isHorizontal(obj.Vertexes[1].Point - obj.Vertexes[0].Point)
        if type(obj.Curve) == Part.Circle or type(obj.Curve) == Part.Ellipse: # or type(obj.Curve) == Part.BSplineCurve:
            return isVertical(obj.Curve.Axis)
        return isRoughly(obj.BoundBox.ZLength, 0.0)

    PathLog.error(translate('PathGeom', "isHorizontal(%s) not supported") % obj)
    return None
예제 #7
0
def addToJob(obj, jobname=None):
    '''adds a path object to a job
    obj = obj
    jobname = None'''
    PathLog.track(jobname)
    if jobname is not None:
        jobs = GetJobs(jobname)
        if len(jobs) == 1:
            job = jobs[0]
        else:
            PathLog.error(translate("Path", "Didn't find job %s") % jobname)
            return None
    else:
        jobs = GetJobs()
        if len(jobs) == 0:
            job = PathJobCmd.CommandJobCreate().Activated()
        elif len(jobs) == 1:
            job = jobs[0]
        else:
            # form = FreeCADGui.PySideUic.loadUi(FreeCAD.getHomePath() + "Mod/Path/DlgJobChooser.ui")
            form = FreeCADGui.PySideUic.loadUi(":/panels/DlgJobChooser.ui")
            mylist = [i.Label for i in jobs]
            form.cboProject.addItems(mylist)
            r = form.exec_()
            if r is False:
                return None
            else:
                print(form.cboProject.currentText())
                job = [i for i in jobs if i.Label == form.cboProject.currentText()][0]

    if obj and job:
        job.Proxy.addOperation(obj)
    return job
예제 #8
0
파일: PathJobGui.py 프로젝트: itain/FreeCAD
 def getFields(self, obj, fields = ['radius', 'height']):
     if self.IsStock(obj):
         if 'radius' in fields:
             obj.Stock.Radius = FreeCAD.Units.Quantity(self.form.stockCylinderRadius.text())
         if 'height' in fields:
             obj.Stock.Height = FreeCAD.Units.Quantity(self.form.stockCylinderHeight.text())
     else:
         PathLog.error(translate('PathJob', 'Stock not a cylinder!'))
 def tooltableFromAttrs(self, stringattrs):
     if stringattrs.get('Version') and 1 == int(stringattrs['Version']):
         attrs = {}
         for key, val in PathUtil.keyValueIter(stringattrs['Tools']):
             attrs[int(key)] = val
         return Path.Tooltable(attrs)
     else:
         PathLog.error(translate('PathToolLibraryManager', "Unsupported Path tooltable template version %s") % stringattrs.get('Version'))
     return None
예제 #10
0
파일: PathJobGui.py 프로젝트: itain/FreeCAD
 def getFields(self, obj, fields = ['length', 'widht', 'height']):
     if self.IsStock(obj):
         if 'length' in fields:
             obj.Stock.Length = FreeCAD.Units.Quantity(self.form.stockBoxLength.text())
         if 'width' in fields:
             obj.Stock.Width  = FreeCAD.Units.Quantity(self.form.stockBoxWidth.text())
         if 'height' in fields:
             obj.Stock.Height = FreeCAD.Units.Quantity(self.form.stockBoxHeight.text())
     else:
         PathLog.error(translate('PathJob', 'Stock not a box!'))
 def createRampEdge(self, originalEdge, startPoint, endPoint):
     # PathLog.debug("Create edge from [{},{},{}] to [{},{},{}]".format(startPoint.x,startPoint.y, startPoint.z, endPoint.x, endPoint.y, endPoint.z))
     if type(originalEdge.Curve) == Part.Line or type(originalEdge.Curve) == Part.LineSegment:
         return Part.makeLine(startPoint, endPoint)
     elif type(originalEdge.Curve) == Part.Circle:
         arcMid = originalEdge.valueAt((originalEdge.FirstParameter + originalEdge.LastParameter) / 2)
         arcMid.z = (startPoint.z + endPoint.z) / 2
         return Part.Arc(startPoint, arcMid, endPoint).toShape()
     else:
         PathLog.error("Edge should not be helix")
예제 #12
0
def addToJob(obj, jobname=None):
    '''adds a path object to a job
    obj = obj
    jobname = None'''
    PathLog.track(jobname)
    if jobname is not None:
        jobs = GetJobs(jobname)
        if len(jobs) == 1:
            job = jobs[0]
        else:
            PathLog.error(translate("Path", "Didn't find job %s") % jobname)
            return None
    else:
        jobs = GetJobs()
        if len(jobs) == 0:
            job = PathJobCmd.CommandJobCreate().Activated()
        elif len(jobs) == 1:
            job = jobs[0]
        else:
            selected = FreeCADGui.Selection.getSelection()
            if 1 == len(selected) and selected[0] in jobs:
                job = selected[0]
            else:
                modelSelected = []
                for job in jobs:
                    if all([o in job.Model.Group for o in selected]):
                        modelSelected.append(job)
                if 1 == len(modelSelected):
                    job = modelSelected[0]
                else:
                    modelObjectSelected = []
                    for job in jobs:
                        if all([o in job.Proxy.baseObjects(job) for o in selected]):
                            modelObjectSelected.append(job)
                    if 1 == len(modelObjectSelected):
                        job = modelObjectSelected[0]
                    else:
                        # form = FreeCADGui.PySideUic.loadUi(FreeCAD.getHomePath() + "Mod/Path/DlgJobChooser.ui")
                        form = FreeCADGui.PySideUic.loadUi(":/panels/DlgJobChooser.ui")
                        if modelObjectSelected:
                            mylist = [j.Label for j in modelObjectSelected]
                        else:
                            mylist = [j.Label for j in jobs]
                        form.cboProject.addItems(mylist)
                        r = form.exec_()
                        if r is False or r == 0:
                            return None
                        else:
                            job = [j for j in jobs if j.Label == form.cboProject.currentText()][0]

    if obj and job:
        job.Proxy.addOperation(obj)
    return job
 def execute(self, obj):
     if not obj.Base:
         return
     if not obj.Base.isDerivedFrom("Path::Feature"):
         return
     if not obj.Base.Path:
         return
     if obj.Length < 0:
         PathLog.error(translate("Length/Radius positive not Null")+"\n")
         obj.Length = 0.1
     self.wire, self.rapids = PathGeom.wireForPath(obj.Base.Path)
     obj.Path = self.generateLeadInOutCurve(obj)
예제 #14
0
 def setup(self, obj):
     obj.Angle = 60
     obj.Method = 2
     toolLoad = obj.ToolController
     if toolLoad is None or toolLoad.ToolNumber == 0:
         PathLog.error(translate("No Tool Controller is selected. We need a tool to build a Path\n"))
         return
     else:
         tool = toolLoad.Proxy.getTool(toolLoad)
         if not tool:
             PathLog.error(translate("No Tool found. We need a tool to build a Path.\n"))
             return
예제 #15
0
    def opExecute(self, obj):
        '''opExecute(obj) ... process engraving operation'''
        PathLog.track()

        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

        output = ''
        try:
            if self.baseobject.isDerivedFrom('Sketcher::SketchObject') or \
                    self.baseobject.isDerivedFrom('Part::Part2DObject') or \
                    hasattr(self.baseobject, 'ArrayType'):

                output += "G0 Z" + PathUtils.fmt(obj.ClearanceHeight.Value) + "F " + PathUtils.fmt(self.vertRapid) + "\n"
                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:
                    tempedges = PathUtils.cleanedges(w.Edges, 0.5)
                    wires.append(Part.Wire(tempedges))
                output += self.buildpathocc(obj, wires, zValues)
                self.wires = wires

            elif isinstance(self.baseobject.Proxy, ArchPanel.PanelSheet):  # process the sheet
                wires = []
                for tag in self.baseobject.Proxy.getTags(self.baseobject, transform=True):
                    output += "G0 Z" + PathUtils.fmt(obj.ClearanceHeight.Value) + "F " + PathUtils.fmt(self.vertRapid) + "\n"
                    self.commandlist.append(Path.Command('G0', {'Z': obj.ClearanceHeight.Value, 'F': self.vertRapid}))
                    tagWires = []
                    for w in tag.Wires:
                        tempedges = PathUtils.cleanedges(w.Edges, 0.5)
                        tagWires.append(Part.Wire(tempedges))
                    output += self.buildpathocc(obj, tagWires, zValues)
                    wires.extend(tagWires)
                self.wires = wires
            else:
                raise ValueError('Unknown baseobject type for engraving')

            output += "G0 Z" + PathUtils.fmt(obj.ClearanceHeight.Value) + "F " + PathUtils.fmt(self.vertRapid) + "\n"
            self.commandlist.append(Path.Command('G0', {'Z': obj.ClearanceHeight.Value, 'F': self.vertRapid}))

        except Exception as e:
            #PathLog.error("Exception: %s" % e)
            PathLog.error(translate("Path", "The Job Base Object has no engraveable element.  Engraving operation will produce no output."))
예제 #16
0
파일: PathStock.py 프로젝트: lanigb/FreeCAD
def shapeBoundBox(obj):
    if hasattr(obj, 'Shape'):
        return obj.Shape.BoundBox
    if obj and 'App::Part' == obj.TypeId:
        bounds = [shapeBoundBox(o) for o in obj.Group]
        if bounds:
            bb = bounds[0]
            for b in bounds[1:]:
                bb = bb.united(b)
            return bb
    if obj:
        PathLog.error(translate('PathStock', "Invalid base object %s - no shape found") % obj.Name)
    return None
예제 #17
0
 def addOperation(self, op, before = None):
     group = self.obj.Operations.Group
     if op not in group:
         if before:
             try:
                 group.insert(group.index(before), op)
             except Exception as e:
                 PathLog.error(e)
                 group.append(op)
         else:
             group.append(op)
         self.obj.Operations.Group = group
         op.Path.Center = self.obj.Operations.Path.Center
예제 #18
0
    def CreateToolProfile(self, tool, dir, pos, rad):
        type = tool.ToolType
        # rad = tool.Diameter / 2.0 - 0.001 * pos[2] # hack to overcome occ bug
        xf = dir[0] * rad
        yf = dir[1] * rad
        xp = pos[0]
        yp = pos[1]
        zp = pos[2]
        h = tool.CuttingEdgeHeight
        if h <= 0.0: #set default if user fails to avoid freeze
            h = 1.0
            PathLog.error("SET Tool Length")
        # common to all tools
        vTR = Vector(xp + yf, yp - xf, zp + h)
        vTC = Vector(xp, yp, zp + h)
        vBC = Vector(xp, yp, zp)
        lT = Part.makeLine(vTR, vTC)
        res = None
        if type == "ChamferMill":
            ang = 90 - tool.CuttingEdgeAngle / 2.0
            if ang > 80:
                ang = 80
            if ang < 0:
                ang = 0
            h1 = math.tan(ang * math.pi / 180) * rad
            if h1 > (h - 0.1):
                h1 = h - 0.1
            vBR = Vector(xp + yf, yp - xf, zp + h1)
            lR = Part.makeLine(vBR, vTR)
            lB = Part.makeLine(vBC, vBR)
            res = Part.Wire([lB, lR, lT])

        elif type == "BallEndMill":
            h1 = rad
            if h1 >= h:
                h1 = h - 0.1
            vBR = Vector(xp + yf, yp - xf, zp + h1)
            r2 = h1 / 2.0
            h2 = rad - math.sqrt(rad * rad - r2 * r2)
            vBCR = Vector(xp + yf / 2.0, yp - xf / 2.0, zp + h2)
            cB = Part.Edge(Part.Arc(vBC, vBCR, vBR))
            lR = Part.makeLine(vBR, vTR)
            res = Part.Wire([cB, lR, lT])

        else:  # default: assume type == "EndMill"
            vBR = Vector(xp + yf, yp - xf, zp)
            lR = Part.makeLine(vBR, vTR)
            lB = Part.makeLine(vBC, vBR)
            res = Part.Wire([lB, lR, lT])

        return res
예제 #19
0
 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
예제 #20
0
    def Activated(self):
        # check that the selection contains exactly what we want
        selection = FreeCADGui.Selection.getSelection()
        if len(selection) != 1:
            PathLog.error(translate('Path_DressupTag', 'Please select one path object')+'\n')
            return
        baseObject = selection[0]

        # everything ok!
        FreeCAD.ActiveDocument.openTransaction(translate('Path_DressupTag', 'Create Tag Dress-up'))
        FreeCADGui.addModule('PathScripts.PathDressupTagGui')
        FreeCADGui.doCommand("PathScripts.PathDressupTagGui.Create(App.ActiveDocument.%s)" % baseObject.Name)
        FreeCAD.ActiveDocument.commitTransaction()
        FreeCAD.ActiveDocument.recompute()
예제 #21
0
파일: PathJobGui.py 프로젝트: itain/FreeCAD
def Create(base, template=None):
    '''Create(base, template) ... creates a job instance for the given base object
    using template to configure it.'''
    FreeCADGui.addModule('PathScripts.PathJob')
    FreeCAD.ActiveDocument.openTransaction(translate("Path_Job", "Create Job"))
    try:
        obj = PathJob.Create('Job', base, template)
        ViewProvider(obj.ViewObject)
        FreeCAD.ActiveDocument.commitTransaction()
        obj.ViewObject.Proxy.editObject(obj.Stock)
        return obj
    except:
        PathLog.error(sys.exc_info())
        FreeCAD.ActiveDocument.abortTransaction()
예제 #22
0
def drillTipLength(tool):
    """returns the length of the drillbit tip."""
    if tool.CuttingEdgeAngle == 180 or tool.CuttingEdgeAngle == 0.0 or tool.Diameter == 0.0:
        return 0.0
    else:
        if tool.CuttingEdgeAngle <= 0 or tool.CuttingEdgeAngle >= 180:
            PathLog.error(translate("Path", "Invalid Cutting Edge Angle %.2f, must be >0° and <=180°") % tool.CuttingEdgeAngle)
            return 0.0
        theta = math.radians(tool.CuttingEdgeAngle)
        length = (tool.Diameter/2) / math.tan(theta/2) 
        if length < 0:
            PathLog.error(translate("Path", "Cutting Edge Angle (%.2f) results in negative tool tip length") % tool.CuttingEdgeAngle)
            return 0.0
        return length
예제 #23
0
    def updateStockEditor(self, index):
        def setupFromBaseEdit():
            if not self.stockFromBase:
                self.stockFromBase = StockFromBaseBoundBoxEdit(self.obj, self.form)
            self.stockEdit = self.stockFromBase
        def setupCreateBoxEdit():
            if not self.stockCreateBox:
                self.stockCreateBox = StockCreateBoxEdit(self.obj, self.form)
            self.stockEdit = self.stockCreateBox
        def setupCreateCylinderEdit():
            if not self.stockCreateCylinder:
                self.stockCreateCylinder = StockCreateCylinderEdit(self.obj, self.form)
            self.stockEdit = self.stockCreateCylinder
        def setupFromExisting():
            if not self.stockFromExisting:
                self.stockFromExisting = StockFromExistingEdit(self.obj, self.form)
            if self.stockFromExisting.candidates(self.obj):
                self.stockEdit = self.stockFromExisting
                return True
            return False

        if index == -1:
            if self.obj.Stock is None or StockFromBaseBoundBoxEdit.IsStock(self.obj):
                setupFromBaseEdit()
            elif StockCreateBoxEdit.IsStock(self.obj):
                setupCreateBoxEdit()
            elif StockCreateCylinderEdit.IsStock(self.obj):
                setupCreateCylinderEdit()
            elif StockFromExistingEdit.IsStock(self.obj):
                setupFromExisting()
            else:
                PathLog.error(translate('PathJob', "Unsupported stock object %s") % self.obj.Stock.Label)
        else:
            if index == StockFromBaseBoundBoxEdit.Index:
                setupFromBaseEdit()
            elif index == StockCreateBoxEdit.Index:
                setupCreateBoxEdit()
            elif index == StockCreateCylinderEdit.Index:
                setupCreateCylinderEdit()
            elif index == StockFromExistingEdit.Index:
                if not setupFromExisting():
                    setupFromBaseEdit()
                    index = -1
            else:
                PathLog.error(translate('PathJob', "Unsupported stock type %s (%d)") % (self.form.stock.currentText(), index))
        self.stockEdit.activate(self.obj, index == -1)

        if -1 != index:
            self.template.updateUI()
예제 #24
0
    def doExecute(self, obj):
        if not obj.Base:
            return
        if not obj.Base.isDerivedFrom("Path::Feature"):
            return
        if not obj.Base.Path:
            return
        if not obj.Base.Path.Commands:
            return

        pathData = self.setup(obj)
        if not pathData:
            print("execute - no pathData")
            return

        self.tags = []
        if hasattr(obj, "Positions"):
            self.tags, positions, disabled = self.createTagsPositionDisabled(obj, obj.Positions, obj.Disabled)
            if obj.Disabled != disabled:
                PathLog.debug("Updating properties.... %s vs. %s" % (obj.Disabled, disabled))
                obj.Positions = positions
                obj.Disabled = disabled

        if not self.tags:
            print("execute - no tags")
            obj.Path = obj.Base.Path
            return

        try:
            self.processTags(obj)
        except Exception as e:
            PathLog.error("processing tags failed clearing all tags ... '%s'" % (e.args[0]))
            #if sys.version_info.major < 3:
            #    traceback.print_exc(e)
            #else:
            #    traceback.print_exc()
            obj.Path = obj.Base.Path

        # update disabled in case there are some additional ones
        disabled = copy.copy(self.obj.Disabled)
        solids = []
        for tag in self.tags:
            solids.append(tag.solid)
            if not tag.enabled and tag.id not in disabled:
                disabled.append(tag.id)
        self.solids = solids
        if obj.Disabled != disabled:
            obj.Disabled = disabled
예제 #25
0
파일: PathAreaOp.py 프로젝트: itain/FreeCAD
    def opOnChanged(self, obj, prop):
        '''opOnChanged(obj, prop) ... base implemenation of the notification framework - do not overwrite.
        The base implementation takes a stab at determining Heights and Depths if the operations's Base
        changes.
        Do not overwrite, overwrite areaOpOnChanged(obj, prop) instead.'''
        #PathLog.track(obj.Label, prop)
        if prop in ['AreaParams', 'PathParams', 'removalshape']:
            obj.setEditorMode(prop, 2)

        if PathOp.FeatureBaseGeometry & self.opFeatures(obj):
            if prop == 'Base' and len(obj.Base) == 1:
                PathLog.info("opOnChanged(%s, %s)" % (obj.Label, prop))
                try:
                    (base, sub) = obj.Base[0]
                    bb = base.Shape.BoundBox  # parent boundbox
                    subobj = base.Shape.getElement(sub[0])
                    fbb = subobj.BoundBox  # feature boundbox
                    obj.StartDepth = bb.ZMax
                    obj.ClearanceHeight = bb.ZMax + 5.0
                    obj.SafeHeight = bb.ZMax + 3.0

                    if fbb.ZMax == fbb.ZMin and fbb.ZMax == bb.ZMax:  # top face
                        obj.FinalDepth = bb.ZMin
                    elif fbb.ZMax > fbb.ZMin and fbb.ZMax == bb.ZMax:  # vertical face, full cut
                        obj.FinalDepth = fbb.ZMin
                    elif fbb.ZMax > fbb.ZMin and fbb.ZMin > bb.ZMin:  # internal vertical wall
                        obj.FinalDepth = fbb.ZMin
                    elif fbb.ZMax == fbb.ZMin and fbb.ZMax > bb.ZMin:  # face/shelf
                        obj.FinalDepth = fbb.ZMin
                    else:  # catch all
                        obj.FinalDepth = bb.ZMin

                    if hasattr(obj, 'Side'):
                        if bb.XLength == fbb.XLength and bb.YLength == fbb.YLength:
                            obj.Side = "Outside"
                        else:
                            obj.Side = "Inside"

                except Exception as e:
                    PathLog.error(translate("PatArea", "Error in calculating depths: %s") % e)
                    obj.StartDepth = 5.0
                    obj.ClearanceHeight = 10.0
                    obj.SafeHeight = 8.0
                    if hasattr(obj, 'Side'):
                        obj.Side = "Outside"

        self.areaOpOnChanged(obj, prop)
예제 #26
0
    def updateUI(self):
        job = self.job
        if job.PostProcessor:
            ppHint = "%s %s %s" % (job.PostProcessor, job.PostProcessorArgs, job.PostProcessorOutputFile)
            self.dialog.postProcessingHint.setText(ppHint)
        else:
            self.dialog.postProcessingGroup.setEnabled(False)
            self.dialog.postProcessingGroup.setChecked(False)

        if job.Stock and not PathJob.isResourceClone(job, 'Stock', 'Stock'):
            stockType = PathStock.StockType.FromStock(job.Stock)
            if stockType == PathStock.StockType.FromBase:
                seHint = translate('PathJob', "Base -/+ %.2f/%.2f %.2f/%.2f %.2f/%.2f") % (job.Stock.ExtXneg, job.Stock.ExtXpos, job.Stock.ExtYneg, job.Stock.ExtYpos, job.Stock.ExtZneg, job.Stock.ExtZpos)
                self.dialog.stockPlacement.setChecked(False)
            elif stockType == PathStock.StockType.CreateBox:
                seHint = translate('PathJob', "Box: %.2f x %.2f x %.2f") % (job.Stock.Length, job.Stock.Width, job.Stock.Height)
            elif stockType == PathStock.StockType.CreateCylinder:
                seHint = translate('PathJob', "Cylinder: %.2f x %.2f") % (job.Stock.Radius, job.Stock.Height)
            else:
                seHint = '-'
                PathLog.error(translate('PathJob', 'Unsupported stock type'))
            self.dialog.stockExtentHint.setText(seHint)
            spHint = "%s" % job.Stock.Placement
            self.dialog.stockPlacementHint.setText(spHint)

        rapidChanged = not job.SetupSheet.Proxy.hasDefaultToolRapids()
        depthsChanged = not job.SetupSheet.Proxy.hasDefaultOperationDepths()
        heightsChanged = not job.SetupSheet.Proxy.hasDefaultOperationHeights()
        opsWithSettings = job.SetupSheet.Proxy.operationsWithSettings()
        settingsChanged = rapidChanged or depthsChanged or heightsChanged or 0 != len(opsWithSettings)
        self.dialog.settingsGroup.setChecked(settingsChanged)
        self.dialog.settingToolRapid.setChecked(rapidChanged)
        self.dialog.settingOperationDepths.setChecked(depthsChanged)
        self.dialog.settingOperationHeights.setChecked(heightsChanged)

        self.dialog.settingsOpsList.clear()
        for op in opsWithSettings:
            item = QtGui.QListWidgetItem(op)
            item.setCheckState(QtCore.Qt.CheckState.Checked)
            self.dialog.settingsOpsList.addItem(item)

        self.dialog.toolsList.clear()
        for tc in sorted(job.ToolController, key=lambda o: o.Label):
            item = QtGui.QListWidgetItem(tc.Label)
            item.setData(self.DataObject, tc)
            item.setCheckState(QtCore.Qt.CheckState.Checked)
            self.dialog.toolsList.addItem(item)
 def getreversed(self, edges):
     """
     Reverses the edge array and the direction of each edge
     """
     outedges = []
     for edge in reversed(edges):
         # reverse the start and end points
         startPoint = edge.valueAt(edge.LastParameter)
         endPoint = edge.valueAt(edge.FirstParameter)
         if type(edge.Curve) == Part.Line or type(edge.Curve) == Part.LineSegment:
             outedges.append(Part.makeLine(startPoint, endPoint))
         elif type(edge.Curve) == Part.Circle:
             arcMid = edge.valueAt((edge.FirstParameter + edge.LastParameter) / 2)
             outedges.append(Part.Arc(startPoint, arcMid, endPoint).toShape())
         else:
             PathLog.error("Edge should not be helix")
     return outedges
    def updateToolController(self):
        tc = self.obj
        try:
            tc.Label = self.form.tcName.text()
            tc.ToolNumber = self.form.tcNumber.value()
            self.horizFeed.updateProperty()
            self.vertFeed.updateProperty()
            self.horizRapid.updateProperty()
            self.vertRapid.updateProperty()
            tc.SpindleSpeed = self.form.spindleSpeed.value()
            tc.SpindleDir = self.form.spindleDirection.currentText()

            self.editor.updateTool()
            tc.Tool = self.editor.tool

        except Exception as e:
            PathLog.error(translate("PathToolController", "Error updating TC: %s") % e)
예제 #29
0
파일: PathJobGui.py 프로젝트: itain/FreeCAD
 def getFields(self, obj, fields = ['xneg', 'xpos', 'yneg', 'ypos', 'zneg', 'zpos']):
     PathLog.track(obj.Label, fields)
     if self.IsStock(obj):
         if 'xneg' in fields:
             obj.Stock.ExtXneg = FreeCAD.Units.Quantity(self.form.stockExtXneg.text())
         if 'xpos' in fields:
             obj.Stock.ExtXpos = FreeCAD.Units.Quantity(self.form.stockExtXpos.text())
         if 'yneg' in fields:
             obj.Stock.ExtYneg = FreeCAD.Units.Quantity(self.form.stockExtYneg.text())
         if 'ypos' in fields:
             obj.Stock.ExtYpos = FreeCAD.Units.Quantity(self.form.stockExtYpos.text())
         if 'zneg' in fields:
             obj.Stock.ExtZneg = FreeCAD.Units.Quantity(self.form.stockExtZneg.text())
         if 'zpos' in fields:
             obj.Stock.ExtZpos = FreeCAD.Units.Quantity(self.form.stockExtZpos.text())
     else:
         PathLog.error(translate('PathJob', 'Stock not from Base bound box!'))
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
예제 #31
0
 def selectionSupportedAsBaseGeometry(self, selection, ignoreErrors):
     if len(selection) != 1:
         if not ignoreErrors:
             PathLog.error(
                 translate(
                     "PathProject", "Please select %s from a single solid" %
                     self.featureName()))
         return False
     sel = selection[0]
     if sel.HasSubObjects:
         if not self.supportsVertexes(
         ) and selection[0].SubObjects[0].ShapeType == "Vertex":
             if not ignoreErrors:
                 PathLog.error(
                     translate("PathProject", "Vertexes are not supported"))
             return False
         if not self.supportsEdges(
         ) and selection[0].SubObjects[0].ShapeType == "Edge":
             if not ignoreErrors:
                 PathLog.error(
                     translate("PathProject", "Edges are not supported"))
             return False
         if not self.supportsFaces(
         ) and selection[0].SubObjects[0].ShapeType == "Face":
             if not ignoreErrors:
                 PathLog.error(
                     translate("PathProject", "Faces are not supported"))
             return False
     else:
         if not self.supportsPanels() or not 'Panel' in sel.Object.Name:
             if not ignoreErrors:
                 PathLog.error(
                     translate(
                         "PathProject", "Please select %s of a solid" %
                         self.featureName()))
             return False
     return True
예제 #32
0
def addToJob(obj, jobname=None):
    '''adds a path object to a job
    obj = obj
    jobname = None'''
    PathLog.track(jobname)
    if jobname is not None:
        jobs = GetJobs(jobname)
        if len(jobs) == 1:
            job = jobs[0]
        else:
            PathLog.error(translate("Path", "Didn't find job %s") % jobname)
            return None
    else:
        jobs = GetJobs()
        if len(jobs) == 0 and UserInput:
            job = UserInput.createJob()
        elif len(jobs) == 1:
            job = jobs[0]
        elif UserInput:
            job = UserInput.chooseJob(jobs)

    if obj and job:
        job.Proxy.addOperation(obj)
    return job
예제 #33
0
 def setFromTemplate(self, obj, template):
     '''setFromTemplate(obj, xmlItem) ... extract properties from xmlItem and assign to receiver.'''
     PathLog.track(obj.Name, template)
     if template.get(ToolControllerTemplate.Version) and 1 == int(template.get(ToolControllerTemplate.Version)):
         if template.get(ToolControllerTemplate.Label):
             obj.Label = template.get(ToolControllerTemplate.Label)
         if template.get(ToolControllerTemplate.VertFeed):
             obj.VertFeed = template.get(ToolControllerTemplate.VertFeed)
         if template.get(ToolControllerTemplate.HorizFeed):
             obj.HorizFeed = template.get(ToolControllerTemplate.HorizFeed)
         if template.get(ToolControllerTemplate.VertRapid):
             obj.VertRapid = template.get(ToolControllerTemplate.VertRapid)
         if template.get(ToolControllerTemplate.HorizRapid):
             obj.HorizRapid = template.get(ToolControllerTemplate.HorizRapid)
         if template.get(ToolControllerTemplate.SpindleSpeed):
             obj.SpindleSpeed = float(template.get(ToolControllerTemplate.SpindleSpeed))
         if template.get(ToolControllerTemplate.SpindleDir):
             obj.SpindleDir = template.get(ToolControllerTemplate.SpindleDir)
         if template.get(ToolControllerTemplate.ToolNumber):
             obj.ToolNumber = int(template.get(ToolControllerTemplate.ToolNumber))
         if template.get(ToolControllerTemplate.Tool):
             obj.Tool.setFromTemplate(template.get(ToolControllerTemplate.Tool))
     else:
         PathLog.error(translate('PathToolController', "Unsupported PathToolController template version %s") % template.get(ToolControllerTemplate.Version))
예제 #34
0
파일: PathOpGui.py 프로젝트: SilRoj/FreeCAD
    def addBaseGeometry(self, selection):
        PathLog.track(selection)
        if len(selection) != 1:
            PathLog.error(translate("PathProject", "Please select %s from a single solid" % self.featureName()))
            return False
        sel = selection[0]
        if sel.HasSubObjects:
            if not self.supportsVertexes() and selection[0].SubObjects[0].ShapeType == "Vertex":
                PathLog.error(translate("PathProject", "Vertexes are not supported"))
                return False
            if not self.supportsEdges() and selection[0].SubObjects[0].ShapeType == "Edge":
                PathLog.error(translate("PathProject", "Edges are not supported"))
                return False
            if not self.supportsFaces() and selection[0].SubObjects[0].ShapeType == "Face":
                PathLog.error(translate("PathProject", "Faces are not supported"))
                return False
        else:
            if not self.supportsPanels() or not 'Panel' in sel.Object.Name:
                PathLog.error(translate("PathProject", "Please select %s of a solid" % self.featureName()))
                return False

        for sub in sel.SubElementNames:
            self.obj.Proxy.addBase(self.obj, sel.Object, sub)
        return True
예제 #35
0
    def opSetDefaultValues(self, obj, job):
        """opSetDefaultValues(obj) ... base implementation, do not overwrite.
        The base implementation sets the depths and heights based on the
        areaOpShapeForDepths() return value.
        Do not overwrite, overwrite areaOpSetDefaultValues(obj, job) instead."""
        PathLog.debug("opSetDefaultValues(%s, %s)" % (obj.Label, job.Label))

        if PathOp.FeatureDepths & self.opFeatures(obj):
            try:
                shape = self.areaOpShapeForDepths(obj, job)
            except Exception as ee:
                PathLog.error(ee)
                shape = None

            # Set initial start and final depths
            if shape is None:
                PathLog.debug("shape is None")
                startDepth = 1.0
                finalDepth = 0.0
            else:
                bb = job.Stock.Shape.BoundBox
                startDepth = bb.ZMax
                finalDepth = bb.ZMin

            # obj.StartDepth.Value = startDepth
            # obj.FinalDepth.Value = finalDepth
            obj.OpStartDepth.Value = startDepth
            obj.OpFinalDepth.Value = finalDepth

            PathLog.debug(
                "Default OpDepths are Start: {}, and Final: {}".format(
                    obj.OpStartDepth.Value, obj.OpFinalDepth.Value))
            PathLog.debug("Default Depths are Start: {}, and Final: {}".format(
                startDepth, finalDepth))

        self.areaOpSetDefaultValues(obj, job)
예제 #36
0
    def setup(self, obj, generate=False):
        PathLog.debug("setup")
        self.obj = obj
        try:
            pathData = PathData(obj)
        except ValueError:
            PathLog.error(
                translate(
                    "Path_DressupTag",
                    "Cannot insert holding tags for this path - please select a Profile path"
                ) + "\n")
            return None

        self.toolRadius = PathDressup.toolController(
            obj.Base).Tool.Diameter / 2
        self.pathData = pathData
        if generate:
            obj.Height = self.pathData.defaultTagHeight()
            obj.Width = self.pathData.defaultTagWidth()
            obj.Angle = self.pathData.defaultTagAngle()
            obj.Radius = self.pathData.defaultTagRadius()
            count = HoldingTagPreferences.defaultCount()
            self.generateTags(obj, count)
        return self.pathData
예제 #37
0
def addToJob(obj, jobname=None):
    '''adds a path object to a job
    obj = obj
    jobname = None'''
    PathLog.track(jobname)
    if jobname is not None:
        jobs = GetJobs(jobname)
        if len(jobs) == 1:
            job = jobs[0]
        else:
            PathLog.error(translate("Path", "Didn't find job %s") % jobname)
            return None
    else:
        jobs = GetJobs()
        if len(jobs) == 0:
            job = PathJobCmd.CommandJobCreate().Activated()
        elif len(jobs) == 1:
            job = jobs[0]
        else:
            # form = FreeCADGui.PySideUic.loadUi(FreeCAD.getHomePath() + "Mod/Path/DlgJobChooser.ui")
            form = FreeCADGui.PySideUic.loadUi(":/panels/DlgJobChooser.ui")
            mylist = [i.Label for i in jobs]
            form.cboProject.addItems(mylist)
            r = form.exec_()
            if r is False:
                return None
            else:
                print(form.cboProject.currentText())
                job = [
                    i for i in jobs
                    if i.Label == form.cboProject.currentText()
                ][0]

    if obj and job:
        job.Proxy.addOperation(obj)
    return job
    def tooltableFromAttrs(self, stringattrs):
        if stringattrs.get("Version") and 1 == int(stringattrs["Version"]):

            tt = Path.Tooltable()
            tt.Version = 1
            tt.Name = self.getNextToolTableName()

            if stringattrs.get("Version"):
                tt.Version = stringattrs.get("Version")

            if stringattrs.get("TableName"):
                tt.Name = stringattrs.get("TableName")
                if any(table.Name == tt.Name for table in self.toolTables):
                    tt.Name = self.getNextToolTableName(tt.Name)

            for key, attrs in PathUtil.keyValueIter(stringattrs["Tools"]):
                tool = Path.Tool()
                tool.Name = str(attrs["name"])
                tool.ToolType = str(attrs["tooltype"])
                tool.Material = str(attrs["material"])
                tool.Diameter = float(attrs["diameter"])
                tool.LengthOffset = float(attrs["lengthOffset"])
                tool.FlatRadius = float(attrs["flatRadius"])
                tool.CornerRadius = float(attrs["cornerRadius"])
                tool.CuttingEdgeAngle = float(attrs["cuttingEdgeAngle"])
                tool.CuttingEdgeHeight = float(attrs["cuttingEdgeHeight"])
                tt.setTool(int(key), tool)

            return tt
        else:
            PathLog.error(
                translate(
                    "PathToolLibraryManager",
                    "Unsupported Path tooltable template version %s",
                ) % stringattrs.get("Version"))
        return None
예제 #39
0
    def opExecute(self, obj):
        '''opExecute(obj) ... process engraving operation'''
        PathLog.track()

        if not hasattr(obj.ToolController.Tool, "CuttingEdgeAngle"):
            PathLog.error(translate("Path_Vcarve", "VCarve requires an engraving cutter with CuttingEdgeAngle"))

        if obj.ToolController.Tool.CuttingEdgeAngle >= 180.0:
            PathLog.error(translate("Path_Vcarve", "Engraver Cutting Edge Angle must be < 180 degrees."))
            return

        try:
            faces = []

            for base in obj.BaseShapes:
                faces.extend(base.Shape.Faces)

            for base in obj.Base:
                for sub in base[1]:
                    shape = getattr(base[0].Shape, sub)
                    if isinstance(shape, Part.Face):
                        faces.append(shape)

            if not faces:
                for model in self.model:
                    if model.isDerivedFrom('Sketcher::SketchObject') or model.isDerivedFrom('Part::Part2DObject'):
                        faces.extend(model.Shape.Faces)

            if faces:
                self.buildPathMedial(obj, faces)
            else:
                PathLog.error(translate('PathVcarve', 'The Job Base Object has no engraveable element. Engraving operation will produce no output.'))

        except Exception as e:
            #PathLog.error(e)
            #traceback.print_exc()
            PathLog.error(translate('PathVcarve', 'Error processing Base object. Engraving operation will produce no output.'))
예제 #40
0
    def Activated(self):

        # check that the selection contains exactly what we want
        selection = FreeCADGui.Selection.getSelection()
        if len(selection) != 1:
            PathLog.error(
                translate("Path_DressupRampEntry", "Please select one path object")
                + "\n"
            )
            return
        baseObject = selection[0]
        if not baseObject.isDerivedFrom("Path::Feature"):
            PathLog.error(
                translate("Path_DressupRampEntry", "The selected object is not a path")
                + "\n"
            )
            return
        if baseObject.isDerivedFrom("Path::FeatureCompoundPython"):
            PathLog.error(
                translate("Path_DressupRampEntry", "Please select a Profile object")
            )
            return

        # everything ok!
        FreeCAD.ActiveDocument.openTransaction("Create RampEntry Dress-up")
        FreeCADGui.addModule("PathScripts.PathDressupRampEntry")
        FreeCADGui.addModule("PathScripts.PathUtils")
        FreeCADGui.doCommand(
            'obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", "RampEntryDressup")'
        )
        FreeCADGui.doCommand(
            "dbo = PathScripts.PathDressupRampEntry.ObjectDressup(obj)"
        )
        FreeCADGui.doCommand("base = FreeCAD.ActiveDocument." + selection[0].Name)
        FreeCADGui.doCommand("job = PathScripts.PathUtils.findParentJob(base)")
        FreeCADGui.doCommand("obj.Base = base")
        FreeCADGui.doCommand("job.Proxy.addOperation(obj, base)")
        FreeCADGui.doCommand(
            "obj.ViewObject.Proxy = PathScripts.PathDressupRampEntry.ViewProviderDressup(obj.ViewObject)"
        )
        FreeCADGui.doCommand(
            "Gui.ActiveDocument.getObject(base.Name).Visibility = False"
        )
        FreeCADGui.doCommand("dbo.setup(obj)")
        FreeCAD.ActiveDocument.commitTransaction()
        FreeCAD.ActiveDocument.recompute()
예제 #41
0
    def Activated(self):

        # check that the selection contains exactly what we want
        selection = FreeCADGui.Selection.getSelection()
        if len(selection) != 1:
            PathLog.error(translate("Please select one path object\n"))
            return
        baseObject = selection[0]
        if not baseObject.isDerivedFrom("Path::Feature"):
            PathLog.error(translate("The selected object is not a path\n"))
            return
        if baseObject.isDerivedFrom("Path::FeatureCompoundPython"):
            PathLog.error(translate("Please select a Profile object"))
            return

        # everything ok!
        FreeCAD.ActiveDocument.openTransaction(
            translate("Create RampEntry Dress-up"))
        FreeCADGui.addModule("PathScripts.PathDressUpRampEntry")
        FreeCADGui.addModule("PathScripts.PathUtils")
        FreeCADGui.doCommand(
            'obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", "RampEntryDressup")'
        )
        FreeCADGui.doCommand(
            'dbo = PathScripts.PathDressupRampEntry.ObjectDressup(obj)')
        FreeCADGui.doCommand('obj.Base = FreeCAD.ActiveDocument.' +
                             selection[0].Name)
        FreeCADGui.doCommand(
            'PathScripts.PathDressupRampEntry.ViewProviderDressup(obj.ViewObject)'
        )
        FreeCADGui.doCommand('PathScripts.PathUtils.addToJob(obj)')
        FreeCADGui.doCommand(
            'Gui.ActiveDocument.getObject(obj.Base.Name).Visibility = False')
        FreeCADGui.doCommand(
            'obj.ToolController = PathScripts.PathUtils.findToolController(obj)'
        )
        FreeCADGui.doCommand('dbo.setup(obj)')
        FreeCAD.ActiveDocument.commitTransaction()
        FreeCAD.ActiveDocument.recompute()
예제 #42
0
    def opSetDefaultValues(self, obj, job):
        '''opSetDefaultValues(obj) ... base implementation, do not overwrite.
        The base implementation sets the depths and heights based on the
        areaOpShapeForDepths() return value.
        Do not overwrite, overwrite areaOpSetDefaultValues(obj, job) instead.'''
        PathLog.debug("opSetDefaultValues(%s, %s)" % (obj.Label, job.Label))

        # Initial setting for EnableRotation is taken from Job settings/SetupSheet
        # User may override on per-operation basis as needed.
        if hasattr(job.SetupSheet, 'SetupEnableRotation'):
            obj.EnableRotation = job.SetupSheet.SetupEnableRotation
        else:
            obj.EnableRotation = 'Off'
        PathLog.debug("opSetDefaultValues(): Enable Rotation: {}".format(
            obj.EnableRotation))

        if PathOp.FeatureDepths & self.opFeatures(obj):
            try:
                shape = self.areaOpShapeForDepths(obj, job)
            except Exception as ee:  # pylint: disable=broad-except
                PathLog.error(ee)
                shape = None

            # Set initial start and final depths
            if shape is None:
                PathLog.debug("shape is None")
                startDepth = 1.0
                finalDepth = 0.0
            else:
                bb = job.Stock.Shape.BoundBox
                startDepth = bb.ZMax
                finalDepth = bb.ZMin

            # Adjust start and final depths if rotation is enabled
            if obj.EnableRotation != 'Off':
                self.initWithRotation = True
                self.stockBB = PathUtils.findParentJob(
                    obj).Stock.Shape.BoundBox  # pylint: disable=attribute-defined-outside-init
                # Calculate rotational distances/radii
                opHeights = self.opDetermineRotationRadii(
                    obj
                )  # return is list with tuples [(xRotRad, yRotRad, zRotRad), (clrOfst, safOfset)]
                (xRotRad, yRotRad, zRotRad) = opHeights[0]  # pylint: disable=unused-variable
                PathLog.debug("opHeights[0]: " + str(opHeights[0]))
                PathLog.debug("opHeights[1]: " + str(opHeights[1]))

                if obj.EnableRotation == 'A(x)':
                    startDepth = xRotRad
                if obj.EnableRotation == 'B(y)':
                    startDepth = yRotRad
                else:
                    startDepth = max(xRotRad, yRotRad)
                finalDepth = -1 * startDepth

            obj.StartDepth.Value = startDepth
            obj.FinalDepth.Value = finalDepth
            obj.OpStartDepth.Value = startDepth
            obj.OpFinalDepth.Value = finalDepth

            PathLog.debug(
                "Default OpDepths are Start: {}, and Final: {}".format(
                    obj.OpStartDepth.Value, obj.OpFinalDepth.Value))
            PathLog.debug("Default Depths are Start: {}, and Final: {}".format(
                startDepth, finalDepth))

        self.areaOpSetDefaultValues(obj, job)
예제 #43
0
    def helix_cut(self, obj, x0, y0, r_out, r_in, dr):
        '''helix_cut(obj, x0, y0, r_out, r_in, dr) ... generate helix commands for specified hole.
            x0, y0: coordinates of center
            r_out, r_in: outer and inner radius of the hole
            dr: step over radius value'''
        from numpy import ceil, linspace

        if (obj.StartDepth.Value <= obj.FinalDepth.Value):
            return

        out = "(helix_cut <{0}, {1}>, {2})".format(
            x0, y0, ", ".join(
                map(str, (r_out, r_in, dr, obj.StartDepth.Value,
                          obj.FinalDepth.Value, obj.StepDown.Value,
                          obj.SafeHeight.Value, self.radius, self.vertFeed,
                          self.horizFeed, obj.Direction, obj.StartSide))))

        nz = max(
            int(
                ceil((obj.StartDepth.Value - obj.FinalDepth.Value) /
                     obj.StepDown.Value)), 2)
        zi = linspace(obj.StartDepth.Value, obj.FinalDepth.Value, 2 * nz + 1)

        def xyz(x=None, y=None, z=None):
            out = ""
            if x is not None:
                out += " X" + fmt(x)
            if y is not None:
                out += " Y" + fmt(y)
            if z is not None:
                out += " Z" + fmt(z)
            return out

        def rapid(x=None, y=None, z=None):
            return "G0" + xyz(x, y, z) + "\n"

        def F(f=None):
            return (" F" + fmt(f) if f else "")

        def feed(x=None, y=None, z=None, f=None):
            return "G1" + xyz(x, y, z) + F(f) + "\n"

        def arc(x, y, i, j, z, f):
            if obj.Direction == "CW":
                code = "G2"
            elif obj.Direction == "CCW":
                code = "G3"
            return code + " I" + fmt(i) + " J" + fmt(j) + " X" + fmt(
                x) + " Y" + fmt(y) + " Z" + fmt(z) + F(f) + "\n"

        def helix_cut_r(r):
            arc_cmd = 'G2' if obj.Direction == 'CW' else 'G3'
            out = ""
            out += rapid(x=x0 + r, y=y0)
            self.commandlist.append(
                Path.Command('G0', {
                    'X': x0 + r,
                    'Y': y0,
                    'F': self.horizRapid
                }))
            out += rapid(z=obj.StartDepth.Value + 2 * self.radius)
            self.commandlist.append(
                Path.Command('G0', {
                    'Z': obj.SafeHeight.Value,
                    'F': self.vertRapid
                }))
            out += feed(z=obj.StartDepth.Value, f=self.vertFeed)
            self.commandlist.append(
                Path.Command('G1', {
                    'Z': obj.StartDepth.Value,
                    'F': self.vertFeed
                }))
            # z = obj.FinalDepth.Value
            for i in range(1, nz + 1):
                out += arc(x0 - r,
                           y0,
                           i=-r,
                           j=0.0,
                           z=zi[2 * i - 1],
                           f=self.horizFeed)
                self.commandlist.append(
                    Path.Command(
                        arc_cmd, {
                            'X': x0 - r,
                            'Y': y0,
                            'Z': zi[2 * i - 1],
                            'I': -r,
                            'J': 0.0,
                            'F': self.horizFeed
                        }))
                out += arc(x0 + r,
                           y0,
                           i=r,
                           j=0.0,
                           z=zi[2 * i],
                           f=self.horizFeed)
                self.commandlist.append(
                    Path.Command(
                        arc_cmd, {
                            'X': x0 + r,
                            'Y': y0,
                            'Z': zi[2 * i],
                            'I': r,
                            'J': 0.0,
                            'F': self.horizFeed
                        }))
            out += arc(x0 - r,
                       y0,
                       i=-r,
                       j=0.0,
                       z=obj.FinalDepth.Value,
                       f=self.horizFeed)
            self.commandlist.append(
                Path.Command(
                    arc_cmd, {
                        'X': x0 - r,
                        'Y': y0,
                        'Z': obj.FinalDepth.Value,
                        'I': -r,
                        'J': 0.0,
                        'F': self.horizFeed
                    }))
            out += arc(x0 + r,
                       y0,
                       i=r,
                       j=0.0,
                       z=obj.FinalDepth.Value,
                       f=self.horizFeed)
            self.commandlist.append(
                Path.Command(
                    arc_cmd, {
                        'X': x0 + r,
                        'Y': y0,
                        'Z': obj.FinalDepth.Value,
                        'I': r,
                        'J': 0.0,
                        'F': self.horizFeed
                    }))
            out += feed(z=obj.StartDepth.Value + 2 * self.radius,
                        f=self.vertFeed)
            out += rapid(z=obj.SafeHeight.Value)
            self.commandlist.append(
                Path.Command('G0', {
                    'Z': obj.SafeHeight.Value,
                    'F': self.vertRapid
                }))
            return out

        assert (r_out > 0.0)
        assert (r_in >= 0.0)

        msg = None
        if r_out < 0.0:
            msg = "r_out < 0"
        elif r_in > 0 and r_out - r_in < 2 * self.radius:
            msg = "r_out - r_in = {0} is < tool diameter of {1}".format(
                r_out - r_in, 2 * self.radius)
        elif r_in == 0.0 and not r_out > self.radius / 2.:
            msg = "Cannot drill a hole of diameter {0} with a tool of diameter {1}".format(
                2 * r_out, 2 * self.radius)
        elif obj.StartSide not in ["Inside", "Outside"]:
            msg = "Invalid value for parameter 'obj.StartSide'"

        if msg:
            out += "(ERROR: Hole at {0}:".format(
                (x0, y0, obj.StartDepth.Value)) + msg + ")\n"
            PathLog.error("PathHelix: Hole at {0}:".format(
                (x0, y0, obj.StartDepth.Value)) + msg + "\n")
            return out

        if r_in > 0:
            out += "(annulus mode)\n"
            r_out = r_out - self.radius
            r_in = r_in + self.radius
            if abs((r_out - r_in) / dr) < 1e-5:
                radii = [(r_out + r_in) / 2]
            else:
                nr = max(int(ceil((r_out - r_in) / dr)), 2)
                radii = linspace(r_out, r_in, nr)
        elif r_out <= 2 * dr:
            out += "(single helix mode)\n"
            radii = [r_out - self.radius]
            assert (radii[0] > 0)
        else:
            out += "(full hole mode)\n"
            r_out = r_out - self.radius
            r_in = dr / 2

            nr = max(1 + int(ceil((r_out - r_in) / dr)), 2)
            radii = linspace(r_out, r_in, nr)
            assert (all(radii > 0))

        if obj.StartSide == "Inside":
            radii = radii[::-1]

        for r in radii:
            out += "(radius {0})\n".format(r)
            out += helix_cut_r(r)

        return out
예제 #44
0
파일: PathJob.py 프로젝트: Roy-043/FreeCAD
 def errorMessage(grp, job):
     PathLog.error("{} corrupt in {} job.".format(grp, job.Name))
예제 #45
0
    def calculateAdaptivePocket(self, obj, base, subObjTups):
        '''calculateAdaptivePocket(obj, base, subObjTups)
        Orient multiple faces around common facial center of mass.
        Identify edges that are connections for adjacent faces.
        Attempt to separate unconnected edges into top and bottom loops of the pocket.
        Trim the top and bottom of the pocket if available and requested.
        return: tuple with pocket shape information'''
        low = []
        high = []
        removeList = []
        Faces = []
        allEdges = []
        makeHighFace = 0
        tryNonPlanar = False
        isHighFacePlanar = True
        isLowFacePlanar = True
        faceType = 0

        for (sub, face) in subObjTups:
            Faces.append(face)

        # identify max and min face heights for top loop
        (zmin, zmax) = self.getMinMaxOfFaces(Faces)

        # Order faces around common center of mass
        subObjTups = self.orderFacesAroundCenterOfMass(subObjTups)
        # find connected edges and map to edge names of base
        (connectedEdges, touching) = self.findSharedEdges(subObjTups)
        (low, high) = self.identifyUnconnectedEdges(subObjTups, touching)

        if len(high) > 0 and obj.AdaptivePocketStart is True:
            # attempt planar face with top edges of pocket
            allEdges = []
            makeHighFace = 0
            tryNonPlanar = False
            for (sub, face, ei) in high:
                allEdges.append(face.Edges[ei])

            (hzmin, hzmax) = self.getMinMaxOfFaces(allEdges)

            try:
                highFaceShape = Part.Face(
                    Part.Wire(Part.__sortEdges__(allEdges)))
            except Exception as ee:
                PathLog.warning(ee)
                PathLog.error(
                    translate(
                        "Path",
                        "A planar adaptive start is unavailable. The non-planar will be attempted."
                    ))
                tryNonPlanar = True
            else:
                makeHighFace = 1

            if tryNonPlanar is True:
                try:
                    highFaceShape = Part.makeFilledFace(
                        Part.__sortEdges__(allEdges))  # NON-planar face method
                except Exception as eee:
                    PathLog.warning(eee)
                    PathLog.error(
                        translate(
                            "Path",
                            "The non-planar adaptive start is also unavailable."
                        ) + "(1)")
                    isHighFacePlanar = False
                else:
                    makeHighFace = 2

            if makeHighFace > 0:
                FreeCAD.ActiveDocument.addObject('Part::Feature',
                                                 'topEdgeFace')
                highFace = FreeCAD.ActiveDocument.ActiveObject
                highFace.Shape = highFaceShape
                removeList.append(highFace.Name)

            # verify non-planar face is within high edge loop Z-boundaries
            if makeHighFace == 2:
                mx = hzmax + obj.StepDown.Value
                mn = hzmin - obj.StepDown.Value
                if highFace.Shape.BoundBox.ZMax > mx or highFace.Shape.BoundBox.ZMin < mn:
                    PathLog.warning("ZMaxDiff: {};  ZMinDiff: {}".format(
                        highFace.Shape.BoundBox.ZMax - mx,
                        highFace.Shape.BoundBox.ZMin - mn))
                    PathLog.error(
                        translate(
                            "Path",
                            "The non-planar adaptive start is also unavailable."
                        ) + "(2)")
                    isHighFacePlanar = False
                    makeHighFace = 0
        else:
            isHighFacePlanar = False

        if len(low) > 0 and obj.AdaptivePocketFinish is True:
            # attempt planar face with bottom edges of pocket
            allEdges = []
            for (sub, face, ei) in low:
                allEdges.append(face.Edges[ei])

            # (lzmin, lzmax) = self.getMinMaxOfFaces(allEdges)

            try:
                lowFaceShape = Part.Face(
                    Part.Wire(Part.__sortEdges__(allEdges)))
                # lowFaceShape = Part.makeFilledFace(Part.__sortEdges__(allEdges))  # NON-planar face method
            except Exception as ee:
                PathLog.error(ee)
                PathLog.error("An adaptive finish is unavailable.")
                isLowFacePlanar = False
            else:
                FreeCAD.ActiveDocument.addObject('Part::Feature',
                                                 'bottomEdgeFace')
                lowFace = FreeCAD.ActiveDocument.ActiveObject
                lowFace.Shape = lowFaceShape
                removeList.append(lowFace.Name)
        else:
            isLowFacePlanar = False

        # Start with a regular pocket envelope
        strDep = obj.StartDepth.Value
        finDep = obj.FinalDepth.Value
        cuts = []
        starts = []
        finals = []
        starts.append(obj.StartDepth.Value)
        finals.append(zmin)
        if obj.AdaptivePocketStart is True or len(subObjTups) == 1:
            strDep = zmax + obj.StepDown.Value
            starts.append(zmax + obj.StepDown.Value)

        finish_step = obj.FinishDepth.Value if hasattr(obj,
                                                       "FinishDepth") else 0.0
        depthparams = PathUtils.depth_params(
            clearance_height=obj.ClearanceHeight.Value,
            safe_height=obj.SafeHeight.Value,
            start_depth=strDep,
            step_down=obj.StepDown.Value,
            z_finish_step=finish_step,
            final_depth=finDep,
            user_depths=None)
        shape = Part.makeCompound(Faces)
        env = PathUtils.getEnvelope(base[0].Shape,
                                    subshape=shape,
                                    depthparams=depthparams)
        cuts.append(env.cut(base[0].Shape))

        # Might need to change to .cut(job.Stock.Shape) if pocket has no bottom
        # job = PathUtils.findParentJob(obj)
        # envBody = env.cut(job.Stock.Shape)

        if isHighFacePlanar is True and len(subObjTups) > 1:
            starts.append(hzmax + obj.StepDown.Value)
            # make shape to trim top of reg pocket
            strDep1 = obj.StartDepth.Value + (hzmax - hzmin)
            if makeHighFace == 1:
                # Planar face
                finDep1 = highFace.Shape.BoundBox.ZMin + obj.StepDown.Value
            else:
                # Non-Planar face
                finDep1 = hzmin + obj.StepDown.Value
            depthparams1 = PathUtils.depth_params(
                clearance_height=obj.ClearanceHeight.Value,
                safe_height=obj.SafeHeight.Value,
                start_depth=strDep1,
                step_down=obj.StepDown.Value,
                z_finish_step=finish_step,
                final_depth=finDep1,
                user_depths=None)
            envTop = PathUtils.getEnvelope(base[0].Shape,
                                           subshape=highFace.Shape,
                                           depthparams=depthparams1)
            cbi = len(cuts) - 1
            cuts.append(cuts[cbi].cut(envTop))

        if isLowFacePlanar is True and len(subObjTups) > 1:
            # make shape to trim top of pocket
            if makeHighFace == 1:
                # Planar face
                strDep2 = lowFace.Shape.BoundBox.ZMax
            else:
                # Non-Planar face
                strDep2 = hzmax
            finDep2 = obj.FinalDepth.Value
            depthparams2 = PathUtils.depth_params(
                clearance_height=obj.ClearanceHeight.Value,
                safe_height=obj.SafeHeight.Value,
                start_depth=strDep2,
                step_down=obj.StepDown.Value,
                z_finish_step=finish_step,
                final_depth=finDep2,
                user_depths=None)
            envBottom = PathUtils.getEnvelope(base[0].Shape,
                                              subshape=lowFace.Shape,
                                              depthparams=depthparams2)
            cbi = len(cuts) - 1
            cuts.append(cuts[cbi].cut(envBottom))

        # package pocket details into tuple
        sdi = len(starts) - 1
        fdi = len(finals) - 1
        cbi = len(cuts) - 1
        pocket = (cuts[cbi], False, '3DPocket', 0.0, 'X', starts[sdi],
                  finals[fdi])
        if FreeCAD.GuiUp:
            import FreeCADGui
            for rn in removeList:
                FreeCADGui.ActiveDocument.getObject(rn).Visibility = False

        for rn in removeList:
            FreeCAD.ActiveDocument.getObject(rn).purgeTouched()
            self.tempObjectNames.append(rn)
        return pocket
예제 #46
0
    def execute(self, obj):
        '''execute(obj) ... base implementation - do not overwrite!
        Verifies that the operation is assigned to a job and that the job also has a valid Base.
        It also sets the following instance variables that can and should be safely be used by
        implementation of opExecute():
            self.baseobject   ... Base object of the Job itself
            self.vertFeed     ... vertical feed rate of assigned tool
            self.vertRapid    ... vertical rapid rate of assigned tool
            self.horizFeed    ... horizontal feed rate of assigned tool
            self.horizRapid   ... norizontal rapid rate of assigned tool
            self.tool         ... the actual tool being used
            self.radius       ... the main radius of the tool being used
            self.commandlist  ... a list for collecting all commands produced by the operation

        Once everything is validated and above variables are set the implementation calls
        opExecute(obj) - which is expected to add the generated commands to self.commandlist
        Finally the base implementation adds a rapid move to clearance height and assigns
        the receiver's Path property from the command list.
        '''
        PathLog.track()

        if not obj.Active:
            path = Path.Path("(inactive operation)")
            obj.Path = path
            if obj.ViewObject:
                obj.ViewObject.Visibility = False
            return

        job = PathUtils.findParentJob(obj)
        if not job:
            PathLog.error(
                translate("Path", "No parent job found for operation."))
            return
        if not job.Base:
            PathLog.error(
                translate("Path", "Parent job %s doesn't have a base object") %
                job.Label)
            return
        self.baseobject = job.Base

        if FeatureTool & self.opFeatures(obj):
            tc = obj.ToolController
            if tc is None or tc.ToolNumber == 0:
                FreeCAD.Console.PrintError(
                    "No Tool Controller is selected. We need a tool to build a Path."
                )
                return
            else:
                self.vertFeed = tc.VertFeed.Value
                self.horizFeed = tc.HorizFeed.Value
                self.vertRapid = tc.VertRapid.Value
                self.horizRapid = tc.HorizRapid.Value
                tool = tc.Proxy.getTool(tc)
                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
                    self.tool = tool

        self.commandlist = []
        self.commandlist.append(Path.Command("(%s)" % obj.Label))
        if obj.Comment:
            self.commandlist.append(Path.Command("(%s)" % obj.Comment))

        result = self.opExecute(obj)

        if FeatureHeights & self.opFeatures(obj):
            # Let's finish by rapid to clearance...just for safety
            self.commandlist.append(
                Path.Command("G0", {"Z": obj.ClearanceHeight.Value}))

        path = Path.Path(self.commandlist)
        obj.Path = path
        return result
예제 #47
0
    def areaOpShapes(self, obj):
        '''areaOpShapes(obj) ... return shapes representing the solids to be removed.'''
        PathLog.track()

        if obj.Base:
            PathLog.debug("base items exist.  Processing...")
            self.removalshapes = []
            self.horiz = []
            vertical = []
            for o in obj.Base:
                PathLog.debug("Base item: {}".format(o))
                base = o[0]
                for sub in o[1]:
                    if "Face" in sub:
                        face = base.Shape.getElement(sub)
                        if type(face.Surface) == Part.Plane and PathGeom.isVertical(face.Surface.Axis):
                            # it's a flat horizontal face
                            self.horiz.append(face)
                        elif type(face.Surface) == Part.Cylinder and PathGeom.isVertical(face.Surface.Axis):
                            # vertical cylinder wall
                            if any(e.isClosed() for e in face.Edges):
                                # complete cylinder
                                circle = Part.makeCircle(face.Surface.Radius, face.Surface.Center)
                                disk = Part.Face(Part.Wire(circle))
                                self.horiz.append(disk)
                            else:
                                # partial cylinder wall
                                vertical.append(face)
                        elif type(face.Surface) == Part.Plane and PathGeom.isHorizontal(face.Surface.Axis):
                            vertical.append(face)
                        else:
                            PathLog.error(translate('PathPocket', "Pocket does not support shape %s.%s") % (base.Label, sub))

            self.vertical = PathGeom.combineConnectedShapes(vertical)
            self.vWires = [TechDraw.findShapeOutline(shape, 1, FreeCAD.Vector(0, 0, 1)) for shape in self.vertical]
            for wire in self.vWires:
                w = PathGeom.removeDuplicateEdges(wire)
                face = Part.Face(w)
                face.tessellate(0.1)
                if PathGeom.isRoughly(face.Area, 0):
                    PathLog.error(translate('PathPocket', 'Vertical faces do not form a loop - ignoring'))
                else:
                    self.horiz.append(face)


            # move all horizontal faces to FinalDepth
            for f in self.horiz:
                f.translate(FreeCAD.Vector(0, 0, obj.FinalDepth.Value - f.BoundBox.ZMin))

            # check all faces and see if they are touching/overlapping and combine those into a compound
            self.horizontal = []
            for shape in PathGeom.combineConnectedShapes(self.horiz):
                shape.sewShape()
                shape.tessellate(0.1)
                if obj.UseOutline:
                    wire = TechDraw.findShapeOutline(shape, 1, FreeCAD.Vector(0, 0, 1))
                    wire.translate(FreeCAD.Vector(0, 0, obj.FinalDepth.Value - wire.BoundBox.ZMin))
                    self.horizontal.append(Part.Face(wire))
                else:
                    self.horizontal.append(shape)

            # extrude all faces up to StartDepth and those are the removal shapes
            extent = FreeCAD.Vector(0, 0, obj.StartDepth.Value - obj.FinalDepth.Value)
            self.removalshapes = [(face.extrude(extent), False) for face in self.horizontal]

        else:  # process the job base object as a whole
            PathLog.debug("processing the whole job base object")
            self.outline = Part.Face(TechDraw.findShapeOutline(self.baseobject.Shape, 1, FreeCAD.Vector(0, 0, 1)))
            stockBB = self.stock.Shape.BoundBox

            self.outline.translate(FreeCAD.Vector(0, 0, stockBB.ZMin - 1))
            self.body  = self.outline.extrude(FreeCAD.Vector(0, 0, stockBB.ZLength + 2))
            self.removalshapes = [(self.stock.Shape.cut(self.body), False)]

        for (shape,hole) in self.removalshapes:
            shape.tessellate(0.1)

        if self.removalshapes:
            obj.removalshape = self.removalshapes[0][0]
        return self.removalshapes
예제 #48
0
    def libararySaveLinuxCNC(self, path):
        # linuxcnc line template
        LIN = "T{} P{} X{} Y{} Z{} A{} B{} C{} U{} V{} W{} D{} I{} J{} Q{}; {}"
        with open(path, "w") as fp:
            fp.write(";\n")

            for row in range(self.toolModel.rowCount()):
                toolNr = self.toolModel.data(self.toolModel.index(row, 0),
                                             PySide.QtCore.Qt.EditRole)
                toolPath = self.toolModel.data(self.toolModel.index(row, 0),
                                               _PathRole)

                bit = PathToolBit.Factory.CreateFrom(toolPath)
                if bit:
                    PathLog.track(bit)

                    pocket = bit.Pocket if hasattr(bit, "Pocket") else "0"
                    xoffset = bit.Xoffset if hasattr(bit, "Xoffset") else "0"
                    yoffset = bit.Yoffset if hasattr(bit, "Yoffset") else "0"
                    zoffset = bit.Zoffset if hasattr(bit, "Zoffset") else "0"
                    aoffset = bit.Aoffset if hasattr(bit, "Aoffset") else "0"
                    boffset = bit.Boffset if hasattr(bit, "Boffset") else "0"
                    coffset = bit.Coffset if hasattr(bit, "Coffset") else "0"
                    uoffset = bit.Uoffset if hasattr(bit, "Uoffset") else "0"
                    voffset = bit.Voffset if hasattr(bit, "Voffset") else "0"
                    woffset = bit.Woffset if hasattr(bit, "Woffset") else "0"

                    diameter = (bit.Diameter.getUserPreferred()[0].split()[0]
                                if hasattr(bit, "Diameter") else "0")
                    frontangle = bit.FrontAngle if hasattr(
                        bit, "FrontAngle") else "0"
                    backangle = bit.BackAngle if hasattr(bit,
                                                         "BackAngle") else "0"
                    orientation = (bit.Orientation if hasattr(
                        bit, "Orientation") else "0")
                    remark = bit.Label

                    fp.write(
                        LIN.format(
                            toolNr,
                            pocket,
                            xoffset,
                            yoffset,
                            zoffset,
                            aoffset,
                            boffset,
                            coffset,
                            uoffset,
                            voffset,
                            woffset,
                            diameter,
                            frontangle,
                            backangle,
                            orientation,
                            remark,
                        ) + "\n")

                    FreeCAD.ActiveDocument.removeObject(bit.Name)

                else:
                    PathLog.error("Could not find tool #{} ".format(toolNr))
예제 #49
0
        def analyzeVerticalFaces(self, obj, vertTuples):
            hT = []
            # base = FreeCAD.ActiveDocument.getObject(self.modelName)

            # Separate elements, regroup by orientation (axis_angle combination)
            vTags = ['X34.2']
            vGrps = [[(2.3, 3.4, 'X')]]
            for tup in vertTuples:
                (face, sub, angle, axis, tag, strDep, finDep, trans) = tup
                if tag in vTags:
                    # Determine index of found string
                    i = 0
                    for orn in vTags:
                        if orn == tag:
                            break
                        i += 1
                    vGrps[i].append(tup)
                else:
                    vTags.append(tag)  # add orientation entry
                    vGrps.append([tup])  # add orientation entry
            # Remove temp elements
            vTags.pop(0)
            vGrps.pop(0)

            # check all faces in each axis_angle group
            shpList = []
            zmaxH = 0.0
            for o in range(0, len(vTags)):
                shpList = []
                zmaxH = vGrps[o][0].BoundBox.ZMax
                for (face, sub, angle, axis, tag, strDep, finDep,
                     trans) in vGrps[o]:
                    shpList.append(face)
                    # Identify tallest face to use as zMax
                    if face.BoundBox.ZMax > zmaxH:
                        zmaxH = face.BoundBox.ZMax
                # check all faces and see if they are touching/overlapping and combine those into a compound
                # Original Code in For loop
                self.vertical = PathGeom.combineConnectedShapes(shpList)
                self.vWires = [
                    TechDraw.findShapeOutline(shape, 1,
                                              FreeCAD.Vector(0, 0, 1))
                    for shape in self.vertical
                ]
                for wire in self.vWires:
                    w = PathGeom.removeDuplicateEdges(wire)
                    face = Part.Face(w)
                    face.tessellate(0.05)
                    if PathGeom.isRoughly(face.Area, 0):
                        PathLog.error(
                            translate(
                                'PathPocket',
                                'Vertical faces do not form a loop - ignoring')
                        )
                    else:
                        strDep = zmaxH + self.leadIn  # base.Shape.BoundBox.ZMax
                        finDep = judgeFinalDepth(obj, face.BoundBox.ZMin)
                        tup = face, sub, angle, axis, tag, strDep, finDep, trans
                        hT.append(tup)
            # Eol
            return hT
예제 #50
0
    def updateStockEditor(self, index, force=False):
        import PathScripts.PathJobGui as PathJobGui
        import PathScripts.PathStock as PathStock

        def setupFromBaseEdit():
            PathLog.track(index, force)
            if force or not self.stockFromBase:
                self.stockFromBase = PathJobGui.StockFromBaseBoundBoxEdit(
                    self.obj, self.form, force)
            self.stockEdit = self.stockFromBase

        def setupCreateBoxEdit():
            PathLog.track(index, force)
            if force or not self.stockCreateBox:
                self.stockCreateBox = PathJobGui.StockCreateBoxEdit(
                    self.obj, self.form, force)
            self.stockEdit = self.stockCreateBox

        def setupCreateCylinderEdit():
            PathLog.track(index, force)
            if force or not self.stockCreateCylinder:
                self.stockCreateCylinder = PathJobGui.StockCreateCylinderEdit(
                    self.obj, self.form, force)
            self.stockEdit = self.stockCreateCylinder

        def setupFromExisting():
            PathLog.track(index, force)
            if force or not self.stockFromExisting:
                self.stockFromExisting = PathJobGui.StockFromExistingEdit(
                    self.obj, self.form, force)
            if self.stockFromExisting.candidates(self.obj):
                self.stockEdit = self.stockFromExisting
                return True
            return False

        if index == -1:
            if self.obj.Stock is None or PathJobGui.StockFromBaseBoundBoxEdit.IsStock(
                    self.obj):
                setupFromBaseEdit()
            elif PathJobGui.StockCreateBoxEdit.IsStock(self.obj):
                setupCreateBoxEdit()
            elif PathJobGui.StockCreateCylinderEdit.IsStock(self.obj):
                setupCreateCylinderEdit()
            elif PathJobGui.StockFromExistingEdit.IsStock(self.obj):
                setupFromExisting()
            else:
                PathLog.error(
                    translate('PathJob', "Unsupported stock object %s") %
                    self.obj.Stock.Label)
        else:
            if index == PathJobGui.StockFromBaseBoundBoxEdit.Index:
                setupFromBaseEdit()
            elif index == PathJobGui.StockCreateBoxEdit.Index:
                setupCreateBoxEdit()
            elif index == PathJobGui.StockCreateCylinderEdit.Index:
                setupCreateCylinderEdit()
            elif index == PathJobGui.StockFromExistingEdit.Index:
                if not setupFromExisting():
                    setupFromBaseEdit()
                    index = -1
            else:
                PathLog.error(
                    translate('PathJob', "Unsupported stock type %s (%d)") %
                    (self.form.stock.currentText(), index))
        self.stockEdit.activate(self.obj, index == -1)
예제 #51
0
    def updateUI(self):
        job = self.job
        if job.PostProcessor:
            ppHint = "%s %s %s" % (
                job.PostProcessor,
                job.PostProcessorArgs,
                job.PostProcessorOutputFile,
            )
            self.dialog.postProcessingHint.setText(ppHint)
        else:
            self.dialog.postProcessingGroup.setEnabled(False)
            self.dialog.postProcessingGroup.setChecked(False)

        if job.Stock and not PathJob.isResourceClone(job, "Stock", "Stock"):
            stockType = PathStock.StockType.FromStock(job.Stock)
            if stockType == PathStock.StockType.FromBase:
                seHint = translate(
                    "Path_Job", "Base -/+ %.2f/%.2f %.2f/%.2f %.2f/%.2f") % (
                        job.Stock.ExtXneg,
                        job.Stock.ExtXpos,
                        job.Stock.ExtYneg,
                        job.Stock.ExtYpos,
                        job.Stock.ExtZneg,
                        job.Stock.ExtZpos,
                    )
                self.dialog.stockPlacement.setChecked(False)
            elif stockType == PathStock.StockType.CreateBox:
                seHint = translate("Path_Job", "Box: %.2f x %.2f x %.2f") % (
                    job.Stock.Length,
                    job.Stock.Width,
                    job.Stock.Height,
                )
            elif stockType == PathStock.StockType.CreateCylinder:
                seHint = translate("Path_Job:", "Cylinder: %.2f x %.2f") % (
                    job.Stock.Radius,
                    job.Stock.Height,
                )
            elif stockType == PathStock.StockType.Unknown:
                seHint = "-"

            else:  # Existing Solid
                seHint = "-"
                PathLog.error(translate("Path_Job", "Unsupported stock type"))
            self.dialog.stockExtentHint.setText(seHint)
            spHint = "%s" % job.Stock.Placement
            self.dialog.stockPlacementHint.setText(spHint)

        rapidChanged = not job.SetupSheet.Proxy.hasDefaultToolRapids()
        depthsChanged = not job.SetupSheet.Proxy.hasDefaultOperationDepths()
        heightsChanged = not job.SetupSheet.Proxy.hasDefaultOperationHeights()
        coolantChanged = not job.SetupSheet.Proxy.hasDefaultCoolantMode()
        opsWithSettings = job.SetupSheet.Proxy.operationsWithSettings()
        settingsChanged = (rapidChanged or depthsChanged or heightsChanged
                           or coolantChanged or 0 != len(opsWithSettings))
        self.dialog.settingsGroup.setChecked(settingsChanged)
        self.dialog.settingToolRapid.setChecked(rapidChanged)
        self.dialog.settingOperationDepths.setChecked(depthsChanged)
        self.dialog.settingOperationHeights.setChecked(heightsChanged)
        self.dialog.settingCoolant.setChecked(coolantChanged)

        self.dialog.settingsOpsList.clear()
        for op in opsWithSettings:
            item = QtGui.QListWidgetItem(op)
            item.setCheckState(QtCore.Qt.CheckState.Checked)
            self.dialog.settingsOpsList.addItem(item)

        self.dialog.toolsList.clear()
        for tc in sorted(job.Tools.Group, key=lambda o: o.Label):
            item = QtGui.QListWidgetItem(tc.Label)
            item.setData(self.DataObject, tc)
            item.setCheckState(QtCore.Qt.CheckState.Checked)
            self.dialog.toolsList.addItem(item)
예제 #52
0
    def execute(self, obj):
        PathLog.track()
        if not obj.Base:
            PathLog.error(translate("Path_DressupTag",
                                    "No Base object found."))
            return
        if not obj.Base.isDerivedFrom("Path::Feature"):
            PathLog.error(
                translate("Path_DressupTag",
                          "Base is not a Path::Feature object."))
            return
        if not obj.Base.Path:
            PathLog.error(
                translate("Path_DressupTag",
                          "Base doesn't have a Path to dress-up."))
            return
        if not obj.Base.Path.Commands:
            PathLog.error(translate("Path_DressupTag", "Base Path is empty."))
            return

        self.obj = obj

        minZ = +MaxInt
        minX = minZ
        minY = minZ

        maxZ = -MaxInt
        maxX = maxZ
        maxY = maxZ

        # the assumption is that all helixes are in the xy-plane - in other words there is no
        # intermittent point of a command that has a lower/higher Z-position than the start and
        # and end positions of a command.
        lastPt = FreeCAD.Vector(0, 0, 0)
        for cmd in obj.Base.Path.Commands:
            pt = PathGeom.commandEndPoint(cmd, lastPt)
            if lastPt.x != pt.x:
                maxX = max(pt.x, maxX)
                minX = min(pt.x, minX)
            if lastPt.y != pt.y:
                maxY = max(pt.y, maxY)
                minY = min(pt.y, minY)
            if lastPt.z != pt.z:
                maxZ = max(pt.z, maxZ)
                minZ = min(pt.z, minZ)
            lastPt = pt
        PathLog.debug("bb = (%.2f, %.2f, %.2f) ... (%.2f, %.2f, %.2f)" %
                      (minX, minY, minZ, maxX, maxY, maxZ))
        self.ptMin = FreeCAD.Vector(minX, minY, minZ)
        self.ptMax = FreeCAD.Vector(maxX, maxY, maxZ)
        self.masterSolid = TagSolid(self, minZ, self.toolRadius())
        self.solids = [
            self.masterSolid.cloneAt(pos) for pos in self.obj.Positions
        ]
        self.tagSolid = Part.Compound(self.solids)

        self.wire, rapid = PathGeom.wireForPath(obj.Base.Path)  # pylint: disable=unused-variable
        self.edges = self.wire.Edges

        maxTagZ = minZ + obj.Height.Value

        # lastX = 0
        # lastY = 0
        lastZ = 0

        commands = []

        for cmd in obj.Base.Path.Commands:
            if cmd in PathGeom.CmdMove:
                if lastZ <= maxTagZ or cmd.Parameters.get("Z",
                                                          lastZ) <= maxTagZ:
                    pass
                else:
                    commands.append(cmd)
            else:
                commands.append(cmd)

        obj.Path = obj.Base.Path

        PathLog.track()
예제 #53
0
    def getPath(self):
        """getPath() ... Call this method on an instance of the class to generate and return
        path data for the requested path array."""

        if len(self.baseList) == 0:
            PathLog.error(
                translate("PathArray", "No base objects for PathArray."))
            return None

        base = self.baseList
        for b in base:
            if not b.isDerivedFrom("Path::Feature"):
                return
            if not b.Path:
                return
            if not b.ToolController:
                return
            if b.ToolController != base[0].ToolController:
                # this may be important if Job output is split by tool controller
                PathLog.warning(
                    translate(
                        "PathArray",
                        "Arrays of paths having different tool controllers are handled according to the tool controller of the first path.",
                    ))

        # build copies
        output = ""
        random.seed(self.seed)

        if self.arrayType == "Linear1D":
            for i in range(self.copies):
                pos = FreeCAD.Vector(
                    self.offsetVector.x * (i + 1),
                    self.offsetVector.y * (i + 1),
                    self.offsetVector.z * (i + 1),
                )
                pos = self._calculateJitter(pos)

                for b in base:
                    pl = FreeCAD.Placement()
                    pl.move(pos)
                    np = Path.Path(
                        [cm.transform(pl) for cm in b.Path.Commands])
                    output += np.toGCode()

        elif self.arrayType == "Linear2D":
            if self.swapDirection:
                for i in range(self.copiesY + 1):
                    for j in range(self.copiesX + 1):
                        if (i % 2) == 0:
                            pos = FreeCAD.Vector(
                                self.offsetVector.x * j,
                                self.offsetVector.y * i,
                                self.offsetVector.z * i,
                            )
                        else:
                            pos = FreeCAD.Vector(
                                self.offsetVector.x * (self.copiesX - j),
                                self.offsetVector.y * i,
                                self.offsetVector.z * i,
                            )
                        pos = self._calculateJitter(pos)

                        for b in base:
                            pl = FreeCAD.Placement()
                            # do not process the index 0,0. It will be processed by the base Paths themselves
                            if not (i == 0 and j == 0):
                                pl.move(pos)
                                np = Path.Path([
                                    cm.transform(pl) for cm in b.Path.Commands
                                ])
                                output += np.toGCode()
            else:
                for i in range(self.copiesX + 1):
                    for j in range(self.copiesY + 1):
                        if (i % 2) == 0:
                            pos = FreeCAD.Vector(
                                self.offsetVector.x * i,
                                self.offsetVector.y * j,
                                self.offsetVector.z * i,
                            )
                        else:
                            pos = FreeCAD.Vector(
                                self.offsetVector.x * i,
                                self.offsetVector.y * (self.copiesY - j),
                                self.offsetVector.z * i,
                            )
                        pos = self._calculateJitter(pos)

                        for b in base:
                            pl = FreeCAD.Placement()
                            # do not process the index 0,0. It will be processed by the base Paths themselves
                            if not (i == 0 and j == 0):
                                pl.move(pos)
                                np = Path.Path([
                                    cm.transform(pl) for cm in b.Path.Commands
                                ])
                                output += np.toGCode()
            # Eif
        else:
            for i in range(self.copies):
                for b in base:
                    ang = 360
                    if self.copies > 0:
                        ang = self.angle / self.copies * (1 + i)
                    np = self.rotatePath(b.Path.Commands, ang, self.centre)
                    output += np.toGCode()

        # return output
        return Path.Path(output)
예제 #54
0
    def scanJob(self, forward):
        PathLog.track()
        job = self.mk.getJob()
        if job and hasattr(job, 'Path') and job.Path:
            bb = job.Path.BoundBox
            if bb.isValid():
                off = self.mk['status.motion.offset.g5x']
                bb.move(FreeCAD.Vector(off['x'], off['y'], off['z']))
                if self.mk.boundBox().isInside(bb):
                    mkx = self.displayPos('x')
                    mky = self.displayPos('y')
                    begin = FreeCAD.Vector(mkx, mky, 0)
                    pts = []
                    bb = job.Path.BoundBox
                    if forward:
                        pts.append(FreeCAD.Vector(bb.XMin, bb.YMin, 0))
                        pts.append(FreeCAD.Vector(bb.XMax, bb.YMin, 0))
                        pts.append(FreeCAD.Vector(bb.XMax, bb.YMax, 0))
                        pts.append(FreeCAD.Vector(bb.XMin, bb.YMax, 0))
                    else:
                        pts.append(FreeCAD.Vector(bb.XMin, bb.YMin, 0))
                        pts.append(FreeCAD.Vector(bb.XMin, bb.YMax, 0))
                        pts.append(FreeCAD.Vector(bb.XMax, bb.YMax, 0))
                        pts.append(FreeCAD.Vector(bb.XMax, bb.YMin, 0))

                    dist = [begin.distanceToPoint(p) for p in pts]
                    rot = dist.index(min(dist))
                    pts = pts[rot:] + pts[:rot]
                    pts.append(pts[0])
                    PathLog.info(" begin = (%5.2f, %5.2f)" %
                                 (begin.x, begin.y))
                    for i, p in enumerate(pts):
                        PathLog.info(" pts[%d] = (%5.2f, %5.2f)" %
                                     (i, p.x, p.y))

                    jog = []
                    if not PathGeom.pointsCoincide(begin, pts[0]):
                        PathLog.info("Move to start point (%.2f, %.2f)" %
                                     (pts[0].x, pts[0].y))
                        jog.append(self._jogXYCmdsFromTo(begin, pts[0]))
                    for i, j in zip(pts, pts[1:]):
                        jog.append(self._jogXYCmdsFromTo(i, j))

                    sequence = [[cmd]
                                for cmd in MKUtils.taskModeManual(self.mk)]
                    sequence.extend(jog)
                    self.mk['command'].sendCommandSequence(sequence)
                else:
                    mbb = self.mk.boundBox()
                    msg = ["Cannot scan job!"]
                    if mbb.XMin > bb.XMin:
                        msg.append("X limit min exceeded by: %.2f" %
                                   (mbb.XMin - bb.XMin))
                    if mbb.XMax < bb.XMax:
                        msg.append("X limit max exceeded by: %.2f" %
                                   (bb.XMax - mbb.XMax))
                    if mbb.YMin > bb.YMin:
                        msg.append("Y limit min exceeded by: %.2f" %
                                   (mbb.YMin - bb.YMin))
                    if mbb.YMax < bb.YMax:
                        msg.append("Y limit max exceeded by: %.2f" %
                                   (bb.YMax - mbb.YMax))
                    if mbb.ZMin > bb.ZMin:
                        msg.append("Z limit min exceeded by: %.2f" %
                                   (mbb.ZMin - bb.ZMin))
                    if mbb.ZMax < bb.ZMax:
                        msg.append("Z limit max exceeded by: %.2f" %
                                   (bb.ZMax - mbb.ZMax))

                    mb = PySide.QtGui.QMessageBox()
                    mb.setWindowIcon(
                        machinekit.IconResource('machinekiticon.png'))
                    mb.setWindowTitle('Machinekit')
                    mb.setTextFormat(PySide.QtCore.Qt.TextFormat.RichText)
                    mb.setText("<div align='center'>%s</div>" %
                               '<br/>'.join(msg))
                    mb.setIcon(PySide.QtGui.QMessageBox.Critical)
                    mb.setStandardButtons(PySide.QtGui.QMessageBox.Ok)
                    mb.exec_()
            else:
                PathLog.error("BoundBox of job %s is not valid" % job.Label)
        else:
            PathLog.error('No job uploaded for scanning')
예제 #55
0
    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.'))
예제 #56
0
    def areaOpShapes(self, obj):
        """areaOpShapes(obj) ... return shapes representing the solids to be removed."""
        PathLog.track()
        self.removalshapes = []

        # self.isDebug = True if PathLog.getLevel(PathLog.thisModule()) == 4 else False
        self.removalshapes = []
        avoidFeatures = list()

        # Get extensions and identify faces to avoid
        extensions = FeatureExtensions.getExtensions(obj)
        for e in extensions:
            if e.avoid:
                avoidFeatures.append(e.feature)

        if obj.Base:
            PathLog.debug("base items exist.  Processing...")
            self.horiz = []
            self.vert = []
            for (base, subList) in obj.Base:
                for sub in subList:
                    if "Face" in sub:
                        if sub not in avoidFeatures and not self.clasifySub(
                                base, sub):
                            PathLog.error(
                                "Pocket does not support shape {}.{}".format(
                                    base.Label, sub))

            # Convert horizontal faces to use outline only if requested
            if obj.UseOutline and self.horiz:
                horiz = [Part.Face(f.Wire1) for f in self.horiz]
                self.horiz = horiz

            # Check if selected vertical faces form a loop
            if len(self.vert) > 0:
                self.vertical = PathGeom.combineConnectedShapes(self.vert)
                self.vWires = [
                    TechDraw.findShapeOutline(shape, 1,
                                              FreeCAD.Vector(0, 0, 1))
                    for shape in self.vertical
                ]
                for wire in self.vWires:
                    w = PathGeom.removeDuplicateEdges(wire)
                    face = Part.Face(w)
                    # face.tessellate(0.1)
                    if PathGeom.isRoughly(face.Area, 0):
                        PathLog.error(
                            "Vertical faces do not form a loop - ignoring")
                    else:
                        self.horiz.append(face)

            # Add faces for extensions
            self.exts = []  # pylint: disable=attribute-defined-outside-init
            for ext in extensions:
                if not ext.avoid:
                    wire = ext.getWire()
                    if wire:
                        faces = ext.getExtensionFaces(wire)
                        for f in faces:
                            self.horiz.append(f)
                            self.exts.append(f)

            # check all faces and see if they are touching/overlapping and combine and simplify
            self.horizontal = PathGeom.combineHorizontalFaces(self.horiz)

            # Move all faces to final depth less buffer before extrusion
            # Small negative buffer is applied to compensate for internal significant digits/rounding issue
            if self.job.GeometryTolerance.Value == 0.0:
                buffer = 0.000001
            else:
                buffer = self.job.GeometryTolerance.Value / 10.0
            for h in self.horizontal:
                h.translate(
                    FreeCAD.Vector(
                        0.0, 0.0,
                        obj.FinalDepth.Value - h.BoundBox.ZMin - buffer))

            # extrude all faces up to StartDepth plus buffer and those are the removal shapes
            extent = FreeCAD.Vector(
                0, 0, obj.StartDepth.Value - obj.FinalDepth.Value + buffer)
            self.removalshapes = [(face.removeSplitter().extrude(extent),
                                   False) for face in self.horizontal]

        else:  # process the job base object as a whole
            PathLog.debug("processing the whole job base object")
            self.outlines = [
                Part.Face(
                    TechDraw.findShapeOutline(base.Shape, 1,
                                              FreeCAD.Vector(0, 0, 1)))
                for base in self.model
            ]
            stockBB = self.stock.Shape.BoundBox

            self.bodies = []
            for outline in self.outlines:
                outline.translate(FreeCAD.Vector(0, 0, stockBB.ZMin - 1))
                body = outline.extrude(
                    FreeCAD.Vector(0, 0, stockBB.ZLength + 2))
                self.bodies.append(body)
                self.removalshapes.append((self.stock.Shape.cut(body), False))

        # Tessellate all working faces
        # for (shape, hole) in self.removalshapes:
        #    shape.tessellate(0.05)  # originally 0.1

        if self.removalshapes:
            obj.removalshape = Part.makeCompound(
                [tup[0] for tup in self.removalshapes])

        return self.removalshapes
예제 #57
0
    def areaOpShapes(self, obj):
        '''areaOpShapes(obj) ... return shapes representing the solids to be removed.'''
        PathLog.track()
        PathLog.debug("areaOpShapes() in PathPocketShape.py")

        def judgeFinalDepth(obj, fD):
            if obj.FinalDepth.Value >= fD:
                return obj.FinalDepth.Value
            else:
                return fD

        def judgeStartDepth(obj, sD):
            if obj.StartDepth.Value >= sD:
                return obj.StartDepth.Value
            else:
                return sD

        def analyzeVerticalFaces(self, obj, vertTuples):
            hT = []
            # base = FreeCAD.ActiveDocument.getObject(self.modelName)

            # Separate elements, regroup by orientation (axis_angle combination)
            vTags = ['X34.2']
            vGrps = [[(2.3, 3.4, 'X')]]
            for tup in vertTuples:
                (face, sub, angle, axis, tag, strDep, finDep, trans) = tup
                if tag in vTags:
                    # Determine index of found string
                    i = 0
                    for orn in vTags:
                        if orn == tag:
                            break
                        i += 1
                    vGrps[i].append(tup)
                else:
                    vTags.append(tag)  # add orientation entry
                    vGrps.append([tup])  # add orientation entry
            # Remove temp elements
            vTags.pop(0)
            vGrps.pop(0)

            # check all faces in each axis_angle group
            shpList = []
            zmaxH = 0.0
            for o in range(0, len(vTags)):
                shpList = []
                zmaxH = vGrps[o][0].BoundBox.ZMax
                for (face, sub, angle, axis, tag, strDep, finDep,
                     trans) in vGrps[o]:
                    shpList.append(face)
                    # Identify tallest face to use as zMax
                    if face.BoundBox.ZMax > zmaxH:
                        zmaxH = face.BoundBox.ZMax
                # check all faces and see if they are touching/overlapping and combine those into a compound
                # Original Code in For loop
                self.vertical = PathGeom.combineConnectedShapes(shpList)
                self.vWires = [
                    TechDraw.findShapeOutline(shape, 1,
                                              FreeCAD.Vector(0, 0, 1))
                    for shape in self.vertical
                ]
                for wire in self.vWires:
                    w = PathGeom.removeDuplicateEdges(wire)
                    face = Part.Face(w)
                    face.tessellate(0.05)
                    if PathGeom.isRoughly(face.Area, 0):
                        PathLog.error(
                            translate(
                                'PathPocket',
                                'Vertical faces do not form a loop - ignoring')
                        )
                    else:
                        strDep = zmaxH + self.leadIn  # base.Shape.BoundBox.ZMax
                        finDep = judgeFinalDepth(obj, face.BoundBox.ZMin)
                        tup = face, sub, angle, axis, tag, strDep, finDep, trans
                        hT.append(tup)
            # Eol
            return hT

        if obj.Base:
            PathLog.debug('base items exist.  Processing...')
            self.removalshapes = []
            self.horiz = []
            vertical = []
            horizTuples = []
            vertTuples = []
            axis = 'X'
            angle = 0.0
            reset = False
            resetPlacement = None
            trans = FreeCAD.Vector(0.0, 0.0, 0.0)

            for o in obj.Base:
                PathLog.debug('Base item: {}'.format(o))
                base = o[0]

                # Limit sub faces to children of single Model object.
                if self.modelName is None:
                    self.modelName = base.Name
                else:
                    if base.Name != self.modelName:
                        for sub in o[1]:
                            PathLog.error(sub +
                                          " is not a part of Model object: " +
                                          self.modelName)
                        o[1] = []
                        PathLog.error(
                            "Only processing faces on a single Model object per operation."
                        )
                        PathLog.error(
                            "You will need to separate faces per Model object within the Job."
                        )

                startBase = FreeCAD.Vector(base.Placement.Base.x,
                                           base.Placement.Base.y,
                                           base.Placement.Base.z)
                startAngle = base.Placement.Rotation.Angle
                startAxis = base.Placement.Rotation.Axis
                startRotation = FreeCAD.Rotation(startAxis, startAngle)
                resetPlacement = FreeCAD.Placement(startBase, startRotation)
                for sub in o[1]:
                    if 'Face' in sub:
                        PathLog.debug('sub: {}'.format(sub))
                        # Determine angle of rotation needed to make normal vector = (0,0,1)
                        strDep = obj.StartDepth.Value
                        finDep = obj.FinalDepth.Value
                        trans = FreeCAD.Vector(0.0, 0.0, 0.0)
                        rtn = False

                        if obj.UseRotation != 'Off':
                            (rtn, angle,
                             axis) = self.pocketRotationAnalysis(obj,
                                                                 base,
                                                                 sub,
                                                                 prnt=True)

                        if rtn is True:
                            reset = True
                            PathLog.debug(
                                str(sub) +
                                ": rotating model to make face normal at (0,0,1) ..."
                            )
                            if axis == 'X':
                                bX = 0.0
                                bY = 0.0
                                bZ = math.sin(math.radians(
                                    angle)) * base.Placement.Base.y
                                vect = FreeCAD.Vector(1, 0, 0)
                            elif axis == 'Y':
                                bX = 0.0
                                bY = 0.0
                                bZ = math.sin(math.radians(
                                    angle)) * base.Placement.Base.x
                                if obj.B_AxisErrorOverride is True:
                                    bZ = -1 * bZ
                                vect = FreeCAD.Vector(0, 1, 0)
                            # Rotate base to such that Surface.Axis of pocket bottom is Z=1
                            base.Placement.Rotation = FreeCAD.Rotation(
                                vect, angle)
                            base.recompute()
                            trans = FreeCAD.Vector(bX, bY, bZ)
                        else:
                            axis = 'X'
                            angle = 0.0
                        tag = axis + str(round(angle, 7))
                        face = base.Shape.getElement(sub)

                        if type(face.Surface
                                ) == Part.Plane and PathGeom.isVertical(
                                    face.Surface.Axis):
                            # it's a flat horizontal face
                            PathLog.debug(" == Part.Plane: isVertical")
                            # Adjust start and finish depths for pocket
                            strDep = base.Shape.BoundBox.ZMax + self.leadIn
                            finDep = judgeFinalDepth(obj, face.BoundBox.ZMin)
                            # Over-write default final depth value, leaves manual override by user
                            obj.StartDepth.Value = trans.z + strDep
                            obj.FinalDepth.Value = trans.z + finDep

                            tup = face, sub, angle, axis, tag, strDep, finDep, trans
                            horizTuples.append(tup)
                        elif type(face.Surface
                                  ) == Part.Cylinder and PathGeom.isVertical(
                                      face.Surface.Axis):
                            PathLog.debug("== Part.Cylinder")
                            # vertical cylinder wall
                            if any(e.isClosed() for e in face.Edges):
                                PathLog.debug("e.isClosed()")
                                # complete cylinder
                                circle = Part.makeCircle(
                                    face.Surface.Radius, face.Surface.Center)
                                disk = Part.Face(Part.Wire(circle))

                                # Adjust start and finish depths for pocket
                                strDep = face.BoundBox.ZMax + self.leadIn  # base.Shape.BoundBox.ZMax + self.leadIn
                                finDep = judgeFinalDepth(
                                    obj, face.BoundBox.ZMin)
                                # Over-write default final depth value, leaves manual override by user
                                obj.StartDepth.Value = trans.z + strDep
                                obj.FinalDepth.Value = trans.z + finDep

                                tup = disk, sub, angle, axis, tag, strDep, finDep, trans
                                horizTuples.append(tup)
                            else:
                                # partial cylinder wall
                                vertical.append(face)

                                # Adjust start and finish depths for pocket
                                strDep = face.BoundBox.ZMax + self.leadIn  # base.Shape.BoundBox.ZMax + self.leadIn
                                finDep = judgeFinalDepth(
                                    obj, face.BoundBox.ZMin)
                                # Over-write default final depth value, leaves manual override by user
                                obj.StartDepth.Value = trans.z + strDep
                                obj.FinalDepth.Value = trans.z + finDep
                                tup = face, sub, angle, axis, tag, strDep, finDep, trans
                                vertTuples.append(tup)

                                PathLog.debug(sub +
                                              "is vertical after rotation.")
                        elif type(face.Surface
                                  ) == Part.Plane and PathGeom.isHorizontal(
                                      face.Surface.Axis):
                            vertical.append(face)

                            # Adjust start and finish depths for pocket
                            strDep = face.BoundBox.ZMax + self.leadIn  # base.Shape.BoundBox.ZMax + self.leadIn
                            finDep = judgeFinalDepth(obj, face.BoundBox.ZMin)
                            # Over-write default final depth value, leaves manual override by user
                            obj.StartDepth.Value = trans.z + strDep
                            obj.FinalDepth.Value = trans.z + finDep
                            tup = face, sub, angle, axis, tag, strDep, finDep, trans
                            vertTuples.append(tup)
                            PathLog.debug(sub + "is vertical after rotation.")
                        else:
                            PathLog.error(
                                translate(
                                    'PathPocket',
                                    'Pocket does not support shape %s.%s') %
                                (base.Label, sub))

                        if reset is True:
                            base.Placement.Rotation = startRotation
                            base.recompute()
                            reset = False
                    # End IF
                # End FOR
                base.Placement = resetPlacement
                base.recompute()
            # End FOR

            # Analyze vertical faces via PathGeom.combineConnectedShapes()
            # hT = analyzeVerticalFaces(self, obj, vertTuples)
            # horizTuples.extend(hT)

            # This section will be replaced analyzeVerticalFaces(self, obj, vertTuples) above
            self.vertical = PathGeom.combineConnectedShapes(vertical)
            self.vWires = [
                TechDraw.findShapeOutline(shape, 1,
                                          FreeCAD.Vector(0.0, 0.0, 1.0))
                for shape in self.vertical
            ]
            for wire in self.vWires:
                w = PathGeom.removeDuplicateEdges(wire)
                face = Part.Face(w)
                face.tessellate(0.05)
                if PathGeom.isRoughly(face.Area, 0):
                    PathLog.error(
                        translate(
                            'PathPocket',
                            'Vertical faces do not form a loop - ignoring'))
                else:
                    # self.horiz.append(face)
                    strDep = base.Shape.BoundBox.ZMax + self.leadIn
                    finDep = judgeFinalDepth(obj, face.BoundBox.ZMin)
                    tup = face, 'vertFace', 0.0, 'X', 'X0.0', strDep, finDep, FreeCAD.Vector(
                        0.0, 0.0, 0.0)
                    horizTuples.append(tup)

            # add faces for extensions
            self.exts = []
            for ext in self.getExtensions(obj):
                wire = Part.Face(ext.getWire())
                if wire:
                    face = Part.Face(wire)
                    # self.horiz.append(face)
                    strDep = base.Shape.BoundBox.ZMax + self.leadIn
                    finDep = judgeFinalDepth(obj, face.BoundBox.ZMin)
                    tup = face, 'vertFace', 0.0, 'X', 'X0.0', strDep, finDep, FreeCAD.Vector(
                        0.0, 0.0, 0.0)
                    horizTuples.append(tup)
                    self.exts.append(face)

            # move all horizontal faces to FinalDepth
            for (face, sub, angle, axis, tag, strDep, finDep,
                 trans) in horizTuples:
                # face.translate(FreeCAD.Vector(0, 0, obj.FinalDepth.Value - face.BoundBox.ZMin))
                if angle <= 0.0:
                    if axis == 'X':
                        face.translate(
                            FreeCAD.Vector(
                                0, trans.z,
                                trans.z + finDep - face.BoundBox.ZMin))
                    elif axis == 'Y':
                        face.translate(
                            FreeCAD.Vector(
                                -1 * trans.z, 0,
                                trans.z + finDep - face.BoundBox.ZMin))
                else:
                    if axis == 'X':
                        face.translate(
                            FreeCAD.Vector(
                                0, -1 * trans.z,
                                trans.z + finDep - face.BoundBox.ZMin))
                    elif axis == 'Y':
                        face.translate(
                            FreeCAD.Vector(
                                trans.z, 0,
                                trans.z + finDep - face.BoundBox.ZMin))

            # Separate elements, regroup by orientation (axis_angle combination)
            hTags = ['X34.2']
            hGrps = [[(2.3, 3.4, 'X')]]
            for tup in horizTuples:
                (face, sub, angle, axis, tag, strDep, finDep, trans) = tup
                if tag in hTags:
                    # Determine index of found string
                    i = 0
                    for orn in hTags:
                        if orn == tag:
                            break
                        i += 1
                    hGrps[i].append(tup)
                else:
                    hTags.append(tag)  # add orientation entry
                    hGrps.append([tup])  # add orientation entry
            # Remove temp elements
            hTags.pop(0)
            hGrps.pop(0)

            # check all faces in each axis_angle group
            self.horizontal = []
            shpList = []
            for o in range(0, len(hTags)):
                PathLog.debug('hTag: {}'.format(hTags[o]))
                shpList = []
                for (face, sub, angle, axis, tag, strDep, finDep,
                     trans) in hGrps[o]:
                    shpList.append(face)
                # check all faces and see if they are touching/overlapping and combine those into a compound
                # Original Code in For loop
                for shape in PathGeom.combineConnectedShapes(shpList):
                    shape.sewShape()
                    # shape.tessellate(0.05) # Russ4262 0.1 original
                    if obj.UseOutline:
                        wire = TechDraw.findShapeOutline(
                            shape, 1, FreeCAD.Vector(0, 0, 1))
                        wire.translate(
                            FreeCAD.Vector(
                                0, 0,
                                obj.FinalDepth.Value - wire.BoundBox.ZMin))
                        PathLog.debug(
                            " -obj.UseOutline: obj.FinalDepth.Value" +
                            str(obj.FinalDepth.Value))
                        PathLog.debug(" -obj.UseOutline: wire.BoundBox.ZMin" +
                                      str(wire.BoundBox.ZMin))
                        # shape.tessellate(0.05) # Russ4262 0.1 original
                        face = Part.Face(wire)
                        tup = face, sub, angle, axis, tag, strDep, finDep, trans
                        self.horizontal.append(tup)
                    else:
                        # Re-pair shape to tuple set
                        for (face, sub, angle, axis, tag, strDep, finDep,
                             trans) in hGrps[o]:
                            if shape is face:
                                tup = face, sub, angle, axis, tag, strDep, finDep, trans
                                self.horizontal.append(tup)
                                break
            # Eol

            # extrude all faces up to StartDepth and those are the removal shapes
            for (face, sub, angle, axis, tag, strDep, finDep,
                 trans) in self.horizontal:
                # extent = FreeCAD.Vector(0, 0, obj.StartDepth.Value - obj.FinalDepth.Value)
                extent = FreeCAD.Vector(0, 0, strDep - finDep)
                shp = face.removeSplitter().extrude(extent)
                # tup = shp, False, sub, angle, axis, tag, strDep, finDep, trans
                tup = shp, False, sub, angle, axis  # shape, isHole, sub, angle, axis
                self.removalshapes.append(tup)

        else:  # process the job base object as a whole
            PathLog.debug("processing the whole job base object")
            self.outlines = [
                Part.Face(
                    TechDraw.findShapeOutline(base.Shape, 1,
                                              FreeCAD.Vector(0, 0, 1)))
                for base in self.model
            ]
            stockBB = self.stock.Shape.BoundBox
            PathLog.debug(" -Using outlines; no obj.Base")

            self.removalshapes = []
            self.bodies = []
            for outline in self.outlines:
                outline.translate(FreeCAD.Vector(0, 0, stockBB.ZMin - 1))
                body = outline.extrude(
                    FreeCAD.Vector(0, 0, stockBB.ZLength + 2))
                self.bodies.append(body)
                self.removalshapes.append(
                    (self.stock.Shape.cut(body), False, 'outline', 0.0, 'X'))

        for (shape, isHole, sub, angle, axis) in self.removalshapes:
            shape.tessellate(0.05)

        if self.removalshapes:
            obj.removalshape = self.removalshapes[0][0]
        return self.removalshapes
예제 #58
0
    def groupConnectedEdges(self, holding):
        '''groupConnectedEdges(self, holding)
        Take edges and determine which are connected.
        Group connected chains/loops into: low and high'''
        holds = []
        grps = []
        searched = []
        stop = False
        attachments = []
        loops = 1

        def updateAttachments(grps):
            atchmnts = []
            lenGrps = len(grps)
            if lenGrps > 0:
                lenG0 = len(grps[0])
                if lenG0 < 2:
                    atchmnts.append((0, 0))
                else:
                    atchmnts.append((0, 0))
                    atchmnts.append((0, lenG0 - 1))
            if lenGrps == 2:
                lenG1 = len(grps[1])
                if lenG1 < 2:
                    atchmnts.append((1, 0))
                else:
                    atchmnts.append((1, 0))
                    atchmnts.append((1, lenG1 - 1))
            return atchmnts

        def isSameVertex(o, t):
            if o.X == t.X:
                if o.Y == t.Y:
                    if o.Z == t.Z:
                        return True
            return False

        for hi in range(0, len(holding)):
            holds.append(hi)

        # Place initial edge in first group and update attachments
        h0 = holds.pop()
        grps.append([h0])
        attachments = updateAttachments(grps)

        while len(holds) > 0:
            if loops > 500:
                PathLog.error('BREAK --- LOOPS LIMIT of 500 ---')
                break
            save = False

            h2 = holds.pop()
            (sub2, face2, ei2) = holding[h2]

            # Cycle through attachments for connection to existing
            for (g, t) in attachments:
                h1 = grps[g][t]
                (sub1, face1, ei1) = holding[h1]

                edg1 = face1.Edges[ei1]
                edg2 = face2.Edges[ei2]

                # CV = self.hasCommonVertex(edg1, edg2, show=False)

                # Check attachment based on attachments order
                if t == 0:
                    # is last vertex of h2 == first vertex of h1
                    e2lv = len(edg2.Vertexes) - 1
                    one = edg2.Vertexes[e2lv]
                    two = edg1.Vertexes[0]
                    if isSameVertex(one, two) is True:
                        # Connected, insert h1 in front of h2
                        grps[g].insert(0, h2)
                        stop = True
                else:
                    # is last vertex of h1 == first vertex of h2
                    e1lv = len(edg1.Vertexes) - 1
                    one = edg1.Vertexes[e1lv]
                    two = edg2.Vertexes[0]
                    if isSameVertex(one, two) is True:
                        # Connected, append h1 after h2
                        grps[g].append(h2)
                        stop = True

                if stop is True:
                    # attachment was found
                    attachments = updateAttachments(grps)
                    holds.extend(searched)
                    stop = False
                    break
                else:
                    # no attachment found
                    save = True
            # Efor
            if save is True:
                searched.append(h2)
                if len(holds) == 0:
                    if len(grps) == 1:
                        h0 = searched.pop(0)
                        grps.append([h0])
                        attachments = updateAttachments(grps)
                        holds.extend(searched)
            # Eif
            loops += 1
        # Ewhile

        low = []
        high = []
        if len(grps) == 1:
            grps.append([])
        grp0 = []
        grp1 = []
        com0 = FreeCAD.Vector(0, 0, 0)
        com1 = FreeCAD.Vector(0, 0, 0)
        if len(grps[0]) > 0:
            for g in grps[0]:
                grp0.append(holding[g])
                (sub, face, ei) = holding[g]
                com0 = com0.add(face.Edges[ei].CenterOfMass)
            com0z = com0.z / len(grps[0])
        if len(grps[1]) > 0:
            for g in grps[1]:
                grp1.append(holding[g])
                (sub, face, ei) = holding[g]
                com1 = com1.add(face.Edges[ei].CenterOfMass)
            com1z = com1.z / len(grps[1])

        if len(grps[1]) > 0:
            if com0z > com1z:
                low = grp1
                high = grp0
            else:
                low = grp0
                high = grp1
        else:
            low = grp0
            high = grp0

        return (low, high)
예제 #59
0
    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
예제 #60
0
    def execute(self):
        if (
            not self.baseOp
            or not self.baseOp.isDerivedFrom("Path::Feature")
            or not self.baseOp.Path
        ):
            return None

        if len(self.baseOp.Path.Commands) == 0:
            PathLog.warning("No Path Commands for %s" % self.baseOp.Label)
            return []

        tc = PathDressup.toolController(self.baseOp)

        self.safeHeight = float(PathUtil.opProperty(self.baseOp, "SafeHeight"))
        self.clearanceHeight = float(
            PathUtil.opProperty(self.baseOp, "ClearanceHeight")
        )
        self.strG0ZsafeHeight = Path.Command(  # was a Feed rate with G1
            "G0", {"Z": self.safeHeight, "F": tc.VertRapid.Value}
        )
        self.strG0ZclearanceHeight = Path.Command("G0", {"Z": self.clearanceHeight})

        cmd = self.baseOp.Path.Commands[0]
        pos = cmd.Placement.Base  # bogus m/c position to create first edge
        bogusX = True
        bogusY = True
        commands = [cmd]
        lastExit = None
        for cmd in self.baseOp.Path.Commands[1:]:
            if cmd.Name in PathGeom.CmdMoveAll:
                if bogusX:
                    bogusX = "X" not in cmd.Parameters
                if bogusY:
                    bogusY = "Y" not in cmd.Parameters
                edge = PathGeom.edgeForCmd(cmd, pos)
                if edge:
                    inside = edge.common(self.boundary).Edges
                    outside = edge.cut(self.boundary).Edges
                    if not self.inside:  # UI "inside boundary" param
                        tmp = inside
                        inside = outside
                        outside = tmp
                    # it's really a shame that one cannot trust the sequence and/or
                    # orientation of edges
                    if 1 == len(inside) and 0 == len(outside):
                        PathLog.track(_vstr(pos), _vstr(lastExit), " + ", cmd)
                        # cmd fully included by boundary
                        if lastExit:
                            if not (
                                bogusX or bogusY
                            ):  # don't insert false paths based on bogus m/c position
                                commands.extend(
                                    self.boundaryCommands(
                                        lastExit, pos, tc.VertFeed.Value
                                    )
                                )
                            lastExit = None
                        commands.append(cmd)
                        pos = PathGeom.commandEndPoint(cmd, pos)
                    elif 0 == len(inside) and 1 == len(outside):
                        PathLog.track(_vstr(pos), _vstr(lastExit), " - ", cmd)
                        # cmd fully excluded by boundary
                        if not lastExit:
                            lastExit = pos
                        pos = PathGeom.commandEndPoint(cmd, pos)
                    else:
                        PathLog.track(
                            _vstr(pos), _vstr(lastExit), len(inside), len(outside), cmd
                        )
                        # cmd pierces boundary
                        while inside or outside:
                            ie = [e for e in inside if PathGeom.edgeConnectsTo(e, pos)]
                            PathLog.track(ie)
                            if ie:
                                e = ie[0]
                                LastPt = e.valueAt(e.LastParameter)
                                flip = PathGeom.pointsCoincide(pos, LastPt)
                                newPos = e.valueAt(e.FirstParameter) if flip else LastPt
                                # inside edges are taken at this point (see swap of inside/outside
                                # above - so we can just connect the dots ...
                                if lastExit:
                                    if not (bogusX or bogusY):
                                        commands.extend(
                                            self.boundaryCommands(
                                                lastExit, pos, tc.VertFeed.Value
                                            )
                                        )
                                    lastExit = None
                                PathLog.track(e, flip)
                                if not (
                                    bogusX or bogusY
                                ):  # don't insert false paths based on bogus m/c position
                                    commands.extend(
                                        PathGeom.cmdsForEdge(
                                            e,
                                            flip,
                                            False,
                                            50,
                                            tc.HorizFeed.Value,
                                            tc.VertFeed.Value,
                                        )
                                    )
                                inside.remove(e)
                                pos = newPos
                                lastExit = newPos
                            else:
                                oe = [
                                    e
                                    for e in outside
                                    if PathGeom.edgeConnectsTo(e, pos)
                                ]
                                PathLog.track(oe)
                                if oe:
                                    e = oe[0]
                                    ptL = e.valueAt(e.LastParameter)
                                    flip = PathGeom.pointsCoincide(pos, ptL)
                                    newPos = (
                                        e.valueAt(e.FirstParameter) if flip else ptL
                                    )
                                    # outside edges are never taken at this point (see swap of
                                    # inside/outside above) - so just move along ...
                                    outside.remove(e)
                                    pos = newPos
                                else:
                                    PathLog.error("huh?")
                                    import Part

                                    Part.show(Part.Vertex(pos), "pos")
                                    for e in inside:
                                        Part.show(e, "ei")
                                    for e in outside:
                                        Part.show(e, "eo")
                                    raise Exception("This is not supposed to happen")
                                # Eif
                            # Eif
                        # Ewhile
                    # Eif
                    # pos = PathGeom.commandEndPoint(cmd, pos)
                # Eif
            else:
                PathLog.track("no-move", cmd)
                commands.append(cmd)
        if lastExit:
            commands.extend(self.boundaryCommands(lastExit, None, tc.VertFeed.Value))
            lastExit = None

        PathLog.track(commands)
        return Path.Path(commands)