Esempio n. 1
0
 def deleteObjectsOnReject(self):
     '''deleteObjectsOnReject() ... return true if all objects should
     be created if the user hits cancel. This is used during the initial
     edit session, if the user does not press OK, it is assumed they've
     changed their mind about creating the operation.'''
     PathLog.track()
     return hasattr(self, 'deleteOnReject') and self.deleteOnReject
Esempio n. 2
0
    def __init__(self, obj, base):
        "Make stock"
        obj.addProperty("App::PropertyLink", "Base", "Base", QtCore.QT_TRANSLATE_NOOP("PathStock", "The base object this stock is derived from"))
        obj.addProperty("App::PropertyLength", "ExtXneg", "Stock", QtCore.QT_TRANSLATE_NOOP("PathStock", "Extra allowance from part bound box in negative X direction"))
        obj.addProperty("App::PropertyLength", "ExtXpos", "Stock", QtCore.QT_TRANSLATE_NOOP("PathStock", "Extra allowance from part bound box in positive X direction"))
        obj.addProperty("App::PropertyLength", "ExtYneg", "Stock", QtCore.QT_TRANSLATE_NOOP("PathStock", "Extra allowance from part bound box in negative Y direction"))
        obj.addProperty("App::PropertyLength", "ExtYpos", "Stock", QtCore.QT_TRANSLATE_NOOP("PathStock", "Extra allowance from part bound box in positive Y direction"))
        obj.addProperty("App::PropertyLength", "ExtZneg", "Stock", QtCore.QT_TRANSLATE_NOOP("PathStock", "Extra allowance from part bound box in negative Z direction"))
        obj.addProperty("App::PropertyLength", "ExtZpos", "Stock", QtCore.QT_TRANSLATE_NOOP("PathStock", "Extra allowance from part bound box in positive Z direction"))

        obj.Base = base
        obj.ExtXneg= 1.0
        obj.ExtXpos= 1.0
        obj.ExtYneg= 1.0
        obj.ExtYpos= 1.0
        obj.ExtZneg= 1.0
        obj.ExtZpos= 1.0

        # placement is only tracked on creation
        bb = shapeBoundBox(base.Group) if base else None
        if bb:
            obj.Placement = FreeCAD.Placement(FreeCAD.Vector(bb.XMin, bb.YMin, bb.ZMin), FreeCAD.Rotation())
        else:
            PathLog.track(obj.Label, base.Label)
        obj.Proxy = self
Esempio n. 3
0
    def opExecute(self, obj):
        PathLog.track(obj.Label)
        (depth, offset) = toolDepthAndOffset(obj.Width.Value, obj.ExtraDepth.Value, self.tool)
        PathLog.track(obj.Label, depth, offset)

        self.basewires = []
        self.adjusted_basewires = []
        wires = []
        for base, subs in obj.Base:
            edges = []
            basewires = []
            for f in subs:
                sub = base.Shape.getElement(f)
                if type(sub) == Part.Edge:
                    edges.append(sub)
                elif sub.Wires:
                    basewires.extend(sub.Wires)
                else:
                    basewires.append(Part.Wire(sub.Edges))
            self.edges = edges
            for edgelist in Part.sortEdges(edges):
                basewires.append(Part.Wire(edgelist))

            self.basewires.extend(basewires)

            for w in self.adjustWirePlacement(obj, base, basewires):
                self.adjusted_basewires.append(w)
                wire = PathOpTools.offsetWire(w, base.Shape, offset, True)
                if wire:
                    wires.append(wire)

        self.wires = wires
        self.buildpathocc(obj, wires, [depth], True)
Esempio n. 4
0
 def setupContextMenu(self, vobj, menu):
     PathLog.track()
     for action in menu.actions():
         menu.removeAction(action)
     action = QtGui.QAction(translate('Path', 'Edit'), menu)
     action.triggered.connect(self.setEdit)
     menu.addAction(action)
Esempio n. 5
0
 def deleteBase(self):
     PathLog.track()
     selected = self.form.baseList.selectedItems()
     for item in selected:
         self.form.baseList.takeItem(self.form.baseList.row(item))
         self.setDirty()
     self.updateBase()
Esempio n. 6
0
 def cleanup(self, resetEdit):
     PathLog.track()
     self.vobj.Proxy.resetTaskPanel()
     FreeCADGui.Control.closeDialog()
     if resetEdit:
         FreeCADGui.ActiveDocument.resetEdit()
     FreeCAD.ActiveDocument.recompute()
Esempio n. 7
0
    def updateSelection(self):
        sel = FreeCADGui.Selection.getSelectionEx()

        PathLog.track(len(sel))
        if len(sel) == 1 and len(sel[0].SubObjects) == 1:
            if 'Vertex' == sel[0].SubObjects[0].ShapeType:
                self.form.orientGroup.setEnabled(False)
                self.form.setOrigin.setEnabled(True)
                self.form.moveToOrigin.setEnabled(True)
            else:
                self.form.orientGroup.setEnabled(True)
                self.form.setOrigin.setEnabled(False)
                self.form.moveToOrigin.setEnabled(False)
        else:
            self.form.orientGroup.setEnabled(False)
            self.form.setOrigin.setEnabled(False)
            self.form.moveToOrigin.setEnabled(False)

        if len(sel) == 1 and sel[0].Object == self.obj.Base:
            self.form.centerInStock.setEnabled(True)
            self.form.centerInStockXY.setEnabled(True)
        else:
            if len(sel) == 1 and self.obj.Base:
                PathLog.debug("sel = %s / %s" % (sel[0].Object.Label, self.obj.Base.Label))
            else:
                PathLog.debug("sel len = %d" % len(sel))
            self.form.centerInStock.setEnabled(False)
            self.form.centerInStockXY.setEnabled(False)
 def setupContextMenu(self, vobj, menu):
     PathLog.track()
     from PySide import QtCore, QtGui
     edit = QtCore.QCoreApplication.translate('Path', 'Edit', None)
     action = QtGui.QAction(edit, menu)
     action.triggered.connect(self.setEdit)
     menu.addAction(action)
Esempio n. 9
0
 def updateData(self, obj, prop):
     PathLog.track(obj.Label, prop)
     # make sure the resource view providers are setup properly
     if prop == 'Base' and self.obj.Base and self.obj.Base.ViewObject and self.obj.Base.ViewObject.Proxy:
         self.obj.Base.ViewObject.Proxy.onEdit(_OpenCloseResourceEditor)
     if prop == 'Stock' and self.obj.Stock and self.obj.Stock.ViewObject and self.obj.Stock.ViewObject.Proxy:
         self.obj.Stock.ViewObject.Proxy.onEdit(_OpenCloseResourceEditor)
Esempio n. 10
0
def cleanedges(splines, precision):
    '''cleanedges([splines],precision). Convert BSpline curves, Beziers, to arcs that can be used for cnc paths.
    Returns Lines as is. Filters Circle and Arcs for over 180 degrees. Discretizes Ellipses. Ignores other geometry. '''
    PathLog.track()
    edges = []
    for spline in splines:
        if geomType(spline) == "BSplineCurve":
            arcs = spline.Curve.toBiArcs(precision)
            for i in arcs:
                edges.append(Part.Edge(i))

        elif geomType(spline) == "BezierCurve":
            newspline = spline.Curve.toBSpline()
            arcs = newspline.toBiArcs(precision)
            for i in arcs:
                edges.append(Part.Edge(i))

        elif geomType(spline) == "Ellipse":
            edges = curvetowire(spline, 1.0)  # fixme hardcoded value

        elif geomType(spline) == "Circle":
            arcs = filterArcs(spline)
            for i in arcs:
                edges.append(Part.Edge(i))

        elif geomType(spline) == "Line":
            edges.append(spline)

        elif geomType(spline) == "LineSegment":
            edges.append(spline)

        else:
            pass

    return edges
def RegisterViewProvider(name, provider):
    '''RegisterViewProvider(name, provider) ... if an IconViewProvider is created for an object with the given name
    an instance of provider is used instead.'''

    PathLog.track(name)
    global _factory
    _factory[name] = provider
Esempio n. 12
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...")
            removalshapes = []
            for b in obj.Base:
                PathLog.debug("Base item: {}".format(b))
                for sub in b[1]:
                    if "Face" in sub:
                        shape = Part.makeCompound([getattr(b[0].Shape, sub)])
                    else:
                        edges = [getattr(b[0].Shape, sub) for sub in b[1]]
                        shape = Part.makeFace(edges, 'Part::FaceMakerSimple')

                    env = PathUtils.getEnvelope(self.baseobject.Shape, subshape=shape, depthparams=self.depthparams)
                    obj.removalshape = env.cut(self.baseobject.Shape)
                    removalshapes.append((obj.removalshape, False))
        else:  # process the job base object as a whole
            PathLog.debug("processing the whole job base object")

            env = PathUtils.getEnvelope(self.baseobject.Shape, subshape=None, depthparams=self.depthparams)
            obj.removalshape = env.cut(self.baseobject.Shape)
            removalshapes = [(obj.removalshape, False)]
        return removalshapes
Esempio n. 13
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
Esempio n. 14
0
    def setupTemplate(self):
        templateFiles = []
        for path in PathPreferences.searchPaths():
            templateFiles.extend(self.templateFilesIn(path))

        template = {}
        for tFile in templateFiles:
            name = os.path.split(os.path.splitext(tFile)[0])[1][4:]
            if name in template:
                basename = name
                i = 0
                while name in template:
                    i = i + 1
                    name = basename + " (%s)" % i
            PathLog.track(name, tFile)
            template[name] = tFile
        selectTemplate = PathPreferences.defaultJobTemplate()
        index = 0
        self.dialog.jobTemplate.addItem('<none>', '')
        for name in sorted(template.keys()):
            if template[name] == selectTemplate:
                index = self.dialog.jobTemplate.count()
            self.dialog.jobTemplate.addItem(name, template[name])
        self.dialog.jobTemplate.setCurrentIndex(index)
        self.dialog.templateGroup.show()
Esempio n. 15
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))
    def opExecute(self, obj):
        '''opExecute(obj) ... processes all Base features and Locations and collects
        them in a list of positions and radii which is then passed to circularHoleExecute(obj, holes).
        If no Base geometries and no Locations are present, the job's Base is inspected and all
        drillable features are added to Base. In this case appropriate values for depths are also
        calculated and assigned.
        Do not overwrite, implement circularHoleExecute(obj, holes) instead.'''
        PathLog.track()

        def haveLocations(self, obj):
            if PathOp.FeatureLocations & self.opFeatures(obj):
                return len(obj.Locations) != 0
            return False

        holes = []

        for base, subs in obj.Base:
            for sub in subs:
                if self.isHoleEnabled(obj, base, sub):
                    pos = self.holePosition(obj, base, sub)
                    if pos:
                        holes.append({'x': pos.x, 'y': pos.y, 'r': self.holeDiameter(obj, base, sub)})
        if haveLocations(self, obj):
            for location in obj.Locations:
                holes.append({'x': location.x, 'y': location.y, 'r': 0})

        if len(holes) > 0:
            self.circularHoleExecute(obj, holes)
    def areaOpShapes(self, obj):
        '''areaOpShapes(obj) ... returns envelope for all wires formed by the base edges.'''
        PathLog.track()

        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 = []
        if obj.Base:
            basewires = []

            for b in obj.Base:
                edgelist = []
                for sub in b[1]:
                    edgelist.append(getattr(b[0].Shape, sub))
                basewires.append((b[0], findWires(edgelist)))

            for base,wires in basewires:
                for wire in wires:
                    f = Part.makeFace(wire, 'Part::FaceMakerSimple')

                    # shift the compound to the bottom of the base object for
                    # proper sectioning
                    zShift = b[0].Shape.BoundBox.ZMin - f.BoundBox.ZMin
                    newPlace = FreeCAD.Placement(FreeCAD.Vector(0, 0, zShift), f.Placement.Rotation)
                    f.Placement = newPlace
                    env = PathUtils.getEnvelope(base.Shape, subshape=f, depthparams=self.depthparams)
                    shapes.append((env, False))
        return shapes
 def __init__(self, obj):
     PathLog.track(obj.Base.Name)
     self.obj = obj
     self.wire, rapid = PathGeom.wireForPath(obj.Base.Path)
     self.rapid = _RapidEdges(rapid)
     self.edges = self.wire.Edges
     self.baseWire = self.findBottomWire(self.edges)
Esempio n. 19
0
    def areaOpOnChanged(self, obj, prop):
        '''areaOpOnChanged(obj, prop) ... facing specific depths calculation.'''
        PathLog.track(prop)
        if prop == "StepOver" and obj.StepOver == 0:
            obj.StepOver = 1

        # default depths calculation not correct for facing
        if prop == "Base":
            job = PathUtils.findParentJob(obj)
            obj.OpStartDepth = job.Stock.Shape.BoundBox.ZMax

            if len(obj.Base) >= 1:
                print('processing')
                sublist = []
                for i in obj.Base:
                    o = i[0]
                    for s in i[1]:
                        sublist.append(o.Shape.getElement(s))

            # If the operation has a geometry identified the Finaldepth
            # is the top of the bboundbox which includes all features.
            # Otherwise, top of part.

                obj.OpFinalDepth = Part.makeCompound(sublist).BoundBox.ZMax
            else:
                obj.OpFinalDepth = job.Base.Shape.BoundBox.ZMax
Esempio n. 20
0
 def initOperation(self, obj):
     PathLog.track(obj.Label)
     obj.addProperty('App::PropertyDistance',    'Width',      'Deburr', QtCore.QT_TRANSLATE_NOOP('PathDeburr', 'The desired width of the chamfer'))
     obj.addProperty('App::PropertyDistance',    'ExtraDepth', 'Deburr', QtCore.QT_TRANSLATE_NOOP('PathDeburr', 'The additional depth of the tool path'))
     obj.addProperty('App::PropertyEnumeration', 'Join',       'Deburr', QtCore.QT_TRANSLATE_NOOP('PathDeburr', 'How to join chamfer segments'))
     obj.Join = ['Round', 'Miter']
     obj.setEditorMode('Join', 2) # hide for now
    def orderAndFlipEdges(self, inputEdges):
        PathLog.track("entry(%.2f, %.2f, %.2f), exit(%.2f, %.2f, %.2f)" % (self.entry.x, self.entry.y, self.entry.z, self.exit.x, self.exit.y, self.exit.z))
        self.edgesOrder = []
        outputEdges = []
        p0 = self.entry
        lastP = p0
        edges = copy.copy(inputEdges)
        while edges:
            # print("(%.2f, %.2f, %.2f) %d %d" % (p0.x, p0.y, p0.z))
            for e in copy.copy(edges):
                p1 = e.valueAt(e.FirstParameter)
                p2 = e.valueAt(e.LastParameter)
                if PathGeom.pointsCoincide(p1, p0):
                    outputEdges.append((e, False))
                    edges.remove(e)
                    lastP = None
                    p0 = p2
                    debugEdge(e, ">>>>> no flip")
                    break
                elif PathGeom.pointsCoincide(p2, p0):
                    flipped = PathGeom.flipEdge(e)
                    if not flipped is None:
                        outputEdges.append((flipped, True))
                    else:
                        p0 = None
                        cnt = 0
                        for p in reversed(e.discretize(Deflection=0.01)):
                            if not p0 is None:
                                outputEdges.append((Part.Edge(Part.LineSegment(p0, p)), True))
                                cnt = cnt + 1
                            p0 = p
                        PathLog.info("replaced edge with %d straight segments" % cnt)
                    edges.remove(e)
                    lastP = None
                    p0 = p1
                    debugEdge(e, ">>>>> flip")
                    break
                else:
                    debugEdge(e, "<<<<< (%.2f, %.2f, %.2f)" % (p0.x, p0.y, p0.z))

            if lastP == p0:
                self.edgesOrder.append(outputEdges)
                self.edgesOrder.append(edges)
                print('input edges:')
                for e in inputEdges:
                    debugEdge(e, '  ', False)
                print('ordered edges:')
                for e, flip in outputEdges:
                    debugEdge(e, '  %c ' % ('<' if flip else '>'), False)
                print('remaining edges:')
                for e in edges:
                    debugEdge(e, '    ', False)
                raise ValueError("No connection to %s" % (p0))
            elif lastP:
                PathLog.debug("xxxxxx (%.2f, %.2f, %.2f) (%.2f, %.2f, %.2f)" % (p0.x, p0.y, p0.z, lastP.x, lastP.y, lastP.z))
            else:
                PathLog.debug("xxxxxx (%.2f, %.2f, %.2f) -" % (p0.x, p0.y, p0.z))
            lastP = p0
        PathLog.track("-")
        return outputEdges
Esempio n. 22
0
 def onDelete(self, obj, arg2=None):
     '''Called by the view provider, there doesn't seem to be a callback on the obj itself.'''
     PathLog.track(obj.Label, arg2)
     doc = obj.Document
     # the first to tear down are the ops, they depend on other resources
     PathLog.debug('taking down ops: %s' % [o.Name for o in self.allOperations()])
     while obj.Operations.Group:
         op = obj.Operations.Group[0]
         if not op.ViewObject or not hasattr(op.ViewObject.Proxy, 'onDelete') or op.ViewObject.Proxy.onDelete(op.ViewObject, ()):
             doc.removeObject(op.Name)
     obj.Operations.Group = []
     doc.removeObject(obj.Operations.Name)
     obj.Operations = None
     # stock could depend on Base
     if obj.Stock:
         PathLog.debug('taking down stock')
         doc.removeObject(obj.Stock.Name)
         obj.Stock = None
     # base doesn't depend on anything inside job
     if obj.Base:
         PathLog.debug('taking down base')
         doc.removeObject(obj.Base.Name)
         obj.Base = None
     # Tool controllers don't depend on anything
     PathLog.debug('taking down tool controller')
     for tc in obj.ToolController:
         doc.removeObject(tc.Name)
     obj.ToolController = []
Esempio n. 23
0
 def getFields(self):
     PathLog.track()
     if self.obj:
         if hasattr(self.obj, "StartDepth"):
             self.obj.StartDepth = FreeCAD.Units.Quantity(self.form.startDepth.text()).Value
         if hasattr(self.obj, "FinalDepth"):
             self.obj.FinalDepth = FreeCAD.Units.Quantity(self.form.finalDepth.text()).Value
         if hasattr(self.obj, "SafeHeight"):
             self.obj.SafeHeight = FreeCAD.Units.Quantity(self.form.safeHeight.text()).Value
         if hasattr(self.obj, "ClearanceHeight"):
             self.obj.ClearanceHeight = FreeCAD.Units.Quantity(self.form.clearanceHeight.text()).Value
         if hasattr(self.obj, "StepDown"):
             self.obj.StepDown = FreeCAD.Units.Quantity(self.form.stepDown.text()).Value
         if hasattr(self.obj, "OffsetExtra"):
             self.obj.OffsetExtra = FreeCAD.Units.Quantity(self.form.extraOffset.text()).Value
         if hasattr(self.obj, "RollRadius"):
             self.obj.RollRadius = FreeCAD.Units.Quantity(self.form.rollRadius.text()).Value
         if hasattr(self.obj, "UseComp"):
             self.obj.UseComp = self.form.useCompensation.isChecked()
         if hasattr(self.obj, "UseStartPoint"):
             self.obj.UseStartPoint = self.form.useStartPoint.isChecked()
         if hasattr(self.obj, "UseEndPoint"):
             self.obj.UseEndPoint = self.form.useEndPoint.isChecked()
         if hasattr(self.obj, "Direction"):
             self.obj.Direction = str(self.form.direction.currentText())
         if hasattr(self.obj, "ToolController"):
             tc = PathUtils.findToolController(self.obj, self.form.uiToolController.currentText())
             self.obj.ToolController = tc
     self.obj.Proxy.execute(self.obj)
    def setFields(self, obj):
        '''setFields(obj) ... fill form with values from obj'''
        PathLog.track()
        self.form.baseList.blockSignals(True)
        self.form.baseList.clearContents()
        self.form.baseList.setRowCount(0)
        for i, (base, subs) in enumerate(obj.Base):
            for sub in subs:
                self.form.baseList.insertRow(self.form.baseList.rowCount())

                item = QtGui.QTableWidgetItem("%s.%s" % (base.Label, sub))
                item.setFlags(item.flags() | QtCore.Qt.ItemIsUserCheckable)
                if obj.Proxy.isHoleEnabled(obj, base, sub):
                    item.setCheckState(QtCore.Qt.Checked)
                else:
                    item.setCheckState(QtCore.Qt.Unchecked)
                name = "%s.%s" % (base.Name, sub)
                item.setData(self.DataFeatureName, name)
                item.setData(self.DataObject, base)
                item.setData(self.DataObjectSub, sub)
                self.form.baseList.setItem(self.form.baseList.rowCount()-1, 0, item)

                item = QtGui.QTableWidgetItem("{:.3f}".format(obj.Proxy.holeDiameter(obj, base, sub)))
                item.setData(self.DataFeatureName, name)
                item.setData(self.DataObject, base)
                item.setData(self.DataObjectSub, sub)
                item.setTextAlignment(QtCore.Qt.AlignHCenter)
                self.form.baseList.setItem(self.form.baseList.rowCount()-1, 1, item)

        self.form.baseList.resizeColumnToContents(0)
        self.form.baseList.blockSignals(False)
        self.itemActivated()
Esempio n. 25
0
 def setJobDefaults(cls, filePath, jobTemplate, geometryTolerance, curveAccuracy):
     PathLog.track("(%s='%s', %s, %s, %s)" % (cls.DefaultFilePath, filePath, jobTemplate, geometryTolerance, curveAccuracy))
     pref = cls.preferences()
     pref.SetString(cls.DefaultFilePath, filePath)
     pref.SetString(cls.DefaultJobTemplate, jobTemplate)
     pref.SetFloat(cls.GeometryTolerance, geometryTolerance)
     pref.SetFloat(cls.LibAreaCurveAccuracy, curveAccuracy)
Esempio n. 26
0
    def findHoles(self, obj, shape):
        import DraftGeomUtils as dgu
        PathLog.track('obj: {} shape: {}'.format(obj, shape))
        holelist = []
        tooldiameter = obj.ToolController.Proxy.getTool(obj.ToolController).Diameter
        PathLog.debug('search for holes larger than tooldiameter: {}: '.format(tooldiameter))
        if dgu.isPlanar(shape):
            PathLog.debug("shape is planar")
            for i in range(len(shape.Edges)):
                candidateEdgeName = "Edge" + str(i + 1)
                e = shape.getElement(candidateEdgeName)
                if PathUtils.isDrillable(shape, e, tooldiameter):
                    PathLog.debug('edge candidate: {} (hash {})is drillable '.format(e, e.hashCode()))
                    x = e.Curve.Center.x
                    y = e.Curve.Center.y
                    diameter = e.BoundBox.XLength
                    holelist.append({'featureName': candidateEdgeName, 'feature': e, 'x': x, 'y': y, 'd': diameter, 'enabled': True})
        else:
            PathLog.debug("shape is not planar")
            for i in range(len(shape.Faces)):
                candidateFaceName = "Face" + str(i + 1)
                f = shape.getElement(candidateFaceName)
                if PathUtils.isDrillable(shape, f, tooldiameter):
                    PathLog.debug('face candidate: {} is drillable '.format(f))
                    x = f.Surface.Center.x
                    y = f.Surface.Center.y
                    diameter = f.BoundBox.XLength
                    holelist.append({'featureName': candidateFaceName, 'feature': f, 'x': x, 'y': y, 'd': diameter, 'enabled': True})

        PathLog.debug("holes found: {}".format(holelist))
        return holelist
Esempio n. 27
0
    def setupUi(self):
        PathLog.track()

        # Connect Signals and Slots
        self.form.startDepth.editingFinished.connect(self.getFields)
        self.form.finalDepth.editingFinished.connect(self.getFields)
        self.form.safeHeight.editingFinished.connect(self.getFields)
        self.form.retractHeight.editingFinished.connect(self.getFields)
        self.form.peckDepth.editingFinished.connect(self.getFields)
        self.form.clearanceHeight.editingFinished.connect(self.getFields)
        self.form.dwellTime.editingFinished.connect(self.getFields)
        self.form.dwellEnabled.stateChanged.connect(self.getFields)
        self.form.peckEnabled.stateChanged.connect(self.getFields)

        # buttons
        self.form.uiEnableSelected.clicked.connect(self.enableSelected)
        self.form.uiDisableSelected.clicked.connect(self.disableSelected)
        self.form.uiFindAllHoles.clicked.connect(self.findAll)
        self.form.uiAddSelected.clicked.connect(self.addSelected)

        self.form.baseList.itemSelectionChanged.connect(self.itemActivated)
        self.form.baseList.itemChanged.connect(self.checkedChanged)

        self.form.uiToolController.currentIndexChanged.connect(self.getFields)

        self.setFields()
Esempio n. 28
0
    def __init__(self, vobj):
        PathLog.track()
        vobj.Proxy = self
        self.vobj = vobj
        self.panel = None

        self.debugDisplay()
Esempio n. 29
0
 def updateToolType(self):
     PathLog.track()
     self.form.blockSignals(True)
     self.tool.ToolType = self.getType(self.form.toolType.currentIndex())
     self.setupToolType(self.tool.ToolType)
     self.updateUI()
     self.form.blockSignals(False)
Esempio n. 30
0
 def opSetDefaultValues(self, obj, job):
     PathLog.track(obj.Label, job.Label)
     obj.Width = '1 mm'
     obj.ExtraDepth = '0.1 mm'
     obj.Join = 'Round'
     obj.setExpression('StepDown', '0 mm')
     obj.StepDown = '0 mm'
Esempio n. 31
0
 def setModelData(self, widget, model, index):
     PathLog.track(index.row(), index.column())
     editor = index.data(self.EditorRole)
     editor.setModelData(widget)
     index.model().setData(index, editor.prop.displayString(),
                           QtCore.Qt.DisplayRole)
    def generateTags(self, obj, count, width=None, height=None, angle=None, radius=None, spacing=None):
        # pylint: disable=unused-argument
        PathLog.track(count, width, height, angle, spacing)
        # for e in self.baseWire.Edges:
        #    debugMarker(e.Vertexes[0].Point, 'base', (0.0, 1.0, 1.0), 0.2)

        if spacing:
            tagDistance = spacing
        else:
            tagDistance = self.baseWire.Length / (count if count else 4)

        W = width  if width  else self.defaultTagWidth()
        H = height if height else self.defaultTagHeight()
        A = angle  if angle  else self.defaultTagAngle()
        R = radius if radius else self.defaultTagRadius()

        # start assigning tags on the longest segment
        (shortestEdge, longestEdge) = self.shortestAndLongestPathEdge()
        startIndex = 0
        for i in range(0, len(self.baseWire.Edges)):
            edge = self.baseWire.Edges[i]
            PathLog.debug('  %d: %.2f' % (i, edge.Length))
            if PathGeom.isRoughly(edge.Length, longestEdge.Length):
                startIndex = i
                break

        startEdge = self.baseWire.Edges[startIndex]
        startCount = int(startEdge.Length / tagDistance)
        if (longestEdge.Length - shortestEdge.Length) > shortestEdge.Length:
            startCount = int(startEdge.Length / tagDistance) + 1

        lastTagLength = (startEdge.Length + (startCount - 1) * tagDistance) / 2
        currentLength = startEdge.Length

        minLength = min(2. * W, longestEdge.Length)

        PathLog.debug("length=%.2f shortestEdge=%.2f(%.2f) longestEdge=%.2f(%.2f) minLength=%.2f" % (self.baseWire.Length, shortestEdge.Length, shortestEdge.Length/self.baseWire.Length, longestEdge.Length, longestEdge.Length / self.baseWire.Length, minLength))
        PathLog.debug("   start: index=%-2d count=%d (length=%.2f, distance=%.2f)" % (startIndex, startCount, startEdge.Length, tagDistance))
        PathLog.debug("               -> lastTagLength=%.2f)" % lastTagLength)
        PathLog.debug("               -> currentLength=%.2f)" % currentLength)

        edgeDict = {startIndex: startCount}

        for i in range(startIndex + 1, len(self.baseWire.Edges)):
            edge = self.baseWire.Edges[i]
            (currentLength, lastTagLength) = self.processEdge(i, edge, currentLength, lastTagLength, tagDistance, minLength, edgeDict)
        for i in range(0, startIndex):
            edge = self.baseWire.Edges[i]
            (currentLength, lastTagLength) = self.processEdge(i, edge, currentLength, lastTagLength, tagDistance, minLength, edgeDict)

        tags = []

        for (i, count) in PathUtil.keyValueIter(edgeDict):
            edge = self.baseWire.Edges[i]
            PathLog.debug(" %d: %d" % (i, count))
            # debugMarker(edge.Vertexes[0].Point, 'base', (1.0, 0.0, 0.0), 0.2)
            # debugMarker(edge.Vertexes[1].Point, 'base', (0.0, 1.0, 0.0), 0.2)
            if 0 != count:
                distance = (edge.LastParameter - edge.FirstParameter) / count
                for j in range(0, count):
                    tag = edge.Curve.value((j+0.5) * distance)
                    tags.append(Tag(j, tag.x, tag.y, W, H, A, R, True))

        return tags
    def cleanupEdges(self, edges):
        # want to remove all edges from the wire itself, and all internal struts
        PathLog.track("+cleanupEdges")
        PathLog.debug(" edges:")
        if not edges:
            return edges
        for e in edges:
            debugEdge(e, '   ')
        PathLog.debug(":")
        self.edgesCleanup = [copy.copy(edges)]

        # remove any edge that has a point inside the tag solid
        # and collect all edges that are connected to the entry and/or exit
        self.entryEdges = []
        self.exitEdges = []
        self.edgePoints = []
        for e in copy.copy(edges):
            p1 = e.valueAt(e.FirstParameter)
            p2 = e.valueAt(e.LastParameter)
            self.edgePoints.append(p1)
            self.edgePoints.append(p2)
            if self.tag.solid.isInside(p1, PathGeom.Tolerance, False) or self.tag.solid.isInside(p2, PathGeom.Tolerance, False):
                edges.remove(e)
                debugEdge(e, '......... X0', False)
            else:
                if PathGeom.pointsCoincide(p1, self.entry) or PathGeom.pointsCoincide(p2, self.entry):
                    self.entryEdges.append(e)
                if PathGeom.pointsCoincide(p1, self.exit) or PathGeom.pointsCoincide(p2, self.exit):
                    self.exitEdges.append(e)
        self.edgesCleanup.append(copy.copy(edges))

        # if there are no edges connected to entry/exit, it means the plunge in/out is vertical
        # we need to add in the missing segment and collect the new entry/exit edges.
        if not self.entryEdges:
            PathLog.debug("fill entryEdges ...")
            self.realEntry = sorted(self.edgePoints, key=lambda p: (p - self.entry).Length)[0]
            self.entryEdges = list([e for e in edges if PathGeom.edgeConnectsTo(e, self.realEntry)])
            edges.append(Part.Edge(Part.LineSegment(self.entry, self.realEntry)))
        else:
            self.realEntry = None
        if not self.exitEdges:
            PathLog.debug("fill exitEdges ...")
            self.realExit = sorted(self.edgePoints, key=lambda p: (p - self.exit).Length)[0]
            self.exitEdges = list([e for e in edges if PathGeom.edgeConnectsTo(e, self.realExit)])
            edges.append(Part.Edge(Part.LineSegment(self.realExit, self.exit)))
        else:
            self.realExit = None
        self.edgesCleanup.append(copy.copy(edges))

        # if there are 2 edges attached to entry/exit, throw away the one that is "lower"
        if len(self.entryEdges) > 1:
            debugEdge(self.entryEdges[0], ' entry[0]', False)
            debugEdge(self.entryEdges[1], ' entry[1]', False)
            if self.entryEdges[0].BoundBox.ZMax < self.entryEdges[1].BoundBox.ZMax:
                edges.remove(self.entryEdges[0])
                debugEdge(e, '......... X1', False)
            else:
                edges.remove(self.entryEdges[1])
                debugEdge(e, '......... X2', False)
        if len(self.exitEdges) > 1:
            debugEdge(self.exitEdges[0], ' exit[0]', False)
            debugEdge(self.exitEdges[1], ' exit[1]', False)
            if self.exitEdges[0].BoundBox.ZMax < self.exitEdges[1].BoundBox.ZMax:
                if self.exitEdges[0] in edges:
                    edges.remove(self.exitEdges[0])
                debugEdge(e, '......... X3', False)
            else:
                if self.exitEdges[1] in edges:
                    edges.remove(self.exitEdges[1])
                debugEdge(e, '......... X4', False)

        self.edgesCleanup.append(copy.copy(edges))
        return edges
Esempio n. 34
0
 def attach(self, vobj):
     PathLog.track()
     self.vobj = vobj
     self.obj = vobj.Object
Esempio n. 35
0
 def __init__(self, vobj, name):
     PathLog.track(name)
     vobj.Proxy = self
     self.icon = name
     mode = 2
Esempio n. 36
0
 def updateModel(self, recomp=True):
     PathLog.track()
     self.getFields()
     self.updateUI()
     if recomp:
         FreeCAD.ActiveDocument.recompute()
Esempio n. 37
0
 def accept(self):
     if any([op.accept() for op in self.ops]):
         PathLog.track()
Esempio n. 38
0
 def templateFilesIn(self, path):
     '''templateFilesIn(path) ... answer all file in the given directory which fit the job template naming convention.
     PathJob template files are name job_*.xml'''
     PathLog.track(path)
     return glob.glob(path + '/job_*.xml')
Esempio n. 39
0
    def opExecute(self, obj, getsim=False):  # pylint: disable=arguments-differ
        '''opExecute(obj, getsim=False) ... implementation of Path.Area ops.
        determines the parameters for _buildPathArea().
        Do not overwrite, implement
            areaOpAreaParams(obj, isHole) ... op specific area param dictionary
            areaOpPathParams(obj, isHole) ... op specific path param dictionary
            areaOpShapes(obj)             ... the shape for path area to process
            areaOpUseProjection(obj)      ... return true if operation can use projection
        instead.'''
        PathLog.track()

        # Instantiate class variables for operation reference
        self.endVector = None  # pylint: disable=attribute-defined-outside-init
        self.rotateFlag = False  # pylint: disable=attribute-defined-outside-init
        self.leadIn = 2.0  # pylint: disable=attribute-defined-outside-init
        self.cloneNames = []  # pylint: disable=attribute-defined-outside-init
        self.guiMsgs = []  # pylint: disable=attribute-defined-outside-init
        self.tempObjectNames = []  # pylint: disable=attribute-defined-outside-init
        self.stockBB = PathUtils.findParentJob(obj).Stock.Shape.BoundBox  # pylint: disable=attribute-defined-outside-init
        self.useTempJobClones(
            'Delete')  # Clear temporary group and recreate for temp job clones

        if obj.EnableRotation != 'Off':
            # Calculate operation heights based upon rotation radii
            opHeights = self.opDetermineRotationRadii(obj)
            (self.xRotRad, self.yRotRad, self.zRotRad) = opHeights[0]  # pylint: disable=attribute-defined-outside-init
            (self.clrOfset, self.safOfst) = opHeights[1]  # pylint: disable=attribute-defined-outside-init

            # Set clearance and safe heights based upon rotation radii
            if obj.EnableRotation == 'A(x)':
                strDep = self.xRotRad
            elif obj.EnableRotation == 'B(y)':
                strDep = self.yRotRad
            else:
                strDep = max(self.xRotRad, self.yRotRad)
            finDep = -1 * strDep

            obj.ClearanceHeight.Value = strDep + self.clrOfset
            obj.SafeHeight.Value = strDep + self.safOfst

            #if self.initWithRotation is False:
            #    if obj.FinalDepth.Value == obj.OpFinalDepth.Value:
            #        obj.FinalDepth.Value = finDep
            #    if obj.StartDepth.Value == obj.OpStartDepth.Value:
            #        obj.StartDepth.Value = strDep

            # Create visual axes when debugging.
            if PathLog.getLevel(PathLog.thisModule()) == 4:
                self.visualAxis()
        else:
            strDep = obj.StartDepth.Value
            finDep = obj.FinalDepth.Value

        # Set axial feed rates based upon horizontal feed rates
        safeCircum = 2 * math.pi * obj.SafeHeight.Value
        self.axialFeed = 360 / safeCircum * self.horizFeed  # pylint: disable=attribute-defined-outside-init
        self.axialRapid = 360 / safeCircum * self.horizRapid  # pylint: disable=attribute-defined-outside-init

        # Initiate depthparams and calculate operation heights for rotational operation
        finish_step = obj.FinishDepth.Value if hasattr(obj,
                                                       "FinishDepth") else 0.0
        self.depthparams = PathUtils.depth_params(  # pylint: disable=attribute-defined-outside-init
            clearance_height=obj.ClearanceHeight.Value,
            safe_height=obj.SafeHeight.Value,
            start_depth=obj.StartDepth.Value,
            step_down=obj.StepDown.Value,
            z_finish_step=finish_step,
            final_depth=obj.FinalDepth.Value,
            user_depths=None)

        # Set start point
        if PathOp.FeatureStartPoint & self.opFeatures(
                obj) and obj.UseStartPoint:
            start = obj.StartPoint
        else:
            start = None

        aOS = self.areaOpShapes(obj)  # pylint: disable=assignment-from-no-return

        # Adjust tuples length received from other PathWB tools/operations beside PathPocketShape
        shapes = []
        for shp in aOS:
            if len(shp) == 2:
                (fc, iH) = shp
                #     fc, iH,   sub,   angle, axis,      strtDep,             finDep
                tup = fc, iH, 'otherOp', 0.0, 'S', obj.StartDepth.Value, obj.FinalDepth.Value
                shapes.append(tup)
            else:
                shapes.append(shp)

        if len(shapes) > 1:
            jobs = [{
                'x': s[0].BoundBox.XMax,
                'y': s[0].BoundBox.YMax,
                'shape': s
            } for s in shapes]

            jobs = PathUtils.sort_jobs(jobs, ['x', 'y'])

            shapes = [j['shape'] for j in jobs]

        sims = []
        numShapes = len(shapes)

        for ns in range(0, numShapes):
            (shape, isHole, sub, angle, axis, strDep, finDep) = shapes[ns]  # pylint: disable=unused-variable
            if ns < numShapes - 1:
                nextAxis = shapes[ns + 1][4]
            else:
                nextAxis = 'L'

            finish_step = obj.FinishDepth.Value if hasattr(
                obj, "FinishDepth") else 0.0
            self.depthparams = PathUtils.depth_params(  # pylint: disable=attribute-defined-outside-init
                clearance_height=obj.ClearanceHeight.Value,
                safe_height=obj.SafeHeight.Value,
                start_depth=strDep,  # obj.StartDepth.Value,
                step_down=obj.StepDown.Value,
                z_finish_step=finish_step,
                final_depth=finDep,  # obj.FinalDepth.Value,
                user_depths=None)

            try:
                (pp, sim) = self._buildPathArea(obj, shape, isHole, start,
                                                getsim)
            except Exception as e:  # pylint: disable=broad-except
                FreeCAD.Console.PrintError(e)
                FreeCAD.Console.PrintError(
                    "Something unexpected happened. Check project and tool config."
                )
            else:
                ppCmds = pp.Commands
                if obj.EnableRotation != 'Off' and self.rotateFlag is True:
                    # Rotate model to index for cut
                    if axis == 'X':
                        axisOfRot = 'A'
                    elif axis == 'Y':
                        axisOfRot = 'B'
                        # Reverse angle temporarily to match model. Error in FreeCAD render of B axis rotations
                        if obj.B_AxisErrorOverride is True:
                            angle = -1 * angle
                    elif axis == 'Z':
                        axisOfRot = 'C'
                    else:
                        axisOfRot = 'A'
                    # Rotate Model to correct angle
                    ppCmds.insert(
                        0,
                        Path.Command('G1', {
                            axisOfRot: angle,
                            'F': self.axialFeed
                        }))
                    ppCmds.insert(0, Path.Command('N100', {}))

                    # Raise cutter to safe depth and return index to starting position
                    ppCmds.append(Path.Command('N200', {}))
                    ppCmds.append(
                        Path.Command('G0', {
                            'Z': obj.SafeHeight.Value,
                            'F': self.vertRapid
                        }))
                    if axis != nextAxis:
                        ppCmds.append(
                            Path.Command('G0', {
                                axisOfRot: 0.0,
                                'F': self.axialRapid
                            }))
                # Eif

                # Save gcode commands to object command list
                self.commandlist.extend(ppCmds)
                sims.append(sim)
            # Eif

            if self.areaOpRetractTool(obj):
                self.endVector = None  # pylint: disable=attribute-defined-outside-init

        # Raise cutter to safe height and rotate back to original orientation
        if self.rotateFlag is True:
            self.commandlist.append(
                Path.Command('G0', {
                    'Z': obj.SafeHeight.Value,
                    'F': self.vertRapid
                }))
            self.commandlist.append(
                Path.Command('G0', {
                    'A': 0.0,
                    'F': self.axialRapid
                }))
            self.commandlist.append(
                Path.Command('G0', {
                    'B': 0.0,
                    'F': self.axialRapid
                }))

        self.useTempJobClones(
            'Delete')  # Delete temp job clone group and contents
        self.guiMessage('title', None,
                        show=True)  # Process GUI messages to user
        for ton in self.tempObjectNames:  # remove temporary objects by name
            FreeCAD.ActiveDocument.removeObject(ton)
        PathLog.debug("obj.Name: " + str(obj.Name) + "\n\n")
        return sims
Esempio n. 40
0
 def setEditorData(self, widget, index):
     PathLog.track(index.row(), index.column())
     index.data(self.EditorRole).setEditorData(widget)
Esempio n. 41
0
def lastPathToolLibrary():
    PathLog.track()
    return preferences().GetString(LastPathToolLibrary,
                                   pathDefaultToolsPath('Library'))
Esempio n. 42
0
    def faceRotationAnalysis(self, obj, norm, surf):
        '''faceRotationAnalysis(obj, norm, surf)
            Determine X and Y independent rotation necessary to make normalAt = Z=1 (0,0,1) '''
        PathLog.track()

        praInfo = "faceRotationAnalysis()"
        rtn = True
        orientation = 'X'
        angle = 500.0
        precision = 6

        for i in range(0, 13):
            if PathGeom.Tolerance * (i * 10) == 1.0:
                precision = i
                break

        def roundRoughValues(precision, val):
            # Convert VALxe-15 numbers to zero
            if PathGeom.isRoughly(0.0, val) is True:
                return 0.0
            # Convert VAL.99999999 to next integer
            elif math.fabs(val % 1) > 1.0 - PathGeom.Tolerance:
                return round(val)
            else:
                return round(val, precision)

        nX = roundRoughValues(precision, norm.x)
        nY = roundRoughValues(precision, norm.y)
        nZ = roundRoughValues(precision, norm.z)
        praInfo += "\n -normalAt(0,0): " + str(nX) + ", " + str(
            nY) + ", " + str(nZ)

        saX = roundRoughValues(precision, surf.x)
        saY = roundRoughValues(precision, surf.y)
        saZ = roundRoughValues(precision, surf.z)
        praInfo += "\n -Surface.Axis: " + str(saX) + ", " + str(
            saY) + ", " + str(saZ)

        # Determine rotation needed and current orientation
        if saX == 0.0:
            if saY == 0.0:
                orientation = "Z"
                if saZ == 1.0:
                    angle = 0.0
                elif saZ == -1.0:
                    angle = -180.0
                else:
                    praInfo += "_else_X" + str(saZ)
            elif saY == 1.0:
                orientation = "Y"
                angle = 90.0
            elif saY == -1.0:
                orientation = "Y"
                angle = -90.0
            else:
                if saZ != 0.0:
                    angle = math.degrees(math.atan(saY / saZ))
                    orientation = "Y"
        elif saY == 0.0:
            if saZ == 0.0:
                orientation = "X"
                if saX == 1.0:
                    angle = -90.0
                elif saX == -1.0:
                    angle = 90.0
                else:
                    praInfo += "_else_X" + str(saX)
            else:
                orientation = "X"
                ratio = saX / saZ
                angle = math.degrees(math.atan(ratio))
                if ratio < 0.0:
                    praInfo += " NEG-ratio"
                    # angle -= 90
                else:
                    praInfo += " POS-ratio"
                    angle = -1 * angle
                    if saX < 0.0:
                        angle = angle + 180.0
        elif saZ == 0.0:
            # if saY != 0.0:
            angle = math.degrees(math.atan(saX / saY))
            orientation = "Y"

        if saX + nX == 0.0:
            angle = -1 * angle
        if saY + nY == 0.0:
            angle = -1 * angle
        if saZ + nZ == 0.0:
            angle = -1 * angle

        if saY == -1.0 or saY == 1.0:
            if nX != 0.0:
                angle = -1 * angle

        # Enforce enabled rotation in settings
        praInfo += "\n -Initial orientation:  {}".format(orientation)
        if orientation == 'Y':
            axis = 'X'
            if obj.EnableRotation == 'B(y)':  # Required axis disabled
                if angle == 180.0 or angle == -180.0:
                    axis = 'Y'
                else:
                    rtn = False
        elif orientation == 'X':
            axis = 'Y'
            if obj.EnableRotation == 'A(x)':  # Required axis disabled
                if angle == 180.0 or angle == -180.0:
                    axis = 'X'
                else:
                    rtn = False
        elif orientation == 'Z':
            axis = 'X'

        if math.fabs(angle) == 0.0:
            angle = 0.0
            rtn = False

        if angle == 500.0:
            angle = 0.0
            rtn = False

        if rtn is False:
            if orientation == 'Z' and angle == 0.0 and obj.ReverseDirection is True:
                if obj.EnableRotation == 'B(y)':
                    axis = 'Y'
                rtn = True

        if rtn is True:
            self.rotateFlag = True  # pylint: disable=attribute-defined-outside-init
            if obj.ReverseDirection is True:
                if angle < 180.0:
                    angle = angle + 180.0
                else:
                    angle = angle - 180.0
            angle = round(angle, precision)

        praInfo += "\n -Rotation analysis:  angle: " + str(
            angle) + ",   axis: " + str(axis)
        if rtn is True:
            praInfo += "\n - ... rotation triggered"
        else:
            praInfo += "\n - ... NO rotation triggered"

        PathLog.debug("\n" + str(praInfo))

        return (rtn, angle, axis, praInfo)
Esempio n. 43
0
def CreateFromTemplate(job, template):
    if template.get('version') and 1 == int(template['version']):
        stockType = template.get('create')
        if stockType:
            placement = None
            posX = template.get('posX')
            posY = template.get('posY')
            posZ = template.get('posZ')
            rotX = template.get('rotX')
            rotY = template.get('rotY')
            rotZ = template.get('rotZ')
            rotW = template.get('rotW')
            if posX is not None and posY is not None and posZ is not None and rotX is not None and rotY is not None and rotZ is not None and rotW is not None:
                pos = FreeCAD.Vector(float(posX), float(posY), float(posZ))
                rot = FreeCAD.Rotation(float(rotX), float(rotY), float(rotZ),
                                       float(rotW))
                placement = FreeCAD.Placement(pos, rot)
            elif posX is not None or posY is not None or posZ is not None or rotX is not None or rotY is not None or rotZ is not None or rotW is not None:
                PathLog.warning(
                    translate(
                        'PathStock',
                        'Corrupted or incomplete placement information in template - ignoring'
                    ))

            if stockType == StockType.FromBase:
                xneg = template.get('xneg')
                xpos = template.get('xpos')
                yneg = template.get('yneg')
                ypos = template.get('ypos')
                zneg = template.get('zneg')
                zpos = template.get('zpos')
                neg = None
                pos = None
                if xneg is not None and xpos is not None and yneg is not None and ypos is not None and zneg is not None and zpos is not None:
                    neg = FreeCAD.Vector(
                        FreeCAD.Units.Quantity(xneg).Value,
                        FreeCAD.Units.Quantity(yneg).Value,
                        FreeCAD.Units.Quantity(zneg).Value)
                    pos = FreeCAD.Vector(
                        FreeCAD.Units.Quantity(xpos).Value,
                        FreeCAD.Units.Quantity(ypos).Value,
                        FreeCAD.Units.Quantity(zpos).Value)
                elif xneg is not None or xpos is not None or yneg is not None or ypos is not None or zneg is not None or zpos is not None:
                    PathLog.error(
                        translate(
                            'PathStock',
                            'Corrupted or incomplete specification for creating stock from base - ignoring extent'
                        ))
                return CreateFromBase(job, neg, pos, placement)

            if stockType == StockType.CreateBox:
                PathLog.track(' create box')
                length = template.get('length')
                width = template.get('width')
                height = template.get('height')
                extent = None
                if length is not None and width is not None and height is not None:
                    PathLog.track('  have extent')
                    extent = FreeCAD.Vector(
                        FreeCAD.Units.Quantity(length).Value,
                        FreeCAD.Units.Quantity(width).Value,
                        FreeCAD.Units.Quantity(height).Value)
                elif length is not None or width is not None or height is not None:
                    PathLog.error(
                        translate(
                            'PathStock',
                            'Corrupted or incomplete size for creating a stock box - ignoring size'
                        ))
                else:
                    PathLog.track(
                        "  take placement (%s) and extent (%s) from model" %
                        (placement, extent))
                return CreateBox(job, extent, placement)

            if stockType == StockType.CreateCylinder:
                radius = template.get('radius')
                height = template.get('height')
                if radius is not None and height is not None:
                    pass
                elif radius is not None or height is not None:
                    radius = None
                    height = None
                    PathLog.error(
                        translate(
                            'PathStock',
                            'Corrupted or incomplete size for creating a stock cylinder - ignoring size'
                        ))
                return CreateCylinder(job, radius, height, placement)

            PathLog.error(
                translate('PathStock',
                          'Unsupported stock type named {}').format(stockType))
        else:
            PathLog.error(
                translate('PathStock',
                          'Unsupported PathStock template version {}').format(
                              template.get('version')))
        return None
Esempio n. 44
0
    def _buildPathArea(self, obj, baseobject, isHole, start, getsim):
        '''_buildPathArea(obj, baseobject, isHole, start, getsim) ... internal function.'''
        # pylint: disable=unused-argument
        PathLog.track()
        area = Path.Area()
        area.setPlane(PathUtils.makeWorkplane(baseobject))
        area.add(baseobject)

        areaParams = self.areaOpAreaParams(obj, isHole)  # pylint: disable=assignment-from-no-return

        heights = [i for i in self.depthparams]
        PathLog.debug('depths: {}'.format(heights))
        area.setParams(**areaParams)
        obj.AreaParams = str(area.getParams())

        PathLog.debug("Area with params: {}".format(area.getParams()))

        sections = area.makeSections(mode=0,
                                     project=self.areaOpUseProjection(obj),
                                     heights=heights)
        PathLog.debug("sections = %s" % sections)
        shapelist = [sec.getShape() for sec in sections]
        PathLog.debug("shapelist = %s" % shapelist)

        pathParams = self.areaOpPathParams(obj, isHole)  # pylint: disable=assignment-from-no-return
        pathParams['shapes'] = shapelist
        pathParams['feedrate'] = self.horizFeed
        pathParams['feedrate_v'] = self.vertFeed
        pathParams['verbose'] = True
        pathParams['resume_height'] = obj.SafeHeight.Value
        pathParams['retraction'] = obj.ClearanceHeight.Value
        pathParams['return_end'] = True
        # Note that emitting preambles between moves breaks some dressups and prevents path optimization on some controllers
        pathParams['preamble'] = False

        if not self.areaOpRetractTool(obj):
            pathParams['threshold'] = 2.001 * self.radius

        if self.endVector is not None:
            pathParams['start'] = self.endVector
        elif PathOp.FeatureStartPoint & self.opFeatures(
                obj) and obj.UseStartPoint:
            pathParams['start'] = obj.StartPoint

        obj.PathParams = str({
            key: value
            for key, value in pathParams.items() if key != 'shapes'
        })
        PathLog.debug("Path with params: {}".format(obj.PathParams))

        (pp, end_vector) = Path.fromShapes(**pathParams)
        PathLog.debug('pp: {}, end vector: {}'.format(pp, end_vector))
        self.endVector = end_vector  # pylint: disable=attribute-defined-outside-init

        simobj = None
        if getsim:
            areaParams['Thicken'] = True
            areaParams['ToolRadius'] = self.radius - self.radius * .005
            area.setParams(**areaParams)
            sec = area.makeSections(mode=0, project=False,
                                    heights=heights)[-1].getShape()
            simobj = sec.extrude(FreeCAD.Vector(0, 0,
                                                baseobject.BoundBox.ZMax))

        return pp, simobj
Esempio n. 45
0
    def opExecute(self, obj, getsim=False): # pylint: disable=arguments-differ
        '''opExecute(obj, getsim=False) ... implementation of Path.Area ops.
        determines the parameters for _buildPathArea().
        Do not overwrite, implement
            areaOpAreaParams(obj, isHole) ... op specific area param dictionary
            areaOpPathParams(obj, isHole) ... op specific path param dictionary
            areaOpShapes(obj)             ... the shape for path area to process
            areaOpUseProjection(obj)      ... return true if operation can use projection
        instead.'''
        PathLog.track()

        # Instantiate class variables for operation reference
        self.endVector = None # pylint: disable=attribute-defined-outside-init
        self.rotateFlag = False # pylint: disable=attribute-defined-outside-init
        self.leadIn = 2.0  # pylint: disable=attribute-defined-outside-init
        self.cloneNames = [] # pylint: disable=attribute-defined-outside-init
        self.guiMsgs = []  # pylint: disable=attribute-defined-outside-init
        self.tempObjectNames = []  # pylint: disable=attribute-defined-outside-init
        self.stockBB = PathUtils.findParentJob(obj).Stock.Shape.BoundBox # pylint: disable=attribute-defined-outside-init
        self.useTempJobClones('Delete')  # Clear temporary group and recreate for temp job clones
        self.rotStartDepth = None  # pylint: disable=attribute-defined-outside-init

        if obj.EnableRotation != 'Off':
            # Calculate operation heights based upon rotation radii
            opHeights = self.opDetermineRotationRadii(obj)
            (self.xRotRad, self.yRotRad, self.zRotRad) = opHeights[0] # pylint: disable=attribute-defined-outside-init
            (self.clrOfset, self.safOfst) = opHeights[1] # pylint: disable=attribute-defined-outside-init

            # Set clearance and safe heights based upon rotation radii
            if obj.EnableRotation == 'A(x)':
                strDep = self.xRotRad
            elif obj.EnableRotation == 'B(y)':
                strDep = self.yRotRad
            else:
                strDep = max(self.xRotRad, self.yRotRad)
            finDep = -1 * strDep

            self.rotStartDepth = strDep
            obj.ClearanceHeight.Value = strDep + self.clrOfset
            obj.SafeHeight.Value = strDep + self.safOfst

            # Create visual axes when debugging.
            if PathLog.getLevel(PathLog.thisModule()) == 4:
                self.visualAxis()
        else:
            strDep = obj.StartDepth.Value
            finDep = obj.FinalDepth.Value

        # Set axial feed rates based upon horizontal feed rates
        safeCircum = 2 * math.pi * obj.SafeHeight.Value
        self.axialFeed = 360 / safeCircum * self.horizFeed # pylint: disable=attribute-defined-outside-init
        self.axialRapid = 360 / safeCircum * self.horizRapid # pylint: disable=attribute-defined-outside-init

        # Initiate depthparams and calculate operation heights for rotational operation
        self.depthparams = self._customDepthParams(obj, obj.StartDepth.Value, obj.FinalDepth.Value)

        # Set start point
        if PathOp.FeatureStartPoint & self.opFeatures(obj) and obj.UseStartPoint:
            start = obj.StartPoint
        else:
            start = None

        aOS = self.areaOpShapes(obj) # pylint: disable=assignment-from-no-return

        # Adjust tuples length received from other PathWB tools/operations beside PathPocketShape
        shapes = []
        for shp in aOS:
            if len(shp) == 2:
                (fc, iH) = shp
                #     fc, iH,   sub,   angle, axis,      strtDep,             finDep
                tup = fc, iH, 'otherOp', 0.0, 'S', obj.StartDepth.Value, obj.FinalDepth.Value
                shapes.append(tup)
            else:
                shapes.append(shp)

        if len(shapes) > 1:
            jobs = list()
            for s in shapes:
                if s[2] == 'OpenEdge':
                    shp = Part.makeCompound(s[0])
                else:
                    shp = s[0]
                jobs.append({
                    'x': shp.BoundBox.XMax,
                    'y': shp.BoundBox.YMax,
                    'shape': s
                })

            jobs = PathUtils.sort_jobs(jobs, ['x', 'y'])

            shapes = [j['shape'] for j in jobs]

        sims = []
        numShapes = len(shapes)
        for ns in range(0, numShapes):
            profileEdgesIsOpen = False
            (shape, isHole, sub, angle, axis, strDep, finDep) = shapes[ns] # pylint: disable=unused-variable
            if sub == 'OpenEdge':
                profileEdgesIsOpen = True
                if PathOp.FeatureStartPoint & self.opFeatures(obj) and obj.UseStartPoint:
                    osp = obj.StartPoint
                    self.commandlist.append(Path.Command('G0', {'X': osp.x, 'Y': osp.y, 'F': self.horizRapid}))

            if ns < numShapes - 1:
                nextAxis = shapes[ns + 1][4]
            else:
                nextAxis = 'L'

            self.depthparams = self._customDepthParams(obj, strDep, finDep)

            try:
                if profileEdgesIsOpen:
                    (pp, sim) = self._buildProfileOpenEdges(obj, shape, isHole, start, getsim)
                else:
                    (pp, sim) = self._buildPathArea(obj, shape, isHole, start, getsim)
            except Exception as e: # pylint: disable=broad-except
                FreeCAD.Console.PrintError(e)
                FreeCAD.Console.PrintError("Something unexpected happened. Check project and tool config.")
            else:
                if profileEdgesIsOpen:
                    ppCmds = pp
                else:
                    ppCmds = pp.Commands
                if obj.EnableRotation != 'Off' and self.rotateFlag is True:
                    # Rotate model to index for cut
                    if axis == 'X':
                        axisOfRot = 'A'
                    elif axis == 'Y':
                        axisOfRot = 'B'
                    elif axis == 'Z':
                        axisOfRot = 'C'
                    else:
                        axisOfRot = 'A'
                    # Rotate Model to correct angle
                    ppCmds.insert(0, Path.Command('G0', {axisOfRot: angle, 'F': self.axialRapid}))

                    # Raise cutter to safe height
                    ppCmds.insert(0, Path.Command('G0', {'Z': obj.SafeHeight.Value, 'F': self.vertRapid}))

                    # Return index to starting position if axis of rotation changes.
                    if numShapes > 1:
                        if ns != numShapes - 1:
                            if axis != nextAxis:
                                ppCmds.append(Path.Command('G0', {axisOfRot: 0.0, 'F': self.axialRapid}))
                # Eif

                # Save gcode commands to object command list
                self.commandlist.extend(ppCmds)
                sims.append(sim)
            # Eif

            if self.areaOpRetractTool(obj) and self.endVector is not None:
                self.endVector[2] = obj.ClearanceHeight.Value
                self.commandlist.append(Path.Command('G0', {'Z': obj.ClearanceHeight.Value, 'F': self.vertRapid}))

        # Raise cutter to safe height and rotate back to original orientation
        if self.rotateFlag is True:
            resetAxis = False
            lastJobOp = None
            nextJobOp = None
            opIdx = 0
            JOB = PathUtils.findParentJob(obj)
            jobOps = JOB.Operations.Group
            numJobOps = len(jobOps)

            for joi in range(0, numJobOps):
                jo = jobOps[joi]
                if jo.Name == obj.Name:
                    opIdx = joi
            lastOpIdx = opIdx - 1
            nextOpIdx = opIdx + 1
            if lastOpIdx > -1:
                lastJobOp = jobOps[lastOpIdx]
            if nextOpIdx < numJobOps:
                nextJobOp = jobOps[nextOpIdx]

            if lastJobOp is not None:
                if hasattr(lastJobOp, 'EnableRotation'):
                    PathLog.debug('Last Op, {}, has `EnableRotation` set to {}'.format(lastJobOp.Label, lastJobOp.EnableRotation))
                    if lastJobOp.EnableRotation != obj.EnableRotation:
                        resetAxis = True
            if ns == numShapes - 1:  # If last shape, check next op EnableRotation setting
                if nextJobOp is not None:
                    if hasattr(nextJobOp, 'EnableRotation'):
                        PathLog.debug('Next Op, {}, has `EnableRotation` set to {}'.format(nextJobOp.Label, nextJobOp.EnableRotation))
                        if nextJobOp.EnableRotation != obj.EnableRotation:
                            resetAxis = True

            # Raise to safe height if rotation activated
            self.commandlist.append(Path.Command('G0', {'Z': obj.SafeHeight.Value, 'F': self.vertRapid}))
            # reset rotational axes if necessary
            if resetAxis is True:
                self.commandlist.append(Path.Command('G0', {'A': 0.0, 'F': self.axialRapid}))
                self.commandlist.append(Path.Command('G0', {'B': 0.0, 'F': self.axialRapid}))

        self.useTempJobClones('Delete')  # Delete temp job clone group and contents
        self.guiMessage('title', None, show=True)  # Process GUI messages to user
        for ton in self.tempObjectNames:  # remove temporary objects by name
            FreeCAD.ActiveDocument.removeObject(ton)
        PathLog.debug("obj.Name: " + str(obj.Name) + "\n\n")
        return sims
Esempio n. 46
0
def setLastFileToolLibrary(path):
    PathLog.track(path)
    if os.path.isfile(path):  # keep the path and file in sync
        preferences().SetString(LastPathToolLibrary, os.path.split(path)[0])
    return preferences().SetString(LastFileToolLibrary, path)
Esempio n. 47
0
    def createPath(self, obj, pathData, tags):
        PathLog.track()
        commands = []
        lastEdge = 0
        lastTag = 0
        # sameTag = None
        t = 0
        # inters = None
        edge = None

        segm = 50
        if hasattr(obj, 'SegmentationFactor'):
            segm = obj.SegmentationFactor
            if segm <= 0:
                segm = 50
                obj.SegmentationFactor = 50

        self.mappers = []
        mapper = None

        while edge or lastEdge < len(pathData.edges):
            PathLog.debug("------- lastEdge = %d/%d.%d/%d" %
                          (lastEdge, lastTag, t, len(tags)))
            if not edge:
                edge = pathData.edges[lastEdge]
                debugEdge(
                    edge, "=======  new edge: %d/%d" %
                    (lastEdge, len(pathData.edges)))
                lastEdge += 1
                # sameTag = None

            if mapper:
                mapper.add(edge)
                if mapper.mappingComplete():
                    commands.extend(mapper.commands)
                    edge = mapper.tail
                    mapper = None
                else:
                    edge = None

            if edge:
                tIndex = (t + lastTag) % len(tags)
                t += 1
                i = tags[tIndex].intersects(edge, edge.FirstParameter)
                if i and self.isValidTagStartIntersection(edge, i):
                    mapper = MapWireToTag(edge, tags[tIndex], i, segm,
                                          pathData.maxZ)
                    self.mappers.append(mapper)
                    edge = mapper.tail

            if not mapper and t >= len(tags):
                # gone through all tags, consume edge and move on
                if edge:
                    debugEdge(edge, '++++++++')
                    if pathData.rapid.isRapid(edge):
                        v = edge.Vertexes[1]
                        if not commands and PathGeom.isRoughly(
                                0, v.X) and PathGeom.isRoughly(
                                    0, v.Y) and not PathGeom.isRoughly(0, v.Z):
                            # The very first move is just to move to ClearanceHeight
                            commands.append(Path.Command('G0', {'Z': v.Z}))
                        else:
                            commands.append(
                                Path.Command('G0', {
                                    'X': v.X,
                                    'Y': v.Y,
                                    'Z': v.Z
                                }))
                    else:
                        commands.extend(PathGeom.cmdsForEdge(edge, segm=segm))
                edge = None
                t = 0

        lastCmd = Path.Command('G0', {'X': 0.0, 'Y': 0.0, 'Z': 0.0})
        outCommands = []

        tc = PathDressup.toolController(obj.Base)
        horizFeed = tc.HorizFeed.Value
        vertFeed = tc.VertFeed.Value
        horizRapid = tc.HorizRapid.Value
        vertRapid = tc.VertRapid.Value

        for cmd in commands:
            params = cmd.Parameters
            zVal = params.get('Z', None)
            zVal2 = lastCmd.Parameters.get('Z', None)

            zVal = zVal and round(zVal, 8)
            zVal2 = zVal2 and round(zVal2, 8)

            if cmd.Name in ['G1', 'G2', 'G3', 'G01', 'G02', 'G03']:
                if False and zVal is not None and zVal2 != zVal:
                    params['F'] = vertFeed
                else:
                    params['F'] = horizFeed
                lastCmd = cmd

            elif cmd.Name in ['G0', 'G00']:
                if zVal is not None and zVal2 != zVal:
                    params['F'] = vertRapid
                else:
                    params['F'] = horizRapid
                lastCmd = cmd

            outCommands.append(Path.Command(cmd.Name, params))

        return Path.Path(outCommands)
Esempio n. 48
0
    def __init__(self, obj, base):
        "Make stock"
        obj.addProperty(
            "App::PropertyLink", "Base", "Base",
            QtCore.QT_TRANSLATE_NOOP(
                "PathStock", "The base object this stock is derived from"))
        obj.addProperty(
            "App::PropertyDistance", "ExtXneg", "Stock",
            QtCore.QT_TRANSLATE_NOOP(
                "PathStock",
                "Extra allowance from part bound box in negative X direction"))
        obj.addProperty(
            "App::PropertyDistance", "ExtXpos", "Stock",
            QtCore.QT_TRANSLATE_NOOP(
                "PathStock",
                "Extra allowance from part bound box in positive X direction"))
        obj.addProperty(
            "App::PropertyDistance", "ExtYneg", "Stock",
            QtCore.QT_TRANSLATE_NOOP(
                "PathStock",
                "Extra allowance from part bound box in negative Y direction"))
        obj.addProperty(
            "App::PropertyDistance", "ExtYpos", "Stock",
            QtCore.QT_TRANSLATE_NOOP(
                "PathStock",
                "Extra allowance from part bound box in positive Y direction"))
        obj.addProperty(
            "App::PropertyDistance", "ExtZneg", "Stock",
            QtCore.QT_TRANSLATE_NOOP(
                "PathStock",
                "Extra allowance from part bound box in negative Z direction"))
        obj.addProperty(
            "App::PropertyDistance", "ExtZpos", "Stock",
            QtCore.QT_TRANSLATE_NOOP(
                "PathStock",
                "Extra allowance from part bound box in positive Z direction"))
        obj.addProperty(
            "App::PropertyLink", "Material", "Component",
            QtCore.QT_TRANSLATE_NOOP("App::Property",
                                     "A material for this object"))

        obj.Base = base
        obj.ExtXneg = 1.0
        obj.ExtXpos = 1.0
        obj.ExtYneg = 1.0
        obj.ExtYpos = 1.0
        obj.ExtZneg = 1.0
        obj.ExtZpos = 1.0

        # placement is only tracked on creation
        bb = shapeBoundBox(base.Group) if base else None
        if bb:
            obj.Placement = FreeCAD.Placement(
                FreeCAD.Vector(bb.XMin, bb.YMin, bb.ZMin), FreeCAD.Rotation())
        else:
            PathLog.track(obj.Label, base.Label)
        obj.Proxy = self

        # debugging aids
        self.origin = None
        self.length = None
        self.width = None
        self.height = None
Esempio n. 49
0
 def Activated(self):
     PathLog.track()
     self.Create()
Esempio n. 50
0
    def areaOpShapes(self, obj):
        '''areaOpShapes(obj) ... returns envelope for all base shapes or wires for Arch.Panels.'''
        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 = []

        if obj.Base:  # The user has selected subobjects from the base.  Process each.
            for base in obj.Base:
                holes = []
                faces = []
                for sub in base[1]:
                    shape = getattr(base[0].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[0].Shape, wire))
                    else:
                        FreeCAD.Console.PrintWarning(
                            "found a base object which is not a face.  Can't continue."
                        )
                        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):
                        env = PathUtils.getEnvelope(
                            shape, subshape=f, depthparams=self.depthparams)
                        PathLog.track()
                        shapes.append((env, True))

                if len(faces) > 0:
                    profileshape = Part.makeCompound(faces)
                    self.profileshape.append(profileshape)

                if obj.processPerimeter:
                    env = PathUtils.getEnvelope(base[0].Shape,
                                                subshape=profileshape,
                                                depthparams=self.depthparams)
                    PathLog.track()
                    shapes.append((env, False))

        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))

                    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))

        self.removalshapes = shapes
        PathLog.debug("%d shapes" % len(shapes))
        return shapes
Esempio n. 51
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(
                                translate(
                                    "PathPocket", "Pocket does not support shape %s.%s"
                                )
                                % (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(
                            translate(
                                "PathPocket",
                                "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
Esempio n. 52
0
def threadPasses(count, radii, majorDia, minorDia, toolDia, toolCrest=None):
    PathLog.track(count, radii, majorDia, minorDia, toolDia, toolCrest)
    minor, major = radii(majorDia, minorDia, toolDia, toolCrest)
    dr = float(major - minor) / count
    return [major - dr * (count - (i + 1)) for i in range(count)]
Esempio n. 53
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 = []
        startDepths = []
        faceDepths = []

        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:
            finish_step = obj.FinishDepth.Value if hasattr(
                obj, "FinishDepth") else 0.0
            for (base, subsList, angle, axis, stock) in baseSubsTuples:
                holes = []
                faces = []
                faceDepths = []
                startDepths = []

                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))
                        # Add face depth to list
                        faceDepths.append(shape.BoundBox.ZMin)
                    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)

                # Raise FinalDepth to lowest face in list on Inside profile ops
                finDep = obj.FinalDepth.Value
                if obj.Side == 'Inside':
                    finDep = min(faceDepths)
                finalDepths.append(finDep)

                strDep = obj.StartDepth.Value
                if strDep > stock.Shape.BoundBox.ZMax:
                    strDep = stock.Shape.BoundBox.ZMax
                startDepths.append(strDep)

                # Recalculate depthparams
                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)

                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()
                        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()
                    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:
                        finalDep = finDep
                        # Recalculate depthparams
                        if obj.Side == 'Inside':
                            if finalDep < shape.BoundBox.ZMin:
                                custDepthparams = 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=shape.BoundBox.
                                    ZMin,  # obj.FinalDepth.Value,
                                    user_depths=None)
                                env = PathUtils.getEnvelope(
                                    base.Shape,
                                    subshape=shape,
                                    depthparams=custDepthparams)
                                finalDep = shape.BoundBox.ZMin
                            else:
                                env = PathUtils.getEnvelope(
                                    base.Shape,
                                    subshape=shape,
                                    depthparams=self.depthparams)
                        else:
                            env = PathUtils.getEnvelope(
                                base.Shape,
                                subshape=shape,
                                depthparams=self.depthparams)
                        tup = env, False, 'pathProfileFaces', angle, axis, strDep, finalDep
                        shapes.append(tup)
                # Eif

            # adjust Start/Final Depths as needed
            # Raise existing Final Depth to level of lowest profile face
            if obj.Side == 'Inside':
                finalDepth = min(finalDepths)
                if obj.FinalDepth.Value < finalDepth:
                    obj.FinalDepth.Value = finalDepth
            # Lower high Start Depth to top of Stock
            startDepth = max(startDepths)
            if obj.StartDepth.Value > startDepth:
                obj.StartDepth.Value = startDepth
        else:  # Try to build targets from the job base
            if 1 == len(self.model):
                if hasattr(self.model[0], "Proxy"):
                    PathLog.info("hasattr() 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
Esempio n. 54
0
 def getTool(self, obj):
     '''returns the tool associated with this tool controller'''
     PathLog.track()
     toolitem = obj.Tooltable.Tools.popitem()
     return toolitem[1]
Esempio n. 55
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.stock        ... Stock object fo 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

        if not self._setBaseAndStock(obj):
            return

        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
                self.radius = tool.Diameter / 2
                self.tool = tool
                obj.OpToolDiameter = tool.Diameter

        self.updateDepths(obj)
        # now that all op values are set make sure the user properties get updated accordingly,
        # in case they still have an expression referencing any op values
        obj.recompute()

        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
Esempio n. 56
0
    def buildpathocc(self, obj, wires, zValues, rel=False):
        '''buildpathocc(obj, wires, zValues, rel=False) ... internal helper function to generate engraving commands.'''
        PathLog.track(obj.Label, len(wires), zValues)

        for wire in wires:
            offset = wire

            # reorder the wire
            if hasattr(obj, 'StartVertex'):
                offset = DraftGeomUtils.rebaseWire(offset, obj.StartVertex)

            edges = copy.copy(offset.Edges)
            last = None

            for z in zValues:
                if last:
                    if rel:
                        self.commandlist.append(
                            Path.Command(
                                'G1', {
                                    'X': last.x,
                                    'Y': last.y,
                                    'Z': last.z - z,
                                    'F': self.vertFeed
                                }))
                    else:
                        self.commandlist.append(
                            Path.Command(
                                'G1', {
                                    'X': last.x,
                                    'Y': last.y,
                                    'Z': z,
                                    'F': self.vertFeed
                                }))

                for edge in edges:
                    if not last:
                        # we set the first move to our first point
                        last = edge.Vertexes[0].Point
                        if len(offset.Edges) > 1:
                            ve = edge.Vertexes[-1]
                            e2 = offset.Edges[1]
                            if not PathGeom.pointsCoincide(
                                    ve.Point, e2.Vertexes[0].Point
                            ) and not PathGeom.pointsCoincide(
                                    ve.Point, e2.Vertexes[-1].Point):
                                PathLog.debug("flip first edge")
                                last = edge.Vertexes[-1].Point
                            else:
                                PathLog.debug("original first edge")
                        else:
                            PathLog.debug("not enough edges to flip")

                        self.commandlist.append(
                            Path.Command(
                                'G0', {
                                    'X': last.x,
                                    'Y': last.y,
                                    'Z': obj.ClearanceHeight.Value,
                                    'F': self.horizRapid
                                }))
                        self.commandlist.append(
                            Path.Command(
                                'G0', {
                                    'X': last.x,
                                    'Y': last.y,
                                    'Z': obj.SafeHeight.Value,
                                    'F': self.vertRapid
                                }))
                        if rel:
                            self.commandlist.append(
                                Path.Command(
                                    'G1', {
                                        'X': last.x,
                                        'Y': last.y,
                                        'Z': last.z - z,
                                        'F': self.vertFeed
                                    }))
                        else:
                            self.commandlist.append(
                                Path.Command(
                                    'G1', {
                                        'X': last.x,
                                        'Y': last.y,
                                        'Z': z,
                                        'F': self.vertFeed
                                    }))

                    if PathGeom.pointsCoincide(last, edge.Vertexes[0].Point):
                        for cmd in PathGeom.cmdsForEdge(edge):
                            self.appendCommand(cmd, z, rel)
                        last = edge.Vertexes[-1].Point
                    else:
                        for cmd in PathGeom.cmdsForEdge(edge, True):
                            self.appendCommand(cmd, z, rel)
                        last = edge.Vertexes[0].Point
            self.commandlist.append(
                Path.Command('G0', {
                    'Z': obj.ClearanceHeight.Value,
                    'F': self.vertRapid
                }))
Esempio n. 57
0
    def areaOpShapes(self, obj):
        '''areaOpShapes(obj) ... return shapes representing the solids to be removed.'''
        PathLog.track()
        PathLog.debug("----- areaOpShapes() in PathPocketShape.py")

        baseSubsTuples = []
        subCount = 0
        allTuples = []

        def planarFaceFromExtrusionEdges(face, trans):
            useFace = 'useFaceName'
            minArea = 0.0
            fCnt = 0
            clsd = []
            planar = False
            # Identify closed edges
            for edg in face.Edges:
                if edg.isClosed():
                    PathLog.debug('  -e.isClosed()')
                    clsd.append(edg)
                    planar = True

            # Attempt to create planar faces and select that with smallest area for use as pocket base
            if planar is True:
                planar = False
                for edg in clsd:
                    fCnt += 1
                    fName = sub + '_face_' + str(fCnt)
                    # Create planar face from edge
                    mFF = Part.Face(Part.Wire(Part.__sortEdges__([edg])))
                    if mFF.isNull():
                        PathLog.debug('Face(Part.Wire()) failed')
                    else:
                        if trans is True:
                            mFF.translate(FreeCAD.Vector(0, 0, face.BoundBox.ZMin - mFF.BoundBox.ZMin))

                        if FreeCAD.ActiveDocument.getObject(fName):
                            FreeCAD.ActiveDocument.removeObject(fName)

                        tmpFace = FreeCAD.ActiveDocument.addObject('Part::Feature', fName).Shape = mFF
                        tmpFace = FreeCAD.ActiveDocument.getObject(fName)
                        tmpFace.purgeTouched()

                        if minArea == 0.0:
                            minArea = tmpFace.Shape.Face1.Area
                            useFace = fName
                            planar = True
                        elif tmpFace.Shape.Face1.Area < minArea:
                            minArea = tmpFace.Shape.Face1.Area
                            FreeCAD.ActiveDocument.removeObject(useFace)
                            useFace = fName
                        else:
                            FreeCAD.ActiveDocument.removeObject(fName)

            if useFace != 'useFaceName':
                self.useTempJobClones(useFace)

            return (planar, useFace)

        def clasifySub(self, bs, sub):
            face = bs.Shape.getElement(sub)

            if type(face.Surface) == Part.Plane:
                PathLog.debug('type() == Part.Plane')
                if PathGeom.isVertical(face.Surface.Axis):
                    PathLog.debug('  -isVertical()')
                    # it's a flat horizontal face
                    self.horiz.append(face)
                    return True

                elif PathGeom.isHorizontal(face.Surface.Axis):
                    PathLog.debug('  -isHorizontal()')
                    self.vert.append(face)
                    return True

                else:
                    return False

            elif type(face.Surface) == Part.Cylinder and PathGeom.isVertical(face.Surface.Axis):
                PathLog.debug('type() == 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))
                    disk.translate(FreeCAD.Vector(0, 0, face.BoundBox.ZMin - disk.BoundBox.ZMin))
                    self.horiz.append(disk)
                    return True

                else:
                    PathLog.debug('  -none isClosed()')
                    # partial cylinder wall
                    self.vert.append(face)
                    return True

            elif type(face.Surface) == Part.SurfaceOfExtrusion:
                # extrusion wall
                PathLog.debug('type() == Part.SurfaceOfExtrusion')
                # Attempt to extract planar face from surface of extrusion
                (planar, useFace) = planarFaceFromExtrusionEdges(face, trans=True)
                # Save face object to self.horiz for processing or display error
                if planar is True:
                    uFace = FreeCAD.ActiveDocument.getObject(useFace)
                    self.horiz.append(uFace.Shape.Faces[0])
                    msg = translate('Path', "<b>Verify depth of pocket for '{}'.</b>".format(sub))
                    msg += translate('Path', "\n<br>Pocket is based on extruded surface.")
                    msg += translate('Path', "\n<br>Bottom of pocket might be non-planar and/or not normal to spindle axis.")
                    msg += translate('Path', "\n<br>\n<br><i>3D pocket bottom is NOT available in this operation</i>.")
                    PathLog.warning(msg)
                    # title = translate('Path', 'Depth Warning')
                    # self.guiMessage(title, msg, False)
                else:
                    PathLog.error(translate("Path", "Failed to create a planar face from edges in {}.".format(sub)))

            else:
                PathLog.debug('  -type(face.Surface): {}'.format(type(face.Surface)))
                return False

        if obj.Base:
            PathLog.debug('Processing... obj.Base')
            self.removalshapes = []  # pylint: disable=attribute-defined-outside-init

            if obj.EnableRotation == 'Off':
                stock = PathUtils.findParentJob(obj).Stock
                for (base, subList) in obj.Base:
                    baseSubsTuples.append((base, subList, 0.0, 'X', stock))
            else:
                for p in range(0, len(obj.Base)):
                    (base, subsList) = obj.Base[p]
                    isLoop = False

                    # First, check all subs collectively for loop of faces
                    if len(subsList) > 2:
                        (isLoop, norm, surf) = self.checkForFacesLoop(base, subsList)

                    if isLoop is True:
                        PathLog.debug("Common Surface.Axis or normalAt() value found for loop faces.")
                        rtn = False
                        subCount += 1
                        (rtn, angle, axis, praInfo) = self.faceRotationAnalysis(obj, norm, surf)  # pylint: disable=unused-variable
                        PathLog.debug("angle: {};  axis: {}".format(angle, axis))

                        if rtn is True:
                            faceNums = ""
                            for f in subsList:
                                faceNums += '_' + f.replace('Face', '')
                            (clnBase, angle, clnStock, tag) = self.applyRotationalAnalysis(obj, base, angle, axis, faceNums)  # pylint: disable=unused-variable

                            # Verify faces are correctly oriented - InverseAngle might be necessary
                            PathLog.debug("Checking if faces are oriented correctly after rotation...")
                            for sub in subsList:
                                face = clnBase.Shape.getElement(sub)
                                if type(face.Surface) == Part.Plane:
                                    if not PathGeom.isHorizontal(face.Surface.Axis):
                                        rtn = False
                                        PathLog.warning(translate("PathPocketShape", "Face appears to NOT be horizontal AFTER rotation applied."))
                                        break

                            if rtn is False:
                                PathLog.debug(translate("Path", "Face appears misaligned after initial rotation."))
                                if obj.InverseAngle is False:
                                    if obj.AttemptInverseAngle is True:
                                        (clnBase, clnStock, angle) = self.applyInverseAngle(obj, clnBase, clnStock, axis, angle)
                                    else:
                                        msg = translate("Path", "Consider toggling the 'InverseAngle' property and recomputing.")
                                        PathLog.warning(msg)

                            if angle < 0.0:
                                angle += 360.0

                            tup = clnBase, subsList, angle, axis, clnStock
                        else:
                            if self.warnDisabledAxis(obj, axis) is False:
                                PathLog.debug("No rotation used")
                            axis = 'X'
                            angle = 0.0
                            stock = PathUtils.findParentJob(obj).Stock
                            tup = base, subsList, angle, axis, stock
                        # Eif

                        allTuples.append(tup)
                        baseSubsTuples.append(tup)
                    # Eif

                    if isLoop is False:
                        PathLog.debug(translate('Path', "Processing subs individually ..."))
                        for sub in subsList:
                            subCount += 1
                            if 'Face' in sub:
                                rtn = False
                                face = base.Shape.getElement(sub)
                                if type(face.Surface) == Part.SurfaceOfExtrusion:
                                    # extrusion wall
                                    PathLog.debug('analyzing type() == Part.SurfaceOfExtrusion')
                                    # Attempt to extract planar face from surface of extrusion
                                    (planar, useFace) = planarFaceFromExtrusionEdges(face, trans=False)
                                    # Save face object to self.horiz for processing or display error
                                    if planar is True:
                                        base = FreeCAD.ActiveDocument.getObject(useFace)
                                        sub = 'Face1'
                                        PathLog.debug('  -successful face created: {}'.format(useFace))
                                    else:
                                        PathLog.error(translate("Path", "Failed to create a planar face from edges in {}.".format(sub)))

                                (norm, surf) = self.getFaceNormAndSurf(face)
                                (rtn, angle, axis, praInfo) = self.faceRotationAnalysis(obj, norm, surf)  # pylint: disable=unused-variable
                                PathLog.debug("initial {}".format(praInfo))

                                if rtn is True:
                                    faceNum = sub.replace('Face', '')
                                    (clnBase, angle, clnStock, tag) = self.applyRotationalAnalysis(obj, base, angle, axis, faceNum)
                                    # Verify faces are correctly oriented - InverseAngle might be necessary
                                    faceIA = clnBase.Shape.getElement(sub)
                                    (norm, surf) = self.getFaceNormAndSurf(faceIA)
                                    (rtn, praAngle, praAxis, praInfo2) = self.faceRotationAnalysis(obj, norm, surf)  # pylint: disable=unused-variable
                                    PathLog.debug("follow-up {}".format(praInfo2))

                                    if abs(praAngle) == 180.0:
                                        rtn = False
                                        if self.isFaceUp(clnBase, faceIA) is False:
                                            PathLog.debug('isFaceUp is False')
                                            angle -= 180.0

                                    if rtn is True:
                                        PathLog.debug(translate("Path", "Face appears misaligned after initial rotation."))
                                        if obj.InverseAngle is False:
                                            if obj.AttemptInverseAngle is True:
                                                (clnBase, clnStock, angle) = self.applyInverseAngle(obj, clnBase, clnStock, axis, angle)
                                            else:
                                                msg = translate("Path", "Consider toggling the 'InverseAngle' property and recomputing.")
                                                PathLog.warning(msg)

                                            if self.isFaceUp(clnBase, faceIA) is False:
                                                PathLog.debug('isFaceUp is False')
                                                angle += 180.0
                                    else:
                                        PathLog.debug("Face appears to be oriented correctly.")

                                    if angle < 0.0:
                                        angle += 360.0

                                    tup = clnBase, [sub], angle, axis, clnStock
                                else:
                                    if self.warnDisabledAxis(obj, axis) is False:
                                        PathLog.debug(str(sub) + ": No rotation used")
                                    axis = 'X'
                                    angle = 0.0
                                    stock = PathUtils.findParentJob(obj).Stock
                                    tup = base, [sub], angle, axis, stock
                                # Eif
                                allTuples.append(tup)
                                baseSubsTuples.append(tup)
                            else:
                                ignoreSub = base.Name + '.' + sub
                                PathLog.error(translate('Path', "Selected feature is not a Face. Ignoring: {}".format(ignoreSub)))

            for o in baseSubsTuples:
                self.horiz = []  # pylint: disable=attribute-defined-outside-init
                self.vert = []  # pylint: disable=attribute-defined-outside-init
                subBase = o[0]
                subsList = o[1]
                angle = o[2]
                axis = o[3]
                stock = o[4]

                for sub in subsList:
                    if 'Face' in sub:
                        if clasifySub(self, subBase, sub) is False:
                            PathLog.error(translate('PathPocket', 'Pocket does not support shape %s.%s') % (subBase.Label, sub))
                            if obj.EnableRotation != 'Off':
                                PathLog.warning(translate('PathPocket', 'Face might not be within rotation accessibility limits.'))

                # Determine final depth as highest value of bottom boundbox of vertical face,
                #   in case of uneven faces on bottom
                if len(self.vert) > 0:
                    vFinDep = self.vert[0].BoundBox.ZMin
                    for vFace in self.vert:
                        if vFace.BoundBox.ZMin > vFinDep:
                            vFinDep = vFace.BoundBox.ZMin
                    # Determine if vertical faces for a loop: Extract planar loop wire as new horizontal face.
                    self.vertical = PathGeom.combineConnectedShapes(self.vert) # pylint: disable=attribute-defined-outside-init
                    self.vWires = [TechDraw.findShapeOutline(shape, 1, FreeCAD.Vector(0, 0, 1)) for shape in self.vertical] # pylint: disable=attribute-defined-outside-init
                    for wire in self.vWires:
                        w = PathGeom.removeDuplicateEdges(wire)
                        face = Part.Face(w)
                        # face.tessellate(0.1)
                        if PathGeom.isRoughly(face.Area, 0):
                            msg = translate('PathPocket', 'Vertical faces do not form a loop - ignoring')
                            PathLog.error(msg)
                            # title = translate("Path", "Face Selection Warning")
                            # self.guiMessage(title, msg, True)
                        else:
                            face.translate(FreeCAD.Vector(0, 0, vFinDep - face.BoundBox.ZMin))
                            self.horiz.append(face)
                            msg = translate('Path', 'Verify final depth of pocket shaped by vertical faces.')
                            PathLog.warning(msg)
                            # title = translate('Path', 'Depth Warning')
                            # self.guiMessage(title, msg, False)

                # add faces for extensions
                self.exts = [] # pylint: disable=attribute-defined-outside-init
                for ext in self.getExtensions(obj):
                    wire = ext.getWire()
                    if wire:
                        face = Part.Face(wire)
                        self.horiz.append(face)
                        self.exts.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 = [] # pylint: disable=attribute-defined-outside-init
                for shape in PathGeom.combineConnectedShapes(self.horiz):
                    shape.sewShape()
                    # shape.tessellate(0.1)
                    shpZMin = shape.BoundBox.ZMin
                    PathLog.debug('PathGeom.combineConnectedShapes shape.BoundBox.ZMin: {}'.format(shape.BoundBox.ZMin))
                    if obj.UseOutline:
                        wire = TechDraw.findShapeOutline(shape, 1, FreeCAD.Vector(0, 0, 1))
                        wFace = Part.Face(wire)
                        if wFace.BoundBox.ZMin != shpZMin:
                            wFace.translate(FreeCAD.Vector(0, 0, shpZMin - wFace.BoundBox.ZMin))
                        self.horizontal.append(wFace)
                        PathLog.debug('PathGeom.combineConnectedShapes shape.BoundBox.ZMin: {}'.format(wFace.BoundBox.ZMin))
                    else:
                        self.horizontal.append(shape)

                # extrude all faces up to StartDepth and those are the removal shapes
                sD = obj.StartDepth.Value
                fD = obj.FinalDepth.Value
                clrnc = 0.5
                for face in self.horizontal:
                    afD = fD
                    useAngle = angle
                    shpZMin = face.BoundBox.ZMin
                    PathLog.debug('self.horizontal shpZMin: {}'.format(shpZMin))
                    if self.isFaceUp(subBase, face) is False:
                        useAngle += 180.0
                        invZ = (-2 * shpZMin) - clrnc
                        face.translate(FreeCAD.Vector(0.0, 0.0, invZ))
                        shpZMin = -1 * shpZMin
                    else:
                        face.translate(FreeCAD.Vector(0.0, 0.0, -1 * clrnc))
                    
                    if obj.LimitDepthToFace is True and obj.EnableRotation != 'Off':
                        if shpZMin > obj.FinalDepth.Value:
                            afD = shpZMin
                            if sD <= afD:
                                sD = afD + 1.0
                                msg = translate('PathPocketShape', 'Start Depth is lower than face depth. Setting to ')
                                PathLog.warning(msg + ' {} mm.'.format(sD))
                    else:
                        face.translate(FreeCAD.Vector(0, 0, obj.FinalDepth.Value - shpZMin))
                    
                    extent = FreeCAD.Vector(0, 0, sD - afD + clrnc)
                    extShp = face.removeSplitter().extrude(extent)
                    self.removalshapes.append((extShp, False, 'pathPocketShape', useAngle, axis, sD, afD))
                    PathLog.debug("Extent values are strDep: {}, finDep: {},  extrd: {}".format(sD, afD, extent))
                # Efor face
            # Efor

        else:
            # process the job base object as a whole
            PathLog.debug(translate("Path", 'Processing model as a whole ...'))
            finDep = obj.FinalDepth.Value
            strDep = obj.StartDepth.Value
            self.outlines = [Part.Face(TechDraw.findShapeOutline(base.Shape, 1, FreeCAD.Vector(0, 0, 1))) for base in self.model] # pylint: disable=attribute-defined-outside-init
            stockBB = self.stock.Shape.BoundBox

            self.removalshapes = [] # pylint: disable=attribute-defined-outside-init
            self.bodies = [] # pylint: disable=attribute-defined-outside-init
            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, 'pathPocketShape', 0.0, 'X', strDep, finDep))

        for (shape, hole, sub, angle, axis, strDep, finDep) in self.removalshapes: # pylint: disable=unused-variable
            shape.tessellate(0.05)  # originally 0.1

        if self.removalshapes:
            obj.removalshape = self.removalshapes[0][0]

        return self.removalshapes
Esempio n. 58
0
 def refresh(self):
     PathLog.track()
     self.form.blockSignals(True)
     self.updateTool()
     self.updateUI()
     self.form.blockSignals(False)
    def createPath(self, obj, pathData, tags):
        PathLog.track()
        commands = []
        lastEdge = 0
        lastTag = 0
        # sameTag = None
        t = 0
        # inters = None
        edge = None

        segm = 50
        if hasattr(obj, 'SegmentationFactor'):
            segm = obj.SegmentationFactor
            if segm <= 0:
                segm = 50
                obj.SegmentationFactor = 50

        self.mappers = []
        mapper = None

        tc = PathDressup.toolController(obj.Base)
        horizFeed = tc.HorizFeed.Value
        vertFeed = tc.VertFeed.Value
        horizRapid = tc.HorizRapid.Value
        vertRapid = tc.VertRapid.Value

        while edge or lastEdge < len(pathData.edges):
            PathLog.debug("------- lastEdge = %d/%d.%d/%d" % (lastEdge, lastTag, t, len(tags)))
            if not edge:
                edge = pathData.edges[lastEdge]
                debugEdge(edge, "=======  new edge: %d/%d" % (lastEdge, len(pathData.edges)))
                lastEdge += 1
                # sameTag = None

            if mapper:
                mapper.add(edge)
                if mapper.mappingComplete():
                    commands.extend(mapper.commands)
                    edge = mapper.tail
                    mapper = None
                else:
                    edge = None

            if edge:
                tIndex = (t + lastTag) % len(tags)
                t += 1
                i = tags[tIndex].intersects(edge, edge.FirstParameter)
                if i and self.isValidTagStartIntersection(edge, i):
                    mapper = MapWireToTag(edge, tags[tIndex], i, segm, pathData.maxZ, hSpeed = horizFeed, vSpeed = vertFeed)
                    self.mappers.append(mapper)
                    edge = mapper.tail

            if not mapper and t >= len(tags):
                # gone through all tags, consume edge and move on
                if edge:
                    debugEdge(edge, '++++++++')
                    if pathData.rapid.isRapid(edge):
                        v = edge.Vertexes[1]
                        if not commands and PathGeom.isRoughly(0, v.X) and PathGeom.isRoughly(0, v.Y) and not PathGeom.isRoughly(0, v.Z):
                            # The very first move is just to move to ClearanceHeight
                            commands.append(Path.Command('G0', {'Z': v.Z, 'F': horizRapid}))
                        else:
                            commands.append(Path.Command('G0', {'X': v.X, 'Y': v.Y, 'Z': v.Z, 'F': vertRapid}))
                    else:
                        commands.extend(PathGeom.cmdsForEdge(edge, segm=segm, hSpeed = horizFeed, vSpeed = vertFeed))
                edge = None
                t = 0

        return Path.Path(commands)
Esempio n. 60
0
    def __init__(self, obj):
        PathLog.track()

        obj.addProperty(
            "App::PropertyBool", "Active", "Path",
            QtCore.QT_TRANSLATE_NOOP(
                "PathOp",
                "Make False, to prevent operation from generating code"))
        obj.addProperty(
            "App::PropertyString", "Comment", "Path",
            QtCore.QT_TRANSLATE_NOOP("PathOp",
                                     "An optional comment for this Operation"))
        obj.addProperty(
            "App::PropertyString", "UserLabel", "Path",
            QtCore.QT_TRANSLATE_NOOP("PathOp", "User Assigned Label"))

        features = self.opFeatures(obj)

        if FeatureBaseGeometry & features:
            self.addBaseProperty(obj)

        if FeatureLocations & features:
            obj.addProperty(
                "App::PropertyVectorList", "Locations", "Path",
                QtCore.QT_TRANSLATE_NOOP("PathOp",
                                         "Base locations for this operation"))

        if FeatureTool & features:
            obj.addProperty(
                "App::PropertyLink", "ToolController", "Path",
                QtCore.QT_TRANSLATE_NOOP(
                    "PathOp",
                    "The tool controller that will be used to calculate the path"
                ))
            self.addOpValues(obj, ['tooldia'])

        if FeatureDepths & features:
            obj.addProperty(
                "App::PropertyDistance", "StartDepth", "Depth",
                QtCore.QT_TRANSLATE_NOOP(
                    "PathOp", "Starting Depth of Tool- first cut depth in Z"))
            obj.addProperty(
                "App::PropertyDistance", "FinalDepth", "Depth",
                QtCore.QT_TRANSLATE_NOOP(
                    "PathOp", "Final Depth of Tool- lowest value in Z"))
            if FeatureNoFinalDepth & features:
                obj.setEditorMode('FinalDepth', 2)  # hide
            self.addOpValues(obj, ['start', 'final'])

        if FeatureStepDown & features:
            obj.addProperty(
                "App::PropertyDistance", "StepDown", "Depth",
                QtCore.QT_TRANSLATE_NOOP("PathOp",
                                         "Incremental Step Down of Tool"))

        if FeatureFinishDepth & features:
            obj.addProperty(
                "App::PropertyDistance", "FinishDepth", "Depth",
                QtCore.QT_TRANSLATE_NOOP(
                    "PathOp", "Maximum material removed on final pass."))

        if FeatureHeights & features:
            obj.addProperty(
                "App::PropertyDistance", "ClearanceHeight", "Depth",
                QtCore.QT_TRANSLATE_NOOP(
                    "PathOp",
                    "The height needed to clear clamps and obstructions"))
            obj.addProperty(
                "App::PropertyDistance", "SafeHeight", "Depth",
                QtCore.QT_TRANSLATE_NOOP(
                    "PathOp", "Rapid Safety Height between locations."))

        if FeatureStartPoint & features:
            obj.addProperty(
                "App::PropertyVector", "StartPoint", "Start Point",
                QtCore.QT_TRANSLATE_NOOP("PathOp",
                                         "The start point of this path"))
            obj.addProperty(
                "App::PropertyBool", "UseStartPoint", "Start Point",
                QtCore.QT_TRANSLATE_NOOP(
                    "PathOp", "make True, if specifying a Start Point"))

        self.initOperation(obj)

        if self.setDefaultValues(obj):
            obj.Proxy = self