Exemplo n.º 1
0
    def setFields(self):
        self.form.startDepth.setText(FreeCAD.Units.Quantity(self.obj.StartDepth.Value, FreeCAD.Units.Length).UserString)
        self.form.finalDepth.setText(FreeCAD.Units.Quantity(self.obj.FinalDepth.Value, FreeCAD.Units.Length).UserString)
        self.form.finishDepth.setText(FreeCAD.Units.Quantity(self.obj.FinishDepth.Value, FreeCAD.Units.Length).UserString)
        self.form.stepDown.setText(FreeCAD.Units.Quantity(self.obj.StepDown, FreeCAD.Units.Length).UserString)
        self.form.safeHeight.setText(FreeCAD.Units.Quantity(self.obj.SafeHeight.Value, FreeCAD.Units.Length).UserString)
        self.form.clearanceHeight.setText(FreeCAD.Units.Quantity(self.obj.ClearanceHeight.Value,  FreeCAD.Units.Length).UserString)

        controllers = PathUtils.getToolControllers(self.obj)
        labels = [c.Label for c in controllers]
        self.form.uiToolController.blockSignals(True)
        self.form.uiToolController.addItems(labels)
        self.form.uiToolController.blockSignals(False)
        if self.obj.ToolController is not None:
            index = self.form.uiToolController.findText(
                self.obj.ToolController.Label, QtCore.Qt.MatchFixedString)
            PathLog.debug("searching for TC label {}. Found Index: {}".format(self.obj.ToolController.Label, index))
            if index >= 0:
                self.form.uiToolController.blockSignals(True)
                self.form.uiToolController.setCurrentIndex(index)
                self.form.uiToolController.blockSignals(False)
        else:
            self.obj.ToolController = PathUtils.findToolController(self.obj)

        for i in self.obj.Base:
            self.form.baseList.addItem(i[0].Name)

        index = self.form.algorithmSelect.findText(
                self.obj.Algorithm, QtCore.Qt.MatchFixedString)
        if index >= 0:
            self.form.algorithmSelect.blockSignals(True)
            self.form.algorithmSelect.setCurrentIndex(index)
            self.form.algorithmSelect.blockSignals(False)
Exemplo n.º 2
0
    def inOutBoneCommands(self, bone, boneAngle, fixedLength):
        corner = bone.corner(self.toolRadius)

        bone.tip = bone.inChord.End  # in case there is no bone

        PathLog.debug("corner = (%.2f, %.2f)" % (corner.x, corner.y))
        # debugMarker(corner, 'corner', (1., 0., 1.), self.toolRadius)

        length = fixedLength
        if bone.obj.Incision == Incision.Custom:
            length = bone.obj.Custom
        if bone.obj.Incision == Incision.Adaptive:
            length = bone.adaptiveLength(boneAngle, self.toolRadius)

        if length == 0:
            PathLog.info("no bone after all ..")
            return [bone.lastCommand, bone.outChord.g1Command(bone.F)]

        boneInChord = bone.inChord.move(length, boneAngle)
        boneOutChord = boneInChord.moveTo(bone.outChord.Start)

        # debugCircle(boneInChord.Start, self.toolRadius, 'boneStart')
        # debugCircle(boneInChord.End, self.toolRadius, 'boneEnd')

        bone.tip = boneInChord.End

        if bone.smooth == 0:
            return [bone.lastCommand, boneInChord.g1Command(bone.F), boneOutChord.g1Command(bone.F), bone.outChord.g1Command(bone.F)]

        # reconstruct the corner and convert to an edge
        offset = corner - bone.inChord.End
        iChord = Chord(bone.inChord.Start + offset, bone.inChord.End + offset)
        oChord = Chord(bone.outChord.Start + offset, bone.outChord.End + offset)
        iLine = iChord.asLine()
        oLine = oChord.asLine()
        cornerShape = Part.Shape([iLine, oLine])

        # construct a shape representing the cut made by the bone
        vt0 = FreeCAD.Vector(0, self.toolRadius, 0)
        vt1 = FreeCAD.Vector(length,  self.toolRadius, 0)
        vb0 = FreeCAD.Vector(0, -self.toolRadius, 0)
        vb1 = FreeCAD.Vector(length, -self.toolRadius, 0)
        vm2 = FreeCAD.Vector(length + self.toolRadius, 0, 0)

        boneBot = Part.LineSegment(vb1, vb0)
        boneLid = Part.LineSegment(vb0, vt0)
        boneTop = Part.LineSegment(vt0, vt1)

        # what we actually want is an Arc - but findIntersect only returns the coincident if one exists
        # which really sucks because that's the one we're probably not interested in ....
        boneArc = Part.Arc(vt1, vm2, vb1)
        # boneArc = Part.Circle(FreeCAD.Vector(length, 0, 0), FreeCAD.Vector(0,0,1), self.toolRadius)
        boneWire = Part.Shape([boneTop, boneArc, boneBot, boneLid])
        boneWire.rotate(FreeCAD.Vector(0, 0, 0), FreeCAD.Vector(0, 0, 1), boneAngle * 180 / math.pi)
        boneWire.translate(bone.inChord.End)
        self.boneShapes = [cornerShape, boneWire]

        bone.inCommands = self.smoothChordCommands(bone, bone.inChord, boneInChord, Part.Edge(iLine), boneWire, corner, bone.smooth & Smooth.In,  (1., 0., 0.))
        bone.outCommands = self.smoothChordCommands(bone, boneOutChord, bone.outChord, Part.Edge(oLine), boneWire, corner, bone.smooth & Smooth.Out, (0., 1., 0.))
        return bone.inCommands + bone.outCommands
Exemplo n.º 3
0
def adjustWirePlacement(obj, shape, wires):
    job = PathUtils.findParentJob(obj)
    if hasattr(shape, 'MapMode') and 'Deactivated' != shape.MapMode:
        if hasattr(shape, 'Support') and 1 == len(shape.Support) and 1 == len(shape.Support[0][1]):
            pmntShape   = shape.Placement
            pmntSupport = shape.Support[0][0].getGlobalPlacement()
            #pmntSupport = shape.Support[0][0].Placement
            pmntBase    = job.Base.Placement
            pmnt = pmntBase.multiply(pmntSupport.inverse().multiply(pmntShape))
            #PathLog.debug("pmnt = %s" % pmnt)
            newWires = []
            for w in wires:
                edges = []
                for e in w.Edges:
                    e = e.copy()
                    e.Placement = FreeCAD.Placement()
                    edges.append(e)
                w = Part.Wire(edges)
                w.Placement = pmnt
                newWires.append(w)
            wires = newWires
        else:
            PathLog.warning(translate("PathEngrave", "Attachment not supported by engraver"))
    else:
        PathLog.debug("MapMode: %s" % (shape.MapMode if hasattr(shape, 'MapMode') else 'None')) 
    return wires
Exemplo n.º 4
0
    def areaOpShapes(self, obj):
        '''areaOpShapes(obj) ... return top face'''
        # Facing is done either against base objects
        if obj.Base:
            PathLog.debug("obj.Base: {}".format(obj.Base))
            faces = []
            for b in obj.Base:
                for sub in b[1]:
                    shape = getattr(b[0].Shape, sub)
                    if isinstance(shape, Part.Face):
                        faces.append(shape)
                    else:
                        PathLog.debug('The base subobject is not a face')
                        return
            planeshape = Part.makeCompound(faces)
            PathLog.debug("Working on a collection of faces {}".format(faces))

        # If no base object, do planing of top surface of entire model
        else:
            planeshape = self.baseobject.Shape
            PathLog.debug("Working on a shape {}".format(self.baseobject.Name))

        # Find the correct shape depending on Boundary shape.
        PathLog.debug("Boundary Shape: {}".format(obj.BoundaryShape))
        bb = planeshape.BoundBox
        if obj.BoundaryShape == 'Boundbox':
            bbperim = Part.makeBox(bb.XLength, bb.YLength, 1, FreeCAD.Vector(bb.XMin, bb.YMin, bb.ZMin), FreeCAD.Vector(0, 0, 1))
            env = PathUtils.getEnvelope(partshape=bbperim, depthparams=self.depthparams)
        elif obj.BoundaryShape == 'Stock':
            stock = PathUtils.findParentJob(obj).Stock.Shape
            env = stock
        else:
            env = PathUtils.getEnvelope(partshape=planeshape, depthparams=self.depthparams)

        return [(env, False)]
Exemplo n.º 5
0
 def rotateSel(sel, n):
     p = sel.Object.Placement
     loc = sel.Object.Placement.Base
     r = axis.cross(n) # rotation axis
     a = DraftVecUtils.angle(n, axis, r) * 180 / math.pi
     PathLog.debug("oh boy: (%.2f, %.2f, %.2f) -> %.2f" % (r.x, r.y, r.z, a))
     Draft.rotate(sel.Object, a, axis=r)
Exemplo n.º 6
0
 def assignTemplate(self, obj, template):
     '''assignTemplate(obj, template) ... extract the properties from the given template file and assign to receiver.
     This will also create any TCs stored in the template.'''
     tcs = []
     if template:
         tree = xml.parse(template)
         for job in tree.getroot().iter(JobTemplate.Job):
             if job.get(JobTemplate.GeometryTolerance):
                 obj.GeometryTolerance = float(job.get(JobTemplate.GeometryTolerance))
             if job.get(JobTemplate.PostProcessor):
                 obj.PostProcessor = job.get(JobTemplate.PostProcessor)
                 if job.get(JobTemplate.PostProcessorArgs):
                     obj.PostProcessorArgs = job.get(JobTemplate.PostProcessorArgs)
                 else:
                     obj.PostProcessorArgs = ''
             if job.get(JobTemplate.PostProcessorOutputFile):
                 obj.PostProcessorOutputFile = job.get(JobTemplate.PostProcessorOutputFile)
             if job.get(JobTemplate.Description):
                 obj.Description = job.get(JobTemplate.Description)
         for tc in tree.getroot().iter(JobTemplate.ToolController):
             tcs.append(PathToolController.FromTemplate(tc))
         for stock in tree.getroot().iter(JobTemplate.Stock):
             obj.Stock = PathStock.CreateFromTemplate(self, stock)
     else:
         tcs.append(PathToolController.Create())
     PathLog.debug("setting tool controllers (%d)" % len(tcs))
     obj.ToolController = tcs
Exemplo 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 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
Exemplo n.º 9
0
    def getFields(self):
        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, "MaterialAllowance"):
                self.obj.MaterialAllowance = FreeCAD.Units.Quantity(self.form.extraOffset.text()).Value
            if hasattr(self.obj, "UseStartPoint"):
                self.obj.UseStartPoint = self.form.useStartPoint.isChecked()
            if hasattr(self.obj, "Algorithm"):
                self.obj.Algorithm = str(self.form.algorithmSelect.currentText())
            if hasattr(self.obj, "CutMode"):
                self.obj.CutMode = str(self.form.cutMode.currentText())
            if hasattr(self.obj, "UseZigZag"):
                self.obj.UseZigZag = self.form.useZigZag.isChecked()
            if hasattr(self.obj, "ZigUnidirectional"):
                self.obj.ZigUnidirectional = self.form.zigZagUnidirectional.isChecked()
            if hasattr(self.obj, "ZigZagAngle"):
                self.obj.ZigZagAngle = FreeCAD.Units.Quantity(self.form.zigZagAngle.text()).Value
            if hasattr(self.obj, "StepOver"):
                self.obj.StepOver = self.form.stepOverPercent.value()
            if hasattr(self.obj, "ToolController"):
                PathLog.debug("name: {}".format(self.form.uiToolController.currentText()))
                tc = PathUtils.findToolController(self.obj, self.form.uiToolController.currentText())
                self.obj.ToolController = tc

        self.obj.Proxy.execute(self.obj)
Exemplo n.º 10
0
 def __init__(self, edge, tag, i, segm, maxZ):
     debugEdge(edge, 'MapWireToTag(%.2f, %.2f, %.2f)' % (i.x, i.y, i.z))
     self.tag = tag
     self.segm = segm
     self.maxZ = maxZ
     if PathGeom.pointsCoincide(edge.valueAt(edge.FirstParameter), i):
         tail = edge
         self.commands = []
         debugEdge(tail, '.........=')
     elif PathGeom.pointsCoincide(edge.valueAt(edge.LastParameter), i):
         debugEdge(edge, '++++++++ .')
         self.commands = PathGeom.cmdsForEdge(edge, segm=segm)
         tail = None
     else:
         e, tail = PathGeom.splitEdgeAt(edge, i)
         debugEdge(e, '++++++++ .')
         self.commands = PathGeom.cmdsForEdge(e, segm=segm)
         debugEdge(tail, '.........-')
         self.initialEdge = edge
     self.tail = tail
     self.edges = []
     self.entry = i
     if tail:
         PathLog.debug("MapWireToTag(%s - %s)" % (i, tail.valueAt(tail.FirstParameter)))
     else:
         PathLog.debug("MapWireToTag(%s - )" % i)
     self.complete = False
     self.haveProblem = False
Exemplo n.º 11
0
    def getFields(self):
        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, "Side"):
                self.obj.Side = str(self.form.cutSide.currentText())
            if hasattr(self.obj, "Direction"):
                self.obj.Direction = str(self.form.direction.currentText())
            if hasattr(self.obj, "ToolController"):
                PathLog.debug("name: {}".format(self.form.uiToolController.currentText()))
                tc = PathUtils.findToolController(self.obj, self.form.uiToolController.currentText())
                self.obj.ToolController = tc

        self.obj.Proxy.execute(self.obj)
Exemplo n.º 12
0
    def createTagsPositionDisabled(self, obj, positionsIn, disabledIn):
        rawTags = []
        for i, pos in enumerate(positionsIn):
            tag = Tag(i, pos.x, pos.y, obj.Width.Value, obj.Height.Value, obj.Angle, obj.Radius, not i in disabledIn)
            tag.createSolidsAt(self.pathData.minZ, self.toolRadius)
            rawTags.append(tag)
        # disable all tags that intersect with their previous tag
        prev = None
        tags = []
        positions = []
        disabled = []
        for i, tag in enumerate(self.pathData.sortedTags(rawTags)):
            if tag.enabled:
                if prev:
                    if prev.solid.common(tag.solid).Faces:
                        PathLog.info("Tag #%d intersects with previous tag - disabling\n" % i)
                        PathLog.debug("this tag = %d [%s]" % (i, tag.solid.BoundBox))
                        tag.enabled = False
                elif self.pathData.edges:
                    e = self.pathData.edges[0]
                    p0 = e.valueAt(e.FirstParameter)
                    p1 = e.valueAt(e.LastParameter)
                    if tag.solid.isInside(p0, PathGeom.Tolerance, True) or tag.solid.isInside(p1, PathGeom.Tolerance, True):
                        PathLog.info("Tag #%d intersects with starting point - disabling\n" % i)
                        tag.enabled = False

            if tag.enabled:
                prev = tag
                PathLog.debug("previousTag = %d [%s]" % (i, prev))
            else:
                disabled.append(i)
            tag.id = i  # assigne final id
            tags.append(tag)
            positions.append(tag.originAt(self.pathData.minZ))
        return (tags, positions, disabled)
Exemplo n.º 13
0
 def allow(self, doc, obj, sub):
     PathLog.debug('obj: {} sub: {}'.format(obj, sub))
     if hasattr(obj, "Shape") and sub:
         shape = obj.Shape
         subobj = shape.getElement(sub)
         return PathUtils.isDrillable(shape, subobj, includePartials = True)
     else:
         return False
Exemplo n.º 14
0
 def addToolController(self, tc):
     group = self.obj.ToolController
     PathLog.debug("addToolController(%s): %s" % (tc.Label, [t.Label for t in group]))
     if tc.Name not in [str(t.Name) for t in group]:
         tc.setExpression('VertRapid',  "%s.%s" % (self.setupSheet.expressionReference(), PathSetupSheet.Template.VertRapid))
         tc.setExpression('HorizRapid', "%s.%s" % (self.setupSheet.expressionReference(), PathSetupSheet.Template.HorizRapid))
         group.append(tc)
         self.obj.ToolController = group
 def onDelete(self, arg1=None, arg2=None):
     PathLog.debug("Deleting Dressup")
     '''this makes sure that the base operation is added back to the project and visible'''
     FreeCADGui.ActiveDocument.getObject(arg1.Object.Base.Name).Visibility = True
     job = PathUtils.findParentJob(self.obj)
     job.Proxy.addOperation(arg1.Object.Base, arg1.Object)
     arg1.Object.Base = None
     return True
Exemplo n.º 16
0
 def allow(self, doc, obj, sub):
     PathLog.debug('obj: {} sub: {}'.format(obj, sub))
     if hasattr(obj, "Shape"):
         obj = obj.Shape
         subobj = obj.getElement(sub)
         return PathUtils.isDrillable(obj, subobj)
     else:
         return False
Exemplo n.º 17
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 = []

        if obj.Base:  # The user has selected subobjects from the base.  Process each.
            holes = []
            faces = []
            for b in obj.Base:
                for sub in b[1]:
                    shape = getattr(b[0].Shape, sub)
                    if isinstance(shape, Part.Face):
                        faces.append(shape)
                        if numpy.isclose(abs(shape.normalAt(0, 0).z), 1):  # horizontal face
                            holes += shape.Wires[1:]
                    else:
                        FreeCAD.Console.PrintWarning("found a base object which is not a face.  Can't continue.")
                        return

            for wire in holes:
                f = Part.makeFace(wire, 'Part::FaceMakerSimple')
                drillable = PathUtils.isDrillable(self.baseobject.Shape, wire)
                if (drillable and obj.processCircles) or (not drillable and obj.processHoles):
                    env = PathUtils.getEnvelope(self.baseobject.Shape, subshape=f, depthparams=self.depthparams)
                    shapes.append((env, True))

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

            if obj.processPerimeter:
                env = PathUtils.getEnvelope(self.baseobject.Shape, subshape=profileshape, depthparams=self.depthparams)
                shapes.append((env, False))

        else:  # Try to build targets from the job base
            if hasattr(self.baseobject, "Proxy"):
                if isinstance(self.baseobject.Proxy, ArchPanel.PanelSheet):  # process the sheet
                    if obj.processCircles or obj.processHoles:
                        for shape in self.baseobject.Proxy.getHoles(self.baseobject, transform=True):
                            for wire in shape.Wires:
                                drillable = PathUtils.isDrillable(self.baseobject.Proxy, wire)
                                if (drillable and obj.processCircles) or (not drillable and obj.processHoles):
                                    f = Part.makeFace(wire, 'Part::FaceMakerSimple')
                                    env = PathUtils.getEnvelope(self.baseobject.Shape, subshape=f, depthparams=self.depthparams)
                                    shapes.append((env, True))

                    if obj.processPerimeter:
                        for shape in self.baseobject.Proxy.getOutlines(self.baseobject, transform=True):
                            for wire in shape.Wires:
                                f = Part.makeFace(wire, 'Part::FaceMakerSimple')
                                env = PathUtils.getEnvelope(self.baseobject.Shape, subshape=f, depthparams=self.depthparams)
                                shapes.append((env, False))

        PathLog.debug("%d shapes" % len(shapes))
        return shapes
Exemplo n.º 18
0
 def pointIsOnPath(self, p):
     v = Part.Vertex(self.pointAtBottom(p))
     PathLog.debug("pt = (%f, %f, %f)" % (v.X, v.Y, v.Z))
     for e in self.bottomEdges:
         indent = "{} ".format(e.distToShape(v)[0])
         debugEdge(e, indent, True)
         if PathGeom.isRoughly(0.0, v.distToShape(e)[0], 0.1):
             return True
     return False
Exemplo n.º 19
0
 def updateBase(self):
     newlist = []
     for i in range(self.form.baseList.count()):
         item = self.form.baseList.item(i)
         obj = item.data(self.DataObject)
         sub = str(item.data(self.DataObjectSub))
         base = (obj, sub)
         newlist.append(base)
     PathLog.debug("Setting new base: %s -> %s" % (self.obj.Base, newlist))
     self.obj.Base = newlist
Exemplo n.º 20
0
 def setupToolType(self, tt):
     PathLog.track()
     if 'Undefined' == tt:
         tt = Path.Tool.getToolTypes(Path.Tool())[0]
     if tt in self.ToolTypeImage:
         self.editor = self.ToolTypeImage[tt](self)
     else:
         PathLog.debug("weak supported ToolType = %s" % (tt))
         self.editor = ToolEditorDefault(self)
     self.editor.setupUI()
    def generateHelix(self):
        edges = self.wire.Edges
        minZ = self.findMinZ(edges)
        outedges = []
        i = 0
        while i < len(edges):
            edge = edges[i]
            israpid = False
            for redge in self.rapids:
                if PathGeom.edgesMatch(edge, redge):
                    israpid = True
            if not israpid:
                bb = edge.BoundBox
                p0 = edge.Vertexes[0].Point
                p1 = edge.Vertexes[1].Point
                if bb.XLength < 1e-6 and bb.YLength < 1e-6 and bb.ZLength > 0 and p0.z > p1.z:
                    # plungelen = abs(p0.z-p1.z)
                    PathLog.debug("Found plunge move at X:{} Y:{} From Z:{} to Z{}, Searching for closed loop".format(p0.x, p0.y, p0.z, p1.z))
                    # next need to determine how many edges in the path after plunge are needed to cover the length:
                    loopFound = False
                    rampedges = []
                    j = i + 1
                    while not loopFound:
                        candidate = edges[j]
                        cp0 = candidate.Vertexes[0].Point
                        cp1 = candidate.Vertexes[1].Point
                        if PathGeom.pointsCoincide(p1, cp1):
                            # found closed loop
                            loopFound = True
                            rampedges.append(candidate)
                            break
                        if abs(cp0.z - cp1.z) > 1e-6:
                            # this edge is not parallel to XY plane, not qualified for ramping.
                            break
                        # PathLog.debug("Next edge length {}".format(candidate.Length))
                        rampedges.append(candidate)
                        j = j + 1
                        if j >= len(edges):
                            break
                    if len(rampedges) == 0 or not loopFound:
                        PathLog.debug("No suitable helix found")
                        outedges.append(edge)
                    else:
                        outedges.extend(self.createHelix(rampedges, p0, p1))
                        if not PathGeom.isRoughly(p1.z, minZ):
                            # the edges covered by the helix not handled again,
                            # unless reached the bottom height
                            i = j

                else:
                    outedges.append(edge)
            else:
                outedges.append(edge)
            i = i + 1
        return outedges
Exemplo n.º 22
0
 def areaOpShapeForDepths(self, obj):
     '''areaOpShapeForDepths(obj) ... returns the shape used to make an initial calculation for the depths being used.
     The default implementation returns the job's Base.Shape'''
     job = PathUtils.findParentJob(obj)
     if job and job.Base:
         PathLog.debug("job=%s base=%s shape=%s" % (job, job.Base, job.Base.Shape))
         return job.Base.Shape
     if job:
         PathLog.warning(translate("PathAreaOp", "job %s has no Base.") % job.Label)
     else:
         PathLog.warning(translate("PathAreaOp", "no job for op %s found.") % obj.Label)
     return None
Exemplo n.º 23
0
 def depthSet(self, obj, spinbox, prop):
     z = self.selectionZLevel(FreeCADGui.Selection.getSelectionEx())
     if z is not None:
         PathLog.debug("depthSet(%s, %s, %.2f)" % (obj.Label, prop, z))
         if spinbox.expression():
             obj.setExpression(prop, None)
             self.setDirty()
         spinbox.updateSpinBox(FreeCAD.Units.Quantity(z, FreeCAD.Units.Length))
         if spinbox.updateProperty():
             self.setDirty()
     else:
         PathLog.info("depthSet(-)")
Exemplo n.º 24
0
 def updateBase(self):
     PathLog.track()
     shapes = []
     for i in range(self.form.baseList.count()):
         item = self.form.baseList.item(i)
         obj = item.data(self.super().DataObject)
         sub = item.data(self.super().DataObjectSub)
         if not sub:
             shapes.append(obj)
     PathLog.debug("Setting new base shapes: %s -> %s" % (self.obj.BaseShapes, shapes))
     self.obj.BaseShapes = shapes
     return self.super().updateBase()
Exemplo n.º 25
0
    def setFields(self):
        self.form.startDepth.setText(FreeCAD.Units.Quantity(self.obj.StartDepth.Value, FreeCAD.Units.Length).UserString)
        self.form.finalDepth.setText(FreeCAD.Units.Quantity(self.obj.FinalDepth.Value, FreeCAD.Units.Length).UserString)
        self.form.safeHeight.setText(FreeCAD.Units.Quantity(self.obj.SafeHeight.Value, FreeCAD.Units.Length).UserString)
        self.form.clearanceHeight.setText(FreeCAD.Units.Quantity(self.obj.ClearanceHeight.Value,  FreeCAD.Units.Length).UserString)
        self.form.stepDown.setText(FreeCAD.Units.Quantity(self.obj.StepDown.Value, FreeCAD.Units.Length).UserString)
        self.form.extraOffset.setText(FreeCAD.Units.Quantity(self.obj.OffsetExtra.Value, FreeCAD.Units.Length).UserString)
        self.form.rollRadius.setText(FreeCAD.Units.Quantity(self.obj.RollRadius.Value, FreeCAD.Units.Length).UserString)
        self.form.useCompensation.setChecked(self.obj.UseComp)
        self.form.useStartPoint.setChecked(self.obj.UseStartPoint)
        self.form.useEndPoint.setChecked(self.obj.UseEndPoint)
        self.form.processHoles.setChecked(self.obj.processHoles)
        self.form.processPerimeter.setChecked(self.obj.processPerimeter)
        self.form.processCircles.setChecked(self.obj.processCircles)

        index = self.form.cutSide.findText(
                self.obj.Side, QtCore.Qt.MatchFixedString)
        if index >= 0:
            self.form.cutSide.blockSignals(True)
            self.form.cutSide.setCurrentIndex(index)
            self.form.cutSide.blockSignals(False)

        index = self.form.direction.findText(
                self.obj.Direction, QtCore.Qt.MatchFixedString)
        if index >= 0:
            self.form.direction.blockSignals(True)
            self.form.direction.setCurrentIndex(index)
            self.form.direction.blockSignals(False)

        controllers = PathUtils.getToolControllers(self.obj)
        labels = [c.Label for c in controllers]
        self.form.uiToolController.blockSignals(True)
        self.form.uiToolController.addItems(labels)
        self.form.uiToolController.blockSignals(False)
        if self.obj.ToolController is not None:
            index = self.form.uiToolController.findText(
                self.obj.ToolController.Label, QtCore.Qt.MatchFixedString)
            PathLog.debug("searching for TC label {}. Found Index: {}".format(self.obj.ToolController.Label, index))
            if index >= 0:
                self.form.uiToolController.blockSignals(True)
                self.form.uiToolController.setCurrentIndex(index)
                self.form.uiToolController.blockSignals(False)
        else:
            self.obj.ToolController = PathUtils.findToolController(self.obj)

        self.form.baseList.blockSignals(True)
        for i in self.obj.Base:
            for sub in i[1]:
                self.form.baseList.addItem(i[0].Name + "." + sub)
        self.form.baseList.blockSignals(False)

        self.form.update()
Exemplo n.º 26
0
 def updateBase(self):
     '''updateBase() ... helper function to transfer current table to obj'''
     PathLog.track()
     newlist = []
     for i in range(self.form.baseList.rowCount()):
         item = self.form.baseList.item(i, 0)
         obj = item.data(self.DataObject)
         sub = str(item.data(self.DataObjectSub))
         base = (obj, sub)
         PathLog.debug("keeping (%s.%s)" % (obj.Label, sub))
         newlist.append(base)
     PathLog.debug("obj.Base=%s newlist=%s" % (self.obj.Base, newlist))
     self.obj.Base = newlist
Exemplo n.º 27
0
    def processTags(self, obj):
        tagID = 0
        if PathLog.getLevel(PathLog.thisModule()) == PathLog.Level.DEBUG:
            for tag in self.tags:
                tagID += 1
                if tag.enabled:
                    PathLog.debug("x=%s, y=%s, z=%s" % (tag.x, tag.y, self.pathData.minZ))
                    # debugMarker(FreeCAD.Vector(tag.x, tag.y, self.pathData.minZ), "tag-%02d" % tagID , (1.0, 0.0, 1.0), 0.5)
                    # if tag.angle != 90:
                    #    debugCone(tag.originAt(self.pathData.minZ), tag.r1, tag.r2, tag.actualHeight, "tag-%02d" % tagID)
                    # else:
                    #    debugCylinder(tag.originAt(self.pathData.minZ), tag.fullWidth()/2, tag.actualHeight, "tag-%02d" % tagID)

        obj.Path = self.createPath(obj, self.pathData, self.tags)
Exemplo n.º 28
0
    def adaptiveLength(self, boneAngle, toolRadius):
        angle = self.angle()
        distance = self.distance(toolRadius)
        # there is something weird happening if the boneAngle came from a horizontal/vertical t-bone
        # for some reason pi/2 is not equal to pi/2
        if math.fabs(angle - boneAngle) < 0.00001:
            # moving directly towards the corner
            PathLog.debug("adaptive - on target: %.2f - %.2f" % (distance, toolRadius))
            return distance - toolRadius
        PathLog.debug("adaptive - angles: corner=%.2f  bone=%.2f diff=%.12f" % (angle/math.pi, boneAngle/math.pi, angle - boneAngle))

        # The bones root and end point form a triangle with the intersection of the tool path
        # with the toolRadius circle around the bone end point.
        # In case the math looks questionable, look for "triangle ssa"
        # c = distance
        # b = self.toolRadius
        # beta = fabs(boneAngle - angle)
        beta = math.fabs(addAngle(boneAngle, -angle))
        D = (distance / toolRadius) * math.sin(beta)
        if D > 1:  # no intersection
            PathLog.debug("adaptive - no intersection - no bone")
            return 0
        gamma = math.asin(D)
        alpha = math.pi - beta - gamma
        length = toolRadius * math.sin(alpha) / math.sin(beta)
        if D < 1 and toolRadius < distance:  # there exists a second solution
            beta2 = beta
            gamma2 = math.pi - gamma
            alpha2 = math.pi - beta2 - gamma2
            length2 = toolRadius * math.sin(alpha2) / math.sin(beta2)
            length = min(length, length2)

        PathLog.debug("adaptive corner=%.2f * %.2f˚ -> bone=%.2f * %.2f˚" % (distance, angle, length, boneAngle))
        return length
Exemplo n.º 29
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 = []
Exemplo n.º 30
0
    def processEdge(self, index, edge, currentLength, lastTagLength, tagDistance, minLength, edgeDict):
        tagCount = 0
        currentLength += edge.Length
        if edge.Length >= minLength:
            while lastTagLength + tagDistance < currentLength:
                tagCount += 1
                lastTagLength += tagDistance
            if tagCount > 0:
                PathLog.debug("      index=%d -> count=%d" % (index, tagCount))
                edgeDict[index] = tagCount
        else:
            PathLog.debug("      skipping=%-2d (%.2f)" % (index, edge.Length))

        return (currentLength, lastTagLength)
Exemplo n.º 31
0
    def checkForFacesLoop(self, base, subsList):
        '''checkForFacesLoop(base, subsList)...
            Accepts a list of face names for the given base.
            Checks to determine if they are looped together.
        '''
        PathLog.track()
        fCnt = 0
        go = True
        vertLoopFace = None
        tempNameList = []
        delTempNameList = 0
        saSum = FreeCAD.Vector(0.0, 0.0, 0.0)
        norm = FreeCAD.Vector(0.0, 0.0, 0.0)
        surf = FreeCAD.Vector(0.0, 0.0, 0.0)
        precision = 6

        def makeTempExtrusion(base, sub, fCnt):
            extName = 'tmpExtrude' + str(fCnt)
            wireName = 'tmpWire' + str(fCnt)
            wr = Part.Wire(Part.__sortEdges__(
                base.Shape.getElement(sub).Edges))
            if wr.isNull():
                PathLog.debug('No wire created from {}'.format(sub))
                return (False, 0, 0)
            else:
                tmpWire = FreeCAD.ActiveDocument.addObject(
                    'Part::Feature', wireName).Shape = wr
                tmpWire = FreeCAD.ActiveDocument.getObject(wireName)
                tmpExt = FreeCAD.ActiveDocument.addObject(
                    'Part::Extrusion', extName)
                tmpExt = FreeCAD.ActiveDocument.getObject(extName)
                tmpExt.Base = tmpWire
                tmpExt.DirMode = "Normal"
                tmpExt.DirLink = None
                tmpExt.LengthFwd = 10.0
                tmpExt.LengthRev = 0.0
                tmpExt.Solid = True
                tmpExt.Reversed = False
                tmpExt.Symmetric = False
                tmpExt.TaperAngle = 0.0
                tmpExt.TaperAngleRev = 0.0

                tmpExt.recompute()
                tmpExt.purgeTouched()
                tmpWire.purgeTouched()
                return (True, tmpWire, tmpExt)

        def roundValue(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)

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

        # Sub Surface.Axis values of faces
        # Vector of (0, 0, 0) will suggests a loop
        for sub in subsList:
            if 'Face' in sub:
                fCnt += 1
                saSum = saSum.add(base.Shape.getElement(sub).Surface.Axis)

        # Minimim of three faces required for loop to exist
        if fCnt < 3:
            go = False

        # Determine if all faces combined point toward loop center = False
        if PathGeom.isRoughly(0, saSum.x):
            if PathGeom.isRoughly(0, saSum.y):
                if PathGeom.isRoughly(0, saSum.z):
                    PathLog.debug(
                        "Combined subs suggest loop of faces. Checking ...")
                    go = True

        if go is True:
            lastExtrusion = None
            matchList = []
            go = False

            # Cycle through subs, extruding to solid for each
            for sub in subsList:
                if 'Face' in sub:
                    fCnt += 1
                    go = False

                    # Extrude face to solid
                    (rtn, tmpWire, tmpExt) = makeTempExtrusion(base, sub, fCnt)

                    # If success, record new temporary objects for deletion
                    if rtn is True:
                        tempNameList.append(tmpExt.Name)
                        tempNameList.append(tmpWire.Name)
                        delTempNameList += 1
                        if lastExtrusion is None:
                            lastExtrusion = tmpExt
                            rtn = True
                    else:
                        go = False
                        break

                    # Cycle through faces on each extrusion, looking for common normal faces for rotation analysis
                    if len(matchList) == 0:
                        for fc in lastExtrusion.Shape.Faces:
                            (norm, raw) = self.getFaceNormAndSurf(fc)
                            rnded = FreeCAD.Vector(
                                roundValue(precision, raw.x),
                                roundValue(precision, raw.y),
                                roundValue(precision, raw.z))
                            if rnded.x == 0.0 or rnded.y == 0.0 or rnded.z == 0.0:
                                for fc2 in tmpExt.Shape.Faces:
                                    (norm2,
                                     raw2) = self.getFaceNormAndSurf(fc2)  # pylint: disable=unused-variable
                                    rnded2 = FreeCAD.Vector(
                                        roundValue(precision, raw2.x),
                                        roundValue(precision, raw2.y),
                                        roundValue(precision, raw2.z))
                                    if rnded == rnded2:
                                        matchList.append(fc2)
                                        go = True
                    else:
                        for m in matchList:
                            (norm, raw) = self.getFaceNormAndSurf(m)
                            rnded = FreeCAD.Vector(
                                roundValue(precision, raw.x),
                                roundValue(precision, raw.y),
                                roundValue(precision, raw.z))
                            for fc2 in tmpExt.Shape.Faces:
                                (norm2, raw2) = self.getFaceNormAndSurf(fc2)
                                rnded2 = FreeCAD.Vector(
                                    roundValue(precision, raw2.x),
                                    roundValue(precision, raw2.y),
                                    roundValue(precision, raw2.z))
                                if rnded.x == 0.0 or rnded.y == 0.0 or rnded.z == 0.0:
                                    if rnded == rnded2:
                                        go = True
                        # Eif
                    if go is False:
                        break
                    # Eif
                # Eif 'Face'
            # Efor
        if go is True:
            go = False
            if len(matchList) == 2:
                saTotal = FreeCAD.Vector(0.0, 0.0, 0.0)
                for fc in matchList:
                    (norm, raw) = self.getFaceNormAndSurf(fc)
                    rnded = FreeCAD.Vector(roundValue(precision, raw.x),
                                           roundValue(precision, raw.y),
                                           roundValue(precision, raw.z))
                    if (rnded.y > 0.0
                            or rnded.z > 0.0) and vertLoopFace is None:
                        vertLoopFace = fc
                    saTotal = saTotal.add(rnded)

                if saTotal == FreeCAD.Vector(0.0, 0.0, 0.0):
                    if vertLoopFace is not None:
                        go = True

        if go is True:
            (norm, surf) = self.getFaceNormAndSurf(vertLoopFace)
        else:
            PathLog.debug(translate('Path', 'Can not identify loop.'))

        if delTempNameList > 0:
            for tmpNm in tempNameList:
                FreeCAD.ActiveDocument.removeObject(tmpNm)

        return (go, norm, surf)
Exemplo n.º 32
0
    def getLeadStart(self, obj, queue, action):
        '''returns Lead In G-code.'''
        results = []
        op = PathDressup.baseOp(obj.Base)
        tc = PathDressup.toolController(obj.Base)
        horizFeed = tc.HorizFeed.Value
        vertFeed = tc.VertFeed.Value
        toolnummer = tc.ToolNumber
        arcs_identical = False

        # Set the correct twist command
        if self.getDirectionOfPath(obj) == 'left':
            arcdir = "G3"
        else:
            arcdir = "G2"

        R = obj.Length.Value  # Radius of roll or length
        if queue[1].Name == "G1":  # line
            p0 = queue[0].Placement.Base
            p1 = queue[1].Placement.Base
            v = self.normalize(p1.sub(p0))
            # PathLog.debug(" CURRENT_IN : P0 Z:{} p1 Z:{}".format(p0.z,p1.z))
        else:
            p0 = queue[0].Placement.Base
            p1 = queue[1].Placement.Base
            v = self.normalize(p1.sub(p0))
            # PathLog.debug(" CURRENT_IN ARC : P0 X:{} Y:{} P1 X:{} Y:{} ".format(p0.x,p0.y,p1.x,p1.y))

        # Calculate offset vector (will be overwritten for arcs)
        if self.getDirectionOfPath(obj) == 'right':
            off_v = FreeCAD.Vector(v.y*R, -v.x*R, 0.0)
        else:
            off_v = FreeCAD.Vector(-v.y*R, v.x*R, 0.0)

        # Check if we enter at line or arc command
        if queue[1].Name in movecommands and queue[1].Name not in arccommands:
            # We have a line move
            vec = p1.sub(p0)
            vec_n = self.normalize(vec)
            vec_inv = self.invert(vec_n)
            vec_off = self.multiply(vec_inv,  obj.ExtendLeadIn)
            #PathLog.debug("LineCMD: {}, Vxinv: {}, Vyinv: {}, Vxoff: {}, Vyoff: {}".format(queue[0].Name, vec_inv.x, vec_inv.y,  vec_off.x,  vec_off.y))
        else:
            # We have an arc move
            # Calculate coordinates for middle of circle
            pij = copy.deepcopy(p0)
            pij.x += queue[1].Parameters['I']
            pij.y += queue[1].Parameters['J']

            # Check if lead in and operation go in same direction (usually for inner circles)
            if arcdir == queue[1].Name:
                arcs_identical = True

            # Calculate vector circle start -> circle middle
            vec_circ = pij.sub(p0)

            # Rotate vector to get direction for lead in
            if arcdir == "G2":
                vec_rot = self.rotate(vec_circ,  90)
            else:
                vec_rot = self.rotate(vec_circ,  -90)

            # Normalize and invert vector
            vec_n = self.normalize(vec_rot)

            v = self.invert(vec_n)

            # Calculate offset of lead in
            if arcdir == "G3":
                off_v = FreeCAD.Vector(-v.y*R, v.x*R, 0.0)
            else:
                off_v = FreeCAD.Vector(v.y*R, -v.x*R, 0.0)

            # Multiply offset by LeadIn length
            vec_off = self.multiply(vec_n,  obj.ExtendLeadIn)

        offsetvector = FreeCAD.Vector(v.x*R-vec_off.x, v.y*R-vec_off.y, 0)  # IJ

        if obj.RadiusCenter == 'Radius':
            leadstart = (p0.add(off_v)).sub(offsetvector)  # Rmode
            if arcs_identical:
                t = p0.sub(leadstart)
                t = p0.add(t)
                leadstart = t
                offsetvector = self.multiply(offsetvector,  -1)
        else:
            leadstart = p0.add(off_v)  # Dmode

        if action == 'start':
            #extendcommand = Path.Command('G0', {"X": 0.0, "Y": 0.0, "Z": op.ClearanceHeight.Value})
            #results.append(extendcommand)
            extendcommand = Path.Command('G0', {"X": leadstart.x, "Y": leadstart.y, "Z": op.ClearanceHeight.Value})
            results.append(extendcommand)
            extendcommand = Path.Command('G0', {"Z": op.SafeHeight.Value})
            results.append(extendcommand)

        if action == 'layer':
            if not obj.KeepToolDown:
                extendcommand = Path.Command('G0', {"Z": op.SafeHeight.Value})
                results.append(extendcommand)

            extendcommand = Path.Command('G0', {"X": leadstart.x, "Y": leadstart.y})
            results.append(extendcommand)

        if not obj.RapidPlunge:
            extendcommand = Path.Command('G1', {"X": leadstart.x, "Y": leadstart.y, "Z": p1.z, "F": vertFeed})
        else:
            extendcommand = Path.Command('G0', {"X": leadstart.x, "Y": leadstart.y, "Z": p1.z,})
        results.append(extendcommand)

        if obj.UseMachineCRC:
            if self.getDirectionOfPath(obj) == 'right':
                results.append(Path.Command('G42', {'D': toolnummer}))
            else:
                results.append(Path.Command('G41', {'D': toolnummer}))

        if obj.StyleOn == 'Arc':
            arcmove = Path.Command(arcdir, {"X": p0.x+vec_off.x, "Y": p0.y+vec_off.y, "I": offsetvector.x+vec_off.x, "J": offsetvector.y+vec_off.y, "F": horizFeed})  # add G2/G3 move
            results.append(arcmove)
            if obj.ExtendLeadIn != 0:
                extendcommand = Path.Command('G1', {"X": p0.x, "Y": p0.y, "F": horizFeed})
                results.append(extendcommand)
        elif obj.StyleOn == 'Tangent':
            extendcommand = Path.Command('G1', {"X": p0.x, "Y": p0.y, "F": horizFeed})
            results.append(extendcommand)
        else:
            PathLog.debug(" CURRENT_IN Perp")

        currLocation.update(results[-1].Parameters)
        currLocation['Z'] = p1.z

        return results
Exemplo n.º 33
0
    def execute(self, obj):
        import Part  # math #DraftGeomUtils
        output = ""

        toolLoad = obj.ToolController
        if toolLoad is None or toolLoad.ToolNumber == 0:
            FreeCAD.Console.PrintError(
                "No Tool Controller is selected. We need a tool to build a Path."
            )
            #return
        else:
            self.vertFeed = toolLoad.VertFeed.Value
            self.horizFeed = toolLoad.HorizFeed.Value
            self.vertRapid = toolLoad.VertRapid.Value
            self.horizRapid = toolLoad.HorizRapid.Value
            tool = toolLoad.Proxy.getTool(toolLoad)
            if not tool or tool.Diameter == 0:
                FreeCAD.Console.PrintError(
                    "No Tool found or diameter is zero. We need a tool to build a Path."
                )
                return
            else:
                self.radius = tool.Diameter / 2

        output += "(" + obj.Label + ")"
        if obj.UseComp:
            output += "(Compensated Tool Path. Diameter: " + str(
                self.radius * 2) + ")"
        else:
            output += "(Uncompensated Tool Path)"

        if obj.Base:
            holes = []
            faces = []
            for b in obj.Base:
                for sub in b[1]:
                    shape = getattr(b[0].Shape, sub)
                    if isinstance(shape, Part.Face):
                        faces.append(shape)
                        if numpy.isclose(abs(shape.normalAt(0, 0).z),
                                         1):  # horizontal face
                            holes += shape.Wires[1:]

                    else:
                        print(
                            "found a base object which is not a face.  Can't continue."
                        )
                        return
            profileshape = Part.makeCompound(faces)
            profilewire = TechDraw.findShapeOutline(profileshape, 1,
                                                    Vector(0, 0, 1))

            if obj.processHoles:
                for wire in holes:
                    edgelist = wire.Edges
                    edgelist = Part.__sortEdges__(edgelist)
                    output += self._buildPathLibarea(obj, edgelist, True)

            if obj.processPerimeter:
                edgelist = profilewire.Edges
                edgelist = Part.__sortEdges__(edgelist)
                output += self._buildPathLibarea(obj, edgelist, False)

        else:  #Try to build targets frorm the job base
            parentJob = PathUtils.findParentJob(obj)
            if parentJob is None:
                return
            baseobject = parentJob.Base
            if baseobject is None:
                return

            if hasattr(baseobject, "Proxy"):
                if isinstance(baseobject.Proxy,
                              ArchPanel.PanelSheet):  # process the sheet
                    if obj.processPerimeter:
                        shapes = baseobject.Proxy.getOutlines(baseobject,
                                                              transform=True)
                        for shape in shapes:
                            for wire in shape.Wires:
                                edgelist = wire.Edges
                                edgelist = Part.__sortEdges__(edgelist)
                                PathLog.debug(
                                    "Processing panel perimeter.  edges found: {}"
                                    .format(len(edgelist)))
                            try:
                                output += self._buildPathLibarea(obj,
                                                                 edgelist,
                                                                 isHole=False)
                            except:
                                FreeCAD.Console.PrintError(
                                    "Something unexpected happened. Unable to generate a contour path. Check project and tool config."
                                )

                    shapes = baseobject.Proxy.getHoles(baseobject,
                                                       transform=True)
                    for shape in shapes:
                        for wire in shape.Wires:
                            drillable = PathUtils.isDrillable(
                                baseobject.Proxy, wire)
                            if (drillable and obj.processCircles) or (
                                    not drillable and obj.processHoles):
                                edgelist = wire.Edges
                                edgelist = Part.__sortEdges__(edgelist)
                                try:
                                    output += self._buildPathLibarea(
                                        obj, edgelist, isHole=True)
                                except:
                                    FreeCAD.Console.PrintError(
                                        "Something unexpected happened. Unable to generate a contour path. Check project and tool config."
                                    )

        if obj.Active:
            path = Path.Path(output)
            obj.Path = path
            obj.ViewObject.Visibility = True

        else:
            path = Path.Path("(inactive operation)")
            obj.Path = path
            obj.ViewObject.Visibility = False
Exemplo n.º 34
0
    def generateTags(self,
                     obj,
                     count,
                     width=None,
                     height=None,
                     angle=None,
                     radius=None,
                     spacing=None):
        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
Exemplo n.º 35
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.leadIn = 2.0  # pylint: disable=attribute-defined-outside-init

        # Initiate depthparams and calculate operation heights for 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
        shapes = []
        for shp in aOS:
            if len(shp) == 2:
                (fc, iH) = shp
                #     fc, iH,  sub or description
                tup = fc, iH, "otherOp"
                shapes.append(tup)
            else:
                shapes.append(shp)

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

            locations = PathUtils.sort_locations(locations, ["x", "y"])

            shapes = [j["shape"] for j in locations]

        sims = []
        for shape, isHole, sub in shapes:
            profileEdgesIsOpen = False

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

            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

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

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

        PathLog.debug("obj.Name: " + str(obj.Name) + "\n\n")
        return sims
Exemplo n.º 36
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
Exemplo n.º 37
0
    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()

        holes = []
        baseSubsTuples = []
        subCount = 0
        allTuples = []
        self.cloneNames = []  # pylint: disable=attribute-defined-outside-init
        self.guiMsgs = []  # pylint: disable=attribute-defined-outside-init
        self.rotateFlag = False  # pylint: disable=attribute-defined-outside-init
        self.useTempJobClones('Delete')  # pylint: disable=attribute-defined-outside-init
        self.stockBB = PathUtils.findParentJob(obj).Stock.Shape.BoundBox  # pylint: disable=attribute-defined-outside-init
        self.clearHeight = obj.ClearanceHeight.Value  # pylint: disable=attribute-defined-outside-init
        self.safeHeight = obj.SafeHeight.Value  # pylint: disable=attribute-defined-outside-init
        self.axialFeed = 0.0  # pylint: disable=attribute-defined-outside-init
        self.axialRapid = 0.0  # pylint: disable=attribute-defined-outside-init
        trgtDep = None

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

        if obj.EnableRotation == 'Off':
            strDep = obj.StartDepth.Value
            finDep = obj.FinalDepth.Value
        else:
            # 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
            (clrOfset, safOfst) = opHeights[1]
            PathLog.debug("Exec. opHeights[0]: " + str(opHeights[0]))
            PathLog.debug("Exec. opHeights[1]: " + str(opHeights[1]))

            # 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 + clrOfset
            obj.SafeHeight.Value = strDep + safOfst

            # Create visual axes when debugging.
            if PathLog.getLevel(PathLog.thisModule()) == 4:
                self.visualAxis()

            # 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

        # Complete rotational analysis and temp clone creation as needed
        if obj.EnableRotation == 'Off':
            PathLog.debug("Enable Rotation setting is 'Off' for {}.".format(
                obj.Name))
            stock = PathUtils.findParentJob(obj).Stock
            for (base, subList) in obj.Base:
                baseSubsTuples.append((base, subList, 0.0, 'A', stock))
        else:
            for p in range(0, len(obj.Base)):
                (base, subsList) = obj.Base[p]
                for sub in subsList:
                    if self.isHoleEnabled(obj, base, sub):
                        shape = getattr(base.Shape, sub)
                        rtn = False
                        (norm, surf) = self.getFaceNormAndSurf(shape)
                        (rtn, angle, axis,
                         praInfo) = self.faceRotationAnalysis(obj, norm, surf)  # pylint: disable=unused-variable
                        if rtn is True:
                            (clnBase, angle, clnStock,
                             tag) = self.applyRotationalAnalysis(
                                 obj, base, angle, axis, subCount)
                            # Verify faces are correctly oriented - InverseAngle might be necessary
                            PathLog.debug(
                                "Verifying {} orientation: running faceRotationAnalysis() again."
                                .format(sub))
                            faceIA = getattr(clnBase.Shape, sub)
                            (norm, surf) = self.getFaceNormAndSurf(faceIA)
                            (rtn, praAngle, praAxis,
                             praInfo) = self.faceRotationAnalysis(
                                 obj, norm, surf)  # pylint: disable=unused-variable
                            if rtn is True:
                                msg = obj.Name + ":: "
                                msg += translate(
                                    "Path",
                                    "{} might be misaligned after initial rotation."
                                    .format(sub)) + "  "
                                if obj.AttemptInverseAngle is True and obj.InverseAngle is False:
                                    (clnBase, clnStock,
                                     angle) = self.applyInverseAngle(
                                         obj, clnBase, clnStock, axis, angle)
                                    msg += translate(
                                        "Path",
                                        "Rotated to 'InverseAngle' to attempt access."
                                    )
                                else:
                                    if len(subsList) == 1:
                                        msg += translate(
                                            "Path",
                                            "Consider toggling the 'InverseAngle' property and recomputing."
                                        )
                                    else:
                                        msg += translate(
                                            "Path",
                                            "Consider transferring '{}' to independent operation."
                                            .format(sub))
                                PathLog.warning(msg)
                                # title = translate("Path", 'Rotation Warning')
                                # self.guiMessage(title, msg, False)
                            else:
                                PathLog.debug(
                                    "Face appears to be oriented correctly.")

                            cmnt = "{}: {} @ {};  ".format(
                                sub, axis, str(round(angle, 5)))
                            if cmnt not in obj.Comment:
                                obj.Comment += cmnt

                            tup = clnBase, sub, tag, angle, axis, clnStock
                            allTuples.append(tup)
                        else:
                            if self.warnDisabledAxis(obj, axis, sub) is True:
                                pass  # Skip drill feature due to access issue
                            else:
                                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
                                allTuples.append(tup)
                        # Eif
                    # Eif
                    subCount += 1
                # Efor
            # Efor
            (Tags,
             Grps) = self.sortTuplesByIndex(allTuples,
                                            2)  # return (TagList, GroupList)
            subList = []
            for o in range(0, len(Tags)):
                PathLog.debug('hTag: {}'.format(Tags[o]))
                subList = []
                for (base, sub, tag, angle, axis, stock) in Grps[o]:
                    subList.append(sub)
                pair = base, subList, angle, axis, stock
                baseSubsTuples.append(pair)
            # Efor

        for base, subs, angle, axis, stock in baseSubsTuples:
            for sub in subs:
                if self.isHoleEnabled(obj, base, sub):
                    pos = self.holePosition(obj, base, sub)
                    if pos:
                        # Default is treat selection as 'Face' shape
                        finDep = base.Shape.getElement(sub).BoundBox.ZMin
                        if base.Shape.getElement(sub).ShapeType == 'Edge':
                            msg = translate(
                                "Path",
                                "Verify Final Depth of holes based on edges. {} depth is: {} mm"
                                .format(sub, round(finDep, 4))) + "  "
                            msg += translate(
                                "Path",
                                "Always select the bottom edge of the hole when using an edge."
                            )
                            PathLog.warning(msg)

                        # If user has not adjusted Final Depth value, attempt to determine from sub
                        trgtDep = obj.FinalDepth.Value
                        if obj.OpFinalDepth.Value == obj.FinalDepth.Value:
                            trgtDep = finDep
                        if obj.FinalDepth.Value < finDep:
                            msg = translate(
                                "Path",
                                "Final Depth setting is below the hole bottom for {}."
                                .format(sub))
                            PathLog.warning(msg)

                        holes.append({
                            'x': pos.x,
                            'y': pos.y,
                            'r': self.holeDiameter(obj, base, sub),
                            'angle': angle,
                            'axis': axis,
                            'trgtDep': trgtDep,
                            'stkTop': stock.Shape.BoundBox.ZMax
                        })

        if haveLocations(self, obj):
            for location in obj.Locations:
                # holes.append({'x': location.x, 'y': location.y, 'r': 0, 'angle': 0.0, 'axis': 'X', 'finDep': obj.FinalDepth.Value})
                trgtDep = obj.FinalDepth.Value
                holes.append({
                    'x':
                    location.x,
                    'y':
                    location.y,
                    'r':
                    0,
                    'angle':
                    0.0,
                    'axis':
                    'X',
                    'trgtDep':
                    trgtDep,
                    'stkTop':
                    PathUtils.findParentJob(obj).stock.Shape.BoundBox.ZMax
                })

        # If all holes based upon edges, set post-operation Final Depth to highest edge height
        if obj.OpFinalDepth.Value == obj.FinalDepth.Value:
            if len(holes) == 1:
                PathLog.info(
                    translate(
                        'Path',
                        "Single-hole operation. Saving Final Depth determined from hole base."
                    ))
                obj.FinalDepth.Value = trgtDep

        if len(holes) > 0:
            self.circularHoleExecute(
                obj, holes)  # circularHoleExecute() located in PathDrilling.py

        self.useTempJobClones(
            'Delete')  # Delete temp job clone group and contents
        self.guiMessage('title', None,
                        show=True)  # Process GUI messages to user
        PathLog.debug("obj.Name: " + str(obj.Name))
Exemplo n.º 38
0
    def createRampMethod2(self, rampedges, p0, projectionlen, rampangle):
        """
        This method generates ramp with following pattern:
        1. Start from the original startpoint of the plunge
        2. Calculate the distance on the path which is needed to implement the ramp
           and travel that distance while maintaining start depth
        3. Start ramping while travelling the original path backwards until reaching the
           original plunge end point
        4. Continue with the original path
        """
        outedges = []
        rampremaining = projectionlen
        curPoint = p0  # start from the upper point of plunge
        if PathGeom.pointsCoincide(
                PathGeom.xy(p0),
                PathGeom.xy(rampedges[-1].valueAt(
                    rampedges[-1].LastParameter))):
            PathLog.debug(
                "The ramp forms a closed wire, needless to move on original Z height"
            )
        else:
            for i, redge in enumerate(rampedges):
                if redge.Length >= rampremaining:
                    # this edge needs to be split
                    p1 = self.getSplitPoint(redge, rampremaining)
                    splitEdge = PathGeom.splitEdgeAt(redge, p1)
                    PathLog.debug(
                        "Got split edges with lengths: {}, {}".format(
                            splitEdge[0].Length, splitEdge[1].Length))
                    # ramp starts at the last point of first edge
                    p1 = splitEdge[0].valueAt(splitEdge[0].LastParameter)
                    p1.z = p0.z
                    outedges.append(
                        self.createRampEdge(splitEdge[0], curPoint, p1))
                    # now we have reached the beginning of the ramp.
                    # start that by going to the beginning of this splitEdge
                    deltaZ = splitEdge[0].Length / math.tan(
                        math.radians(rampangle))
                    newPoint = FreeCAD.Base.Vector(
                        splitEdge[0].valueAt(splitEdge[0].FirstParameter).x,
                        splitEdge[0].valueAt(splitEdge[0].FirstParameter).y,
                        p1.z - deltaZ)
                    outedges.append(
                        self.createRampEdge(splitEdge[0], p1, newPoint))
                    curPoint = newPoint
                elif i == len(rampedges) - 1:
                    # last ramp element but still did not reach the full length?
                    # Probably a rounding issue on floats.
                    # Lets start the ramp anyway
                    p1 = redge.valueAt(redge.LastParameter)
                    p1.z = p0.z
                    outedges.append(self.createRampEdge(redge, curPoint, p1))
                    # and go back that edge
                    deltaZ = redge.Length / math.tan(math.radians(rampangle))
                    newPoint = FreeCAD.Base.Vector(
                        redge.valueAt(redge.FirstParameter).x,
                        redge.valueAt(redge.FirstParameter).y, p1.z - deltaZ)
                    outedges.append(self.createRampEdge(redge, p1, newPoint))
                    curPoint = newPoint

                else:
                    # we are travelling on start depth
                    newPoint = FreeCAD.Base.Vector(
                        redge.valueAt(redge.LastParameter).x,
                        redge.valueAt(redge.LastParameter).y, p0.z)
                    outedges.append(
                        self.createRampEdge(redge, curPoint, newPoint))
                    curPoint = newPoint
                    rampremaining = rampremaining - redge.Length

            # the last edge got handled previously
            rampedges.pop()
        # ramp backwards to the plunge position
        for i, redge in enumerate(reversed(rampedges)):
            deltaZ = redge.Length / math.tan(math.radians(rampangle))
            newPoint = FreeCAD.Base.Vector(
                redge.valueAt(redge.FirstParameter).x,
                redge.valueAt(redge.FirstParameter).y, curPoint.z - deltaZ)
            if i == len(rampedges) - 1:
                # make sure that the last point of the ramps ends to the original position
                newPoint = redge.valueAt(redge.FirstParameter)
            outedges.append(self.createRampEdge(redge, curPoint, newPoint))
            curPoint = newPoint

        return outedges
Exemplo n.º 39
0
    def createRampMethod3(self, rampedges, p0, projectionlen, rampangle):
        """
        This method generates ramp with following pattern:
        1. Start from the original startpoint of the plunge
        2. Ramp down along the path that comes after the plunge until
           traveled half of the Z distance
        3. Change direction and ramp backwards to the original plunge end point
        4. Continue with the original path

        This method causes many unnecessary moves with tool down.
        """
        outedges = []
        rampremaining = projectionlen
        curPoint = p0  # start from the upper point of plunge
        done = False

        while not done:
            for i, redge in enumerate(rampedges):
                if redge.Length >= rampremaining:
                    # will reach end of ramp within this edge, needs to be split
                    p1 = self.getSplitPoint(redge, rampremaining)
                    splitEdge = PathGeom.splitEdgeAt(redge, p1)
                    PathLog.debug(
                        "Got split edge (index: {}) with lengths: {}, {}".
                        format(i, splitEdge[0].Length, splitEdge[1].Length))
                    # ramp ends to the last point of first edge
                    p1 = splitEdge[0].valueAt(splitEdge[0].LastParameter)
                    deltaZ = splitEdge[0].Length / math.tan(
                        math.radians(rampangle))
                    p1.z = curPoint.z - deltaZ
                    outedges.append(
                        self.createRampEdge(splitEdge[0], curPoint, p1))
                    curPoint.z = p1.z - deltaZ
                    # now we have reached the end of the ramp. Reverse direction of ramp
                    # start that by going back to the beginning of this splitEdge
                    outedges.append(
                        self.createRampEdge(splitEdge[0], p1, curPoint))

                    done = True
                    break
                elif i == len(rampedges) - 1:
                    # last ramp element but still did not reach the full length?
                    # Probably a rounding issue on floats.
                    p1 = redge.valueAt(redge.LastParameter)
                    deltaZ = redge.Length / math.tan(math.radians(rampangle))
                    p1.z = curPoint.z - deltaZ
                    outedges.append(self.createRampEdge(redge, curPoint, p1))
                    # and go back that edge
                    newPoint = FreeCAD.Base.Vector(
                        redge.valueAt(redge.FirstParameter).x,
                        redge.valueAt(redge.FirstParameter).y, p1.z - deltaZ)
                    outedges.append(self.createRampEdge(redge, p1, newPoint))
                    curPoint = newPoint
                    done = True
                else:
                    deltaZ = redge.Length / math.tan(math.radians(rampangle))
                    newPoint = FreeCAD.Base.Vector(
                        redge.valueAt(redge.LastParameter).x,
                        redge.valueAt(redge.LastParameter).y,
                        curPoint.z - deltaZ)
                    outedges.append(
                        self.createRampEdge(redge, curPoint, newPoint))
                    curPoint = newPoint
                    rampremaining = rampremaining - redge.Length

        returnedges = self.getreversed(rampedges[:i])

        # ramp backwards to the plunge position
        for i, redge in enumerate(returnedges):
            deltaZ = redge.Length / math.tan(math.radians(rampangle))
            newPoint = FreeCAD.Base.Vector(
                redge.valueAt(redge.LastParameter).x,
                redge.valueAt(redge.LastParameter).y, curPoint.z - deltaZ)
            if i == len(rampedges) - 1:
                # make sure that the last point of the ramps ends to the original position
                newPoint = redge.valueAt(redge.LastParameter)
            outedges.append(self.createRampEdge(redge, curPoint, newPoint))
            curPoint = newPoint

        return outedges
Exemplo n.º 40
0
    def createRampMethod1(self, rampedges, p0, projectionlen, rampangle):
        """
        This method generates ramp with following pattern:
        1. Start from the original startpoint of the plunge
        2. Ramp down along the path that comes after the plunge
        3. When reaching the Z level of the original plunge, return back to the beginning
           by going the path backwards until the original plunge end point is reached
        4. Continue with the original path

        This method causes many unnecessary moves with tool down.
        """
        outedges = []
        rampremaining = projectionlen
        curPoint = p0  # start from the upper point of plunge
        done = False
        goingForward = True
        while not done:
            for i, redge in enumerate(rampedges):
                if redge.Length >= rampremaining:
                    # will reach end of ramp within this edge, needs to be split
                    p1 = self.getSplitPoint(redge, rampremaining)
                    splitEdge = PathGeom.splitEdgeAt(redge, p1)
                    PathLog.debug("Ramp remaining: {}".format(rampremaining))
                    PathLog.debug(
                        "Got split edge (index: {}) (total len: {}) with lengths: {}, {}"
                        .format(i, redge.Length, splitEdge[0].Length,
                                splitEdge[1].Length))
                    # ramp ends to the last point of first edge
                    p1 = splitEdge[0].valueAt(splitEdge[0].LastParameter)
                    outedges.append(
                        self.createRampEdge(splitEdge[0], curPoint, p1))
                    # now we have reached the end of the ramp. Go back to plunge position with constant Z
                    # start that by going to the beginning of this splitEdge
                    if goingForward:
                        outedges.append(
                            self.createRampEdge(
                                splitEdge[0], p1,
                                redge.valueAt(redge.FirstParameter)))
                    else:
                        # if we were reversing, we continue to the same direction as the ramp
                        outedges.append(
                            self.createRampEdge(
                                splitEdge[0], p1,
                                redge.valueAt(redge.LastParameter)))
                    done = True
                    break
                else:
                    deltaZ = redge.Length / math.tan(math.radians(rampangle))
                    newPoint = FreeCAD.Base.Vector(
                        redge.valueAt(redge.LastParameter).x,
                        redge.valueAt(redge.LastParameter).y,
                        curPoint.z - deltaZ)
                    outedges.append(
                        self.createRampEdge(redge, curPoint, newPoint))
                    curPoint = newPoint
                    rampremaining = rampremaining - redge.Length

            if not done:
                # we did not reach the end of the ramp going this direction, lets reverse.
                rampedges = self.getreversed(rampedges)
                PathLog.debug("Reversing")
                if goingForward:
                    goingForward = False
                else:
                    goingForward = True
        # now we need to return to original position.
        if goingForward:
            # if the ramp was going forward, the return edges are the edges we already covered in ramping,
            # except the last one, which was already covered inside for loop. Direction needs to be reversed also
            returnedges = self.getreversed(rampedges[:i])
        else:
            # if the ramp was already reversing, the edges needed for return are the ones
            # which were not covered in ramp
            returnedges = rampedges[(i + 1):]

        # add the return edges:
        outedges.extend(returnedges)

        return outedges
Exemplo n.º 41
0
    def generateHelix(self):
        edges = self.wire.Edges
        minZ = self.findMinZ(edges)
        outedges = []
        i = 0
        while i < len(edges):
            edge = edges[i]
            israpid = False
            for redge in self.rapids:
                if PathGeom.edgesMatch(edge, redge):
                    israpid = True
            if not israpid:
                bb = edge.BoundBox
                p0 = edge.Vertexes[0].Point
                p1 = edge.Vertexes[1].Point
                if bb.XLength < 1e-6 and bb.YLength < 1e-6 and bb.ZLength > 0 and p0.z > p1.z:
                    # plungelen = abs(p0.z-p1.z)
                    PathLog.debug(
                        "Found plunge move at X:{} Y:{} From Z:{} to Z{}, Searching for closed loop"
                        .format(p0.x, p0.y, p0.z, p1.z))
                    # check if above ignoreAbove parameter - do not generate helix if it is
                    newEdge, cont = self.checkIgnoreAbove(edge)
                    if newEdge is not None:
                        outedges.append(newEdge)
                        p0.z = self.ignoreAbove
                    if cont:
                        i = i + 1
                        continue
                    # next need to determine how many edges in the path after plunge are needed to cover the length:
                    loopFound = False
                    rampedges = []
                    j = i + 1
                    while not loopFound:
                        candidate = edges[j]
                        cp0 = candidate.Vertexes[0].Point
                        cp1 = candidate.Vertexes[1].Point
                        if PathGeom.pointsCoincide(p1, cp1):
                            # found closed loop
                            loopFound = True
                            rampedges.append(candidate)
                            break
                        if abs(cp0.z - cp1.z) > 1e-6:
                            # this edge is not parallel to XY plane, not qualified for ramping.
                            break
                        # PathLog.debug("Next edge length {}".format(candidate.Length))
                        rampedges.append(candidate)
                        j = j + 1
                        if j >= len(edges):
                            break
                    if len(rampedges) == 0 or not loopFound:
                        PathLog.debug("No suitable helix found")
                        outedges.append(edge)
                    else:
                        outedges.extend(self.createHelix(rampedges, p0, p1))
                        if not PathGeom.isRoughly(p1.z, minZ):
                            # the edges covered by the helix not handled again,
                            # unless reached the bottom height
                            i = j

                else:
                    outedges.append(edge)
            else:
                outedges.append(edge)
            i = i + 1
        return outedges
Exemplo n.º 42
0
    def generateRamps(self, allowBounce=True):
        edges = self.wire.Edges
        outedges = []
        for edge in edges:
            israpid = False
            for redge in self.rapids:
                if PathGeom.edgesMatch(edge, redge):
                    israpid = True
            if not israpid:
                bb = edge.BoundBox
                p0 = edge.Vertexes[0].Point
                p1 = edge.Vertexes[1].Point
                rampangle = self.angle
                if bb.XLength < 1e-6 and bb.YLength < 1e-6 and bb.ZLength > 0 and p0.z > p1.z:

                    # check if above ignoreAbove parameter - do not generate ramp if it is
                    newEdge, cont = self.checkIgnoreAbove(edge)
                    if newEdge is not None:
                        outedges.append(newEdge)
                        p0.z = self.ignoreAbove
                    if cont:
                        continue

                    plungelen = abs(p0.z - p1.z)
                    projectionlen = plungelen * math.tan(
                        math.radians(rampangle)
                    )  # length of the forthcoming ramp projected to XY plane
                    PathLog.debug(
                        "Found plunge move at X:{} Y:{} From Z:{} to Z{}, length of ramp: {}"
                        .format(p0.x, p0.y, p0.z, p1.z, projectionlen))
                    if self.method == 'RampMethod3':
                        projectionlen = projectionlen / 2

                    # next need to determine how many edges in the path after
                    # plunge are needed to cover the length:
                    covered = False
                    coveredlen = 0
                    rampedges = []
                    i = edges.index(edge) + 1
                    while not covered:
                        candidate = edges[i]
                        cp0 = candidate.Vertexes[0].Point
                        cp1 = candidate.Vertexes[1].Point
                        if abs(cp0.z - cp1.z) > 1e-6:
                            # this edge is not parallel to XY plane, not qualified for ramping.
                            break
                        # PathLog.debug("Next edge length {}".format(candidate.Length))
                        rampedges.append(candidate)
                        coveredlen = coveredlen + candidate.Length

                        if coveredlen > projectionlen:
                            covered = True
                        i = i + 1
                        if i >= len(edges):
                            break
                    if len(rampedges) == 0:
                        PathLog.debug(
                            "No suitable edges for ramping, plunge will remain as such"
                        )
                        outedges.append(edge)
                    else:
                        if not covered:
                            if (not allowBounce
                                ) or self.method == 'RampMethod2':
                                l = 0
                                for redge in rampedges:
                                    l = l + redge.Length
                                if self.method == 'RampMethod3':
                                    rampangle = math.degrees(
                                        math.atan(l / (plungelen / 2)))
                                else:
                                    rampangle = math.degrees(
                                        math.atan(l / plungelen))
                                PathLog.warning(
                                    "Cannot cover with desired angle, tightening angle to: {}"
                                    .format(rampangle))

                        # PathLog.debug("Doing ramp to edges: {}".format(rampedges))
                        if self.method == 'RampMethod1':
                            outedges.extend(
                                self.createRampMethod1(rampedges, p0,
                                                       projectionlen,
                                                       rampangle))
                        elif self.method == 'RampMethod2':
                            outedges.extend(
                                self.createRampMethod2(rampedges, p0,
                                                       projectionlen,
                                                       rampangle))
                        else:
                            # if the ramp cannot be covered with Method3, revert to Method1
                            # because Method1 support going back-and-forth and thus results in same path as Method3 when
                            # length of the ramp is smaller than needed for single ramp.
                            if (not covered) and allowBounce:
                                projectionlen = projectionlen * 2
                                outedges.extend(
                                    self.createRampMethod1(
                                        rampedges, p0, projectionlen,
                                        rampangle))
                            else:
                                outedges.extend(
                                    self.createRampMethod3(
                                        rampedges, p0, projectionlen,
                                        rampangle))
                else:
                    outedges.append(edge)
            else:
                outedges.append(edge)
        return outedges
Exemplo n.º 43
0
    def areaOpShapes(self, obj):
        '''areaOpShapes(obj) ... return shapes representing the solids to be removed.'''
        PathLog.track()

        subObjTups = []
        removalshapes = []

        if obj.Base:
            PathLog.debug("base items exist.  Processing... ")
            for base in obj.Base:
                PathLog.debug("obj.Base item: {}".format(base))

                # Check if all subs are faces
                allSubsFaceType = True
                Faces = []
                for sub in base[1]:
                    if "Face" in sub:
                        face = getattr(base[0].Shape, sub)
                        Faces.append(face)
                        subObjTups.append((sub, face))
                    else:
                        allSubsFaceType = False
                        break

                if len(Faces) == 0:
                    allSubsFaceType = False

                if allSubsFaceType is True and obj.HandleMultipleFeatures == 'Collectively':
                    (fzmin, fzmax) = self.getMinMaxOfFaces(Faces)
                    if obj.FinalDepth.Value < fzmin:
                        PathLog.warning(translate('PathPocket', 'Final depth set below ZMin of face(s) selected.'))
                    '''
                    if obj.OpFinalDepth == obj.FinalDepth:
                        obj.FinalDepth.Value = fzmin
                        finish_step = obj.FinishDepth.Value if hasattr(obj, "FinishDepth") else 0.0
                        self.depthparams = PathUtils.depth_params(
                            clearance_height=obj.ClearanceHeight.Value,
                            safe_height=obj.SafeHeight.Value,
                            start_depth=obj.StartDepth.Value,
                            step_down=obj.StepDown.Value,
                            z_finish_step=finish_step,
                            final_depth=fzmin,
                            user_depths=None)
                        PathLog.info("Updated obj.FinalDepth.Value and self.depthparams to zmin: {}".format(fzmin))
                    '''

                    if obj.AdaptivePocketStart is True or obj.AdaptivePocketFinish is True:
                        pocketTup = self.calculateAdaptivePocket(obj, base, subObjTups)
                        if pocketTup is not False:
                            removalshapes.append(pocketTup)  # (shape, isHole, sub, angle, axis, strDep, finDep)
                    else:
                        strDep = obj.StartDepth.Value
                        finDep = obj.FinalDepth.Value

                        shape = Part.makeCompound(Faces)
                        env = PathUtils.getEnvelope(base[0].Shape, subshape=shape, depthparams=self.depthparams)
                        obj.removalshape = env.cut(base[0].Shape)
                        obj.removalshape.tessellate(0.1)
                        # (shape, isHole, sub, angle, axis, strDep, finDep)
                        removalshapes.append((obj.removalshape, False, '3DPocket', 0.0, 'X', strDep, finDep))
                else:
                    for sub in base[1]:
                        if "Face" in sub:
                            shape = Part.makeCompound([getattr(base[0].Shape, sub)])
                        else:
                            edges = [getattr(base[0].Shape, sub) for sub in base[1]]
                            shape = Part.makeFace(edges, 'Part::FaceMakerSimple')

                        env = PathUtils.getEnvelope(base[0].Shape, subshape=shape, depthparams=self.depthparams)
                        obj.removalshape = env.cut(base[0].Shape)
                        obj.removalshape.tessellate(0.1)

                        removalshapes.append((obj.removalshape, False))

        else:  # process the job base object as a whole
            PathLog.debug("processing the whole job base object")
            strDep = obj.StartDepth.Value
            finDep = obj.FinalDepth.Value
            # recomputeDepthparams = False
            for base in self.model:
                '''
                if obj.OpFinalDepth == obj.FinalDepth:
                    if base.Shape.BoundBox.ZMin < obj.FinalDepth.Value:
                        obj.FinalDepth.Value = base.Shape.BoundBox.ZMin
                        finDep = base.Shape.BoundBox.ZMin
                        recomputeDepthparams = True
                        PathLog.info("Updated obj.FinalDepth.Value to {}".format(finDep))
                if obj.OpStartDepth == obj.StartDepth:
                    if base.Shape.BoundBox.ZMax > obj.StartDepth.Value:
                        obj.StartDepth.Value = base.Shape.BoundBox.ZMax
                        finDep = base.Shape.BoundBox.ZMax
                        recomputeDepthparams = True
                        PathLog.info("Updated obj.StartDepth.Value to {}".format(strDep))
                if recomputeDepthparams is True:
                    finish_step = obj.FinishDepth.Value if hasattr(obj, "FinishDepth") else 0.0
                    self.depthparams = PathUtils.depth_params(
                        clearance_height=obj.ClearanceHeight.Value,
                        safe_height=obj.SafeHeight.Value,
                        start_depth=obj.StartDepth.Value,
                        step_down=obj.StepDown.Value,
                        z_finish_step=finish_step,
                        final_depth=obj.FinalDepth.Value,
                        user_depths=None)
                    recomputeDepthparams = False
                '''

                if obj.ProcessStockArea is True:
                    job = PathUtils.findParentJob(obj)

                    '''
                    finish_step = obj.FinishDepth.Value if hasattr(obj, "FinishDepth") else 0.0
                    depthparams = PathUtils.depth_params(
                        clearance_height=obj.ClearanceHeight.Value,
                        safe_height=obj.SafeHeight.Value,
                        start_depth=obj.StartDepth.Value,
                        step_down=obj.StepDown.Value,
                        z_finish_step=finish_step,
                        final_depth=base.Shape.BoundBox.ZMin,
                        user_depths=None)
                    stockEnvShape = PathUtils.getEnvelope(job.Stock.Shape, subshape=None, depthparams=depthparams)
                    '''
                    stockEnvShape = PathUtils.getEnvelope(job.Stock.Shape, subshape=None, depthparams=self.depthparams)

                    obj.removalshape = stockEnvShape.cut(base.Shape)
                    obj.removalshape.tessellate(0.1)
                else:
                    env = PathUtils.getEnvelope(base.Shape, subshape=None, depthparams=self.depthparams)
                    obj.removalshape = env.cut(base.Shape)
                    obj.removalshape.tessellate(0.1)

                removalshapes.append((obj.removalshape, False, '3DPocket', 0.0, 'X', strDep, finDep))

        return removalshapes
Exemplo n.º 44
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: {} is drillable '.format(e))
                    x = e.Curve.Center.x
                    y = e.Curve.Center.y
                    diameter = e.BoundBox.XLength
                    holelist.append((candidateEdgeName, e, x, y, diameter))
        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((candidateFaceName, f, x, y, diameter))

        PathLog.debug("holes found: {}".format(holelist))
        return holelist
Exemplo n.º 45
0
    def opSetDefaultValues(self, obj, job):
        '''opSetDefaultValues(obj) ... base implementation, do not overwrite.
        The base implementation sets the depths and heights based on the
        areaOpShapeForDepths() return value.
        Do not overwrite, overwrite areaOpSetDefaultValues(obj, job) instead.'''
        PathLog.debug("opSetDefaultValues(%s, %s)" % (obj.Label, job.Label))

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

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

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

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

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

            # Manage operation start and final depths
            if self.docRestored is True:  # This op is NOT the first in the Operations list
                PathLog.debug("Doc restored")
                obj.FinalDepth.Value = obj.OpFinalDepth.Value
                obj.StartDepth.Value = obj.OpStartDepth.Value
            else:
                PathLog.debug("New operation")
                obj.StartDepth.Value = startDepth
                obj.FinalDepth.Value = finalDepth
                obj.OpStartDepth.Value = startDepth
                obj.OpFinalDepth.Value = finalDepth

                if obj.EnableRotation != 'Off':
                    if self.initOpFinalDepth is None:
                        self.initOpFinalDepth = finalDepth
                        PathLog.debug("Saved self.initOpFinalDepth")
                    if self.initOpStartDepth is None:
                        self.initOpStartDepth = startDepth
                        PathLog.debug("Saved self.initOpStartDepth")
                    self.defValsSet = True
            PathLog.debug(
                "Default OpDepths are Start: {}, and Final: {}".format(
                    obj.OpStartDepth.Value, obj.OpFinalDepth.Value))
            PathLog.debug("Default Depths are Start: {}, and Final: {}".format(
                startDepth, finalDepth))

        self.areaOpSetDefaultValues(obj, job)
Exemplo n.º 46
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
            # rtn = True
            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)
Exemplo n.º 47
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.stockBB = PathUtils.findParentJob(obj).Stock.Shape.BoundBox  # pylint: disable=attribute-defined-outside-init
        self.useTempJobClones(
            'Delete')  # Clear temporary group and recreate for temp job clones

        # Import OpFinalDepth from pre-existing operation for recompute() scenarios
        if self.defValsSet is True:
            PathLog.debug("self.defValsSet is True.")
            if self.initOpStartDepth is not None:
                if self.initOpStartDepth != obj.OpStartDepth.Value:
                    obj.OpStartDepth.Value = self.initOpStartDepth
                    obj.StartDepth.Value = self.initOpStartDepth

            if self.initOpFinalDepth is not None:
                if self.initOpFinalDepth != obj.OpFinalDepth.Value:
                    obj.OpFinalDepth.Value = self.initOpFinalDepth
                    obj.FinalDepth.Value = self.initOpFinalDepth
            self.defValsSet = False

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

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

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

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

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

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

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

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

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

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

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

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

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

        # PathLog.debug("Pre_path depths are Start: {}, and Final: {}".format(obj.StartDepth.Value, obj.FinalDepth.Value))
        sims = []
        numShapes = len(shapes)

        # if numShapes == 1:
        #     nextAxis = shapes[0][4]
        # elif numShapes > 1:
        #     nextAxis = shapes[1][4]
        # else:
        #     nextAxis = 'L'

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

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

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

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

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

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

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

        self.useTempJobClones(
            'Delete')  # Delete temp job clone group and contents
        self.guiMessage('title', None,
                        show=True)  # Process GUI messages to user
        PathLog.debug("obj.Name: " + str(obj.Name) + "\n\n")
        return sims
Exemplo n.º 48
0
    def findHoles(self, obj, baseobject):
        '''findHoles(obj, baseobject) ... inspect baseobject and identify all features that resemble a straight cricular hole.'''
        shape = baseobject.Shape
        PathLog.track('obj: {} shape: {}'.format(obj, shape))
        holelist = []
        features = []
        # tooldiameter = obj.ToolController.Proxy.getTool(obj.ToolController).Diameter
        tooldiameter = None
        PathLog.debug('search for holes larger than tooldiameter: {}: '.format(
            tooldiameter))
        if DraftGeomUtils.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
                    })
                    features.append((baseobject, candidateEdgeName))
                    PathLog.debug("Found hole feature %s.%s" %
                                  (baseobject.Label, candidateEdgeName))
        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
                    })
                    features.append((baseobject, candidateFaceName))
                    PathLog.debug("Found hole feature %s.%s" %
                                  (baseobject.Label, candidateFaceName))

        PathLog.debug("holes found: {}".format(holelist))
        return features
Exemplo n.º 49
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)
Exemplo n.º 50
0
    def execute(self, obj):
        PathLog.track()
        if not obj.Active:
            path = Path.Path("(inactive operation)")
            obj.Path = path
            obj.ViewObject.Visibility = False
            return

        commandlist = []
        toolLoad = obj.ToolController

        self.depthparams = depth_params(
            clearance_height=obj.ClearanceHeight.Value,
            safe_height=obj.SafeHeight.Value,
            start_depth=obj.StartDepth.Value,
            step_down=obj.StepDown,
            z_finish_step=obj.FinishDepth.Value,
            final_depth=obj.FinalDepth.Value,
            user_depths=None)

        if toolLoad is None or toolLoad.ToolNumber == 0:
            FreeCAD.Console.PrintError(
                "No Tool Controller is selected. We need a tool to build a Path."
            )
            return
        else:
            self.vertFeed = toolLoad.VertFeed.Value
            self.horizFeed = toolLoad.HorizFeed.Value
            self.vertRapid = toolLoad.VertRapid.Value
            self.horizRapid = toolLoad.HorizRapid.Value
            tool = toolLoad.Proxy.getTool(toolLoad)

            if tool.Diameter == 0:
                FreeCAD.Console.PrintError(
                    "No Tool found or diameter is zero. We need a tool to build a Path."
                )
                return
            else:
                self.radius = tool.Diameter / 2

        commandlist.append(Path.Command("(" + obj.Label + ")"))

        # Facing is done either against base objects
        if obj.Base:
            PathLog.debug("obj.Base: {}".format(obj.Base))
            faces = []
            for b in obj.Base:
                for sub in b[1]:
                    shape = getattr(b[0].Shape, sub)
                    if isinstance(shape, Part.Face):
                        faces.append(shape)
                    else:
                        PathLog.debug('The base subobject is not a face')
                        return
            planeshape = Part.makeCompound(faces)
            PathLog.info("Working on a collection of faces {}".format(faces))

        # If no base object, do planing of top surface of entire model
        else:
            parentJob = PathUtils.findParentJob(obj)
            if parentJob is None:
                PathLog.debug("No base object. No parent job found")
                return
            baseobject = parentJob.Base
            if baseobject is None:
                PathLog.debug("Parent job exists but no Base Object")
                return
            planeshape = baseobject.Shape
            PathLog.info("Working on a shape {}".format(baseobject.Name))

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

        # if user wants the boundbox, calculate that
        PathLog.info("Boundary Shape: {}".format(obj.BoundaryShape))
        bb = planeshape.BoundBox
        if obj.BoundaryShape == 'Boundbox':
            bbperim = Part.makeBox(bb.XLength, bb.YLength, 1,
                                   FreeCAD.Vector(bb.XMin, bb.YMin, bb.ZMin),
                                   FreeCAD.Vector(0, 0, 1))
            env = PathUtils.getEnvelope(partshape=bbperim,
                                        depthparams=self.depthparams)
        else:
            env = PathUtils.getEnvelope(partshape=planeshape,
                                        depthparams=self.depthparams)

        try:
            commandlist.extend(self._buildPathArea(obj, env).Commands)
        except Exception as e:
            FreeCAD.Console.PrintError(e)
            FreeCAD.Console.PrintError(
                translate(
                    "Path_MillFace",
                    "The selected settings did not produce a valid path.\n"))

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

        path = Path.Path(commandlist)
        obj.Path = path
        obj.ViewObject.Visibility = True
Exemplo n.º 51
0
    def setFields(self):
        self.form.startDepth.setText(
            FreeCAD.Units.Quantity(self.obj.StartDepth.Value,
                                   FreeCAD.Units.Length).UserString)
        self.form.finalDepth.setText(
            FreeCAD.Units.Quantity(self.obj.FinalDepth.Value,
                                   FreeCAD.Units.Length).UserString)
        self.form.safeHeight.setText(
            FreeCAD.Units.Quantity(self.obj.SafeHeight.Value,
                                   FreeCAD.Units.Length).UserString)
        self.form.clearanceHeight.setText(
            FreeCAD.Units.Quantity(self.obj.ClearanceHeight.Value,
                                   FreeCAD.Units.Length).UserString)
        self.form.stepDown.setText(
            FreeCAD.Units.Quantity(self.obj.StepDown.Value,
                                   FreeCAD.Units.Length).UserString)
        self.form.extraOffset.setText(
            FreeCAD.Units.Quantity(self.obj.OffsetExtra.Value,
                                   FreeCAD.Units.Length).UserString)
        self.form.rollRadius.setText(
            FreeCAD.Units.Quantity(self.obj.RollRadius.Value,
                                   FreeCAD.Units.Length).UserString)
        self.form.useCompensation.setChecked(self.obj.UseComp)
        self.form.useStartPoint.setChecked(self.obj.UseStartPoint)
        self.form.useEndPoint.setChecked(self.obj.UseEndPoint)
        self.form.processHoles.setChecked(self.obj.processHoles)
        self.form.processPerimeter.setChecked(self.obj.processPerimeter)
        self.form.processCircles.setChecked(self.obj.processCircles)

        index = self.form.cutSide.findText(self.obj.Side,
                                           QtCore.Qt.MatchFixedString)
        if index >= 0:
            self.form.cutSide.blockSignals(True)
            self.form.cutSide.setCurrentIndex(index)
            self.form.cutSide.blockSignals(False)

        index = self.form.direction.findText(self.obj.Direction,
                                             QtCore.Qt.MatchFixedString)
        if index >= 0:
            self.form.direction.blockSignals(True)
            self.form.direction.setCurrentIndex(index)
            self.form.direction.blockSignals(False)

        controllers = PathUtils.getToolControllers(self.obj)
        labels = [c.Label for c in controllers]
        self.form.uiToolController.blockSignals(True)
        self.form.uiToolController.addItems(labels)
        self.form.uiToolController.blockSignals(False)
        if self.obj.ToolController is not None:
            index = self.form.uiToolController.findText(
                self.obj.ToolController.Label, QtCore.Qt.MatchFixedString)
            PathLog.debug("searching for TC label {}. Found Index: {}".format(
                self.obj.ToolController.Label, index))
            if index >= 0:
                self.form.uiToolController.blockSignals(True)
                self.form.uiToolController.setCurrentIndex(index)
                self.form.uiToolController.blockSignals(False)
        else:
            self.obj.ToolController = PathUtils.findToolController(self.obj)

        self.form.baseList.blockSignals(True)
        for i in self.obj.Base:
            for sub in i[1]:
                self.form.baseList.addItem(i[0].Name + "." + sub)
        self.form.baseList.blockSignals(False)

        self.form.update()
Exemplo n.º 52
0
    def Activated(self):
        PathLog.track()
        FreeCAD.ActiveDocument.openTransaction(
            translate("Path_Post", "Post Process the Selected path(s)"))
        FreeCADGui.addModule("PathScripts.PathPost")

        # Attempt to figure out what the user wants to post-process
        # If a job is selected, post that.
        # If there's only one job in a document, post it.
        # If a user has selected a subobject of a job, post the job.
        # If multiple jobs and can't guess, ask them.

        selected = FreeCADGui.Selection.getSelectionEx()
        if len(selected) > 1:
            FreeCAD.Console.PrintError("Please select a single job or other path object\n")
            return
        elif len(selected) == 1:
            sel = selected[0].Object
            if sel.Name[:3] == "Job":
                job = sel
            elif hasattr(sel, "Path"):
                try:
                    job = PathUtils.findParentJob(sel)
                except Exception: # pylint: disable=broad-except
                    job = None
            else:
                job = None
        if job is None:
            targetlist = []
            for o in FreeCAD.ActiveDocument.Objects:
                if hasattr(o, "Proxy"):
                    if isinstance(o.Proxy, PathJob.ObjectJob):
                        targetlist.append(o.Label)
            PathLog.debug("Possible post objects: {}".format(targetlist))
            if len(targetlist) > 1:
                form = FreeCADGui.PySideUic.loadUi(":/panels/DlgJobChooser.ui")
                form.cboProject.addItems(targetlist)
                r = form.exec_()
                if r is False:
                    return
                else:
                    jobname = form.cboProject.currentText()
            else:
                jobname = targetlist[0]
            job = FreeCAD.ActiveDocument.getObject(jobname)

        PathLog.debug("about to postprocess job: {}".format(job.Name))

        # Build up an ordered list of operations and tool changes.
        # Then post-the ordered list
        if hasattr(job, "Fixtures"):
            wcslist = job.Fixtures
        else:
            wcslist = ['G54']

        if hasattr(job, "OrderOutputBy"):
            orderby = job.OrderOutputBy
        else:
            orderby = "Operation"

        if hasattr(job, "SplitOutput"):
            split = job.SplitOutput
        else:
            split = False

        postlist = []

        if orderby == 'Fixture':
            PathLog.debug("Ordering by Fixture")
            # Order by fixture means all operations and tool changes will be completed in one
            # fixture before moving to the next.

            currTool = None
            for index, f in enumerate(wcslist):
                # create an object to serve as the fixture path
                fobj = _TempObject()
                c1 = Path.Command(f)
                fobj.Path = Path.Path([c1])
                if index != 0:
                    c2 = Path.Command("G0 Z" + str(job.Stock.Shape.BoundBox.ZMax + job.SetupSheet.ClearanceHeightOffset.Value))
                    fobj.Path.addCommands(c2)
                fobj.InList.append(job)
                sublist = [fobj]

                # Now generate the gcode
                for obj in job.Operations.Group:
                    tc = PathUtil.toolControllerForOp(obj)
                    if tc is not None and PathUtil.opProperty(obj, 'Active'):
                        if tc.ToolNumber != currTool:
                            sublist.append(tc)
                            PathLog.debug("Appending TC: {}".format(tc.Name))
                            currTool = tc.ToolNumber
                    sublist.append(obj)
                postlist.append(sublist)

        elif orderby == 'Tool':
            PathLog.debug("Ordering by Tool")
            # Order by tool means tool changes are minimized.
            # all operations with the current tool are processed in the current
            # fixture before moving to the next fixture.

            currTool = None
            fixturelist = []
            for f in wcslist:
                # create an object to serve as the fixture path
                fobj = _TempObject()
                c1 = Path.Command(f)
                c2 = Path.Command("G0 Z" + str(job.Stock.Shape.BoundBox.ZMax + job.SetupSheet.ClearanceHeightOffset.Value))
                fobj.Path = Path.Path([c1, c2])
                fobj.InList.append(job)
                fixturelist.append(fobj)

            # Now generate the gcode
            curlist = []  # list of ops for tool, will repeat for each fixture
            sublist = []  # list of ops for output splitting

            for idx, obj in enumerate(job.Operations.Group):

                # check if the operation is active
                active =  PathUtil.opProperty(obj, 'Active')

                tc = PathUtil.toolControllerForOp(obj)
                if tc is None or tc.ToolNumber == currTool and active:
                    curlist.append(obj)
                elif tc.ToolNumber != currTool and currTool is None and active:  # first TC
                    sublist.append(tc)
                    curlist.append(obj)
                    currTool = tc.ToolNumber
                elif tc.ToolNumber != currTool and currTool is not None and active:  # TC
                    for fixture in fixturelist:
                        sublist.append(fixture)
                        sublist.extend(curlist)
                    postlist.append(sublist)
                    sublist = [tc]
                    curlist = [obj]
                    currTool = tc.ToolNumber

                if idx == len(job.Operations.Group) - 1:  # Last operation.
                    for fixture in fixturelist:
                        sublist.append(fixture)
                        sublist.extend(curlist)
                    postlist.append(sublist)

        elif orderby == 'Operation':
            PathLog.debug("Ordering by Operation")
            # Order by operation means ops are done in each fixture in
            # sequence.
            currTool = None
            firstFixture = True

            # Now generate the gcode
            for obj in job.Operations.Group:
                if PathUtil.opProperty(obj, 'Active'):
                    sublist = []
                    PathLog.debug("obj: {}".format(obj.Name))
                    for f in wcslist:
                        fobj = _TempObject()
                        c1 = Path.Command(f)
                        fobj.Path = Path.Path([c1])
                        if not firstFixture:
                            c2 = Path.Command("G0 Z" + str(job.Stock.Shape.BoundBox.ZMax + job.SetupSheet.ClearanceHeightOffset.Value))
                            fobj.Path.addCommands(c2)
                        fobj.InList.append(job)
                        sublist.append(fobj)
                        firstFixture = False
                        tc = PathUtil.toolControllerForOp(obj)
                        if tc is not None:
                            if tc.ToolNumber != currTool:
                                sublist.append(tc)
                                currTool = tc.ToolNumber
                        sublist.append(obj)
                    postlist.append(sublist)

        fail = True
        rc = '' # pylint: disable=unused-variable
        if split:
            for slist in postlist:
                (fail, rc, filename) = self.exportObjectsWith(slist, job)
        else:
            finalpostlist = [item for slist in postlist for item in slist]
            (fail, rc, filename) = self.exportObjectsWith(finalpostlist, job)

        self.subpart = 1

        if fail:
            FreeCAD.ActiveDocument.abortTransaction()
        else:
            if hasattr(job, "LastPostProcessDate"):
                job.LastPostProcessDate = str(datetime.now())
            if hasattr(job, "LastPostProcessOutput"):
                job.LastPostProcessOutput = filename
            FreeCAD.ActiveDocument.commitTransaction()

        FreeCAD.ActiveDocument.recompute()
Exemplo n.º 53
0
def isDrillable(obj, candidate, tooldiameter=None, includePartials=False):
    """
    Checks candidates to see if they can be drilled.
    Candidates can be either faces - circular or cylindrical or circular edges.
    The tooldiameter can be optionally passed.  if passed, the check will return
    False for any holes smaller than the tooldiameter.
    obj=Shape
    candidate = Face or Edge
    tooldiameter=float
    """
    PathLog.track('obj: {} candidate: {} tooldiameter {}'.format(obj, candidate, tooldiameter))
    if list == type(obj):
        for shape in obj:
            if isDrillable(shape, candidate, tooldiameter, includePartials):
                return (True, shape)
        return (False, None)

    drillable = False
    try:
        if candidate.ShapeType == 'Face':
            face = candidate
            # eliminate flat faces
            if (round(face.ParameterRange[0], 8) == 0.0) and (round(face.ParameterRange[1], 8) == round(math.pi * 2, 8)):
                for edge in face.Edges:  # Find seam edge and check if aligned to Z axis.
                    if (isinstance(edge.Curve, Part.Line)):
                        PathLog.debug("candidate is a circle")
                        v0 = edge.Vertexes[0].Point
                        v1 = edge.Vertexes[1].Point
                        #check if the cylinder seam is vertically aligned.  Eliminate tilted holes
                        if (numpy.isclose(v1.sub(v0).x, 0, rtol=1e-05, atol=1e-06)) and \
                                (numpy.isclose(v1.sub(v0).y, 0, rtol=1e-05, atol=1e-06)):
                            drillable = True
                            # vector of top center
                            lsp = Vector(face.BoundBox.Center.x, face.BoundBox.Center.y, face.BoundBox.ZMax)
                            # vector of bottom center
                            lep = Vector(face.BoundBox.Center.x, face.BoundBox.Center.y, face.BoundBox.ZMin)
                            # check if the cylindrical 'lids' are inside the base
                            # object.  This eliminates extruded circles but allows
                            # actual holes.
                            if obj.isInside(lsp, 1e-6, False) or obj.isInside(lep, 1e-6, False):
                                PathLog.track("inside check failed. lsp: {}  lep: {}".format(lsp,lep))
                                drillable = False
                            # eliminate elliptical holes
                            elif not hasattr(face.Surface, "Radius"):
                                PathLog.debug("candidate face has no radius attribute")
                                drillable = False
                            else:
                                if tooldiameter is not None:
                                    drillable = face.Surface.Radius >= tooldiameter/2
                                else:
                                    drillable = True
            elif type(face.Surface) == Part.Plane and PathGeom.pointsCoincide(face.Surface.Axis, FreeCAD.Vector(0,0,1)):
                if len(face.Edges) == 1 and type(face.Edges[0].Curve) == Part.Circle:
                    center = face.Edges[0].Curve.Center
                    if obj.isInside(center, 1e-6, False):
                        if tooldiameter is not None:
                            drillable = face.Edges[0].Curve.Radius >= tooldiameter/2
                        else:
                            drillable = True
        else:
            for edge in candidate.Edges:
                if isinstance(edge.Curve, Part.Circle) and (includePartials or edge.isClosed()):
                    PathLog.debug("candidate is a circle or ellipse")
                    if not hasattr(edge.Curve, "Radius"):
                        PathLog.debug("No radius.  Ellipse.")
                        drillable = False
                    else:
                        PathLog.debug("Has Radius, Circle")
                        if tooldiameter is not None:
                            drillable = edge.Curve.Radius >= tooldiameter/2
                            if not drillable:
                                FreeCAD.Console.PrintMessage(
                                        "Found a drillable hole with diameter: {}: "
                                        "too small for the current tool with "
                                        "diameter: {}".format(edge.Curve.Radius*2, tooldiameter))
                        else:
                            drillable = True
        PathLog.debug("candidate is drillable: {}".format(drillable))
    except Exception as ex:
        PathLog.warning(translate("PathUtils", "Issue determine drillability: {}").format(ex))
    return drillable
Exemplo n.º 54
0
        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.info(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
Exemplo n.º 55
0
def cmdsForEdge(edge, flip = False, useHelixForBSpline = True, segm = 50):
    """(edge, flip=False, useHelixForBSpline=True, segm=50) -> List(Path.Command)
    Returns a list of Path.Command representing the given edge.
    If flip is True the edge is considered to be backwards.
    If useHelixForBSpline is True an Edge based on a BSplineCurve is considered
    to represent a helix and results in G2 or G3 command. Otherwise edge has
    no direct Path.Command mapping and will be approximated by straight segments.
    segm is a factor for the segmentation of arbitrary curves not mapped to G1/2/3
    commands. The higher the value the more segments will be used."""
    pt = edge.valueAt(edge.LastParameter) if not flip else edge.valueAt(edge.FirstParameter)
    params = {'X': pt.x, 'Y': pt.y, 'Z': pt.z}
    if type(edge.Curve) == Part.Line or type(edge.Curve) == Part.LineSegment:
        commands =  [Path.Command('G1', params)]
    else:
        p1 = edge.valueAt(edge.FirstParameter) if not flip else edge.valueAt(edge.LastParameter)
        p2 = edge.valueAt((edge.FirstParameter + edge.LastParameter)/2)
        p3 = pt

        if (type(edge.Curve) == Part.Circle and isRoughly(edge.Curve.Axis.x, 0) and isRoughly(edge.Curve.Axis.y, 0)) or (useHelixForBSpline and type(edge.Curve) == Part.BSplineCurve):
            # This is an arc or a helix and it should be represented by a simple G2/G3 command
            if edge.Curve.Axis.z < 0:
                cmd = 'G2' if not flip else 'G3'
            else:
                cmd = 'G3' if not flip else 'G2'

            if pointsCoincide(p1, p3):
                # A full circle
                offset = edge.Curve.Center - pt
            else:
                pd = Part.Circle(xy(p1), xy(p2), xy(p3)).Center
                PathLog.debug("**** %s.%d: (%.2f, %.2f, %.2f) - (%.2f, %.2f, %.2f) - (%.2f, %.2f, %.2f) -> center=(%.2f, %.2f)" % (cmd, flip, p1.x, p1.y, p1.z, p2.x, p2.y, p2.z, p3.x, p3.y, p3.z, pd.x, pd.y))

                # Have to calculate the center in the XY plane, using pd leads to an error if this is a helix
                pa = xy(p1)
                pb = xy(p2)
                pc = xy(p3)
                offset = Part.Circle(pa, pb, pc).Center - pa

                PathLog.debug("**** (%.2f, %.2f, %.2f) - (%.2f, %.2f, %.2f)" % (pa.x, pa.y, pa.z, pc.x, pc.y, pc.z))
                PathLog.debug("**** (%.2f, %.2f, %.2f) - (%.2f, %.2f, %.2f)" % (pb.x, pb.y, pb.z, pd.x, pd.y, pd.z))
            PathLog.debug("**** (%.2f, %.2f, %.2f)" % (offset.x, offset.y, offset.z))

            params.update({'I': offset.x, 'J': offset.y, 'K': (p3.z - p1.z)/2})
            commands = [ Path.Command(cmd, params) ]

        else:
            # We're dealing with a helix or a more complex shape and it has to get approximated
            # by a number of straight segments
            eStraight = Part.Edge(Part.LineSegment(p1, p3))
            esP2 = eStraight.valueAt((eStraight.FirstParameter + eStraight.LastParameter)/2)
            deviation = (p2 - esP2).Length
            if isRoughly(deviation, 0):
                return [ Path.Command('G1', {'X': p3.x, 'Y': p3.y, 'Z': p3.z}) ]
            # at this point pixellation is all we can do
            commands = []
            segments = int(math.ceil((deviation / eStraight.Length) * segm))
            #print("**** pixellation with %d segments" % segments)
            dParameter = (edge.LastParameter - edge.FirstParameter) / segments
            for i in range(0, segments):
                if flip:
                    p = edge.valueAt(edge.LastParameter - (i + 1) * dParameter)
                else:
                    p = edge.valueAt(edge.FirstParameter + (i + 1) * dParameter)
                cmd = Path.Command('G1', {'X': p.x, 'Y': p.y, 'Z': p.z})
                #print("***** %s" % cmd)
                commands.append(cmd)
    #print commands
    return commands
Exemplo n.º 56
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 and len(
                        self.commandlist) > 1:
                self.endVector[2] = obj.ClearanceHeight.Value
                self.commandlist.append(
                    Path.Command('G0', {
                        'Z': obj.ClearanceHeight.Value,
                        'F': self.vertRapid
                    }))

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

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

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

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

        self.useTempJobClones(
            'Delete')  # Delete temp job clone group and contents
        self.guiMessage('title', None,
                        show=True)  # Process GUI messages to user
        for ton in self.tempObjectNames:  # remove temporary objects by name
            FreeCAD.ActiveDocument.removeObject(ton)
        PathLog.debug("obj.Name: " + str(obj.Name) + "\n\n")
        return sims
Exemplo n.º 57
0
def edgeForCmd(cmd, startPoint):
    """(cmd, startPoint).
    Returns an Edge representing the given command, assuming a given startPoint."""

    endPoint = commandEndPoint(cmd, startPoint)
    if (cmd.Name in CmdMoveStraight) or (cmd.Name in CmdMoveRapid):
        if pointsCoincide(startPoint, endPoint):
            return None
        return Part.Edge(Part.LineSegment(startPoint, endPoint))

    if cmd.Name in CmdMoveArc:
        center = startPoint + commandEndPoint(cmd, Vector(0,0,0), 'I', 'J', 'K')
        A = xy(startPoint - center)
        B = xy(endPoint - center)
        d = -B.x * A.y + B.y * A.x

        if isRoughly(d, 0, 0.005):
            PathLog.debug("Half circle arc at: (%.2f, %.2f, %.2f)" % (center.x, center.y, center.z))
            # we're dealing with half a circle here
            angle = getAngle(A) + math.pi/2
            if cmd.Name in CmdMoveCW:
                angle -= math.pi
        else:
            C = A + B
            angle = getAngle(C)
            PathLog.debug("Arc (%8f) at: (%.2f, %.2f, %.2f) -> angle=%f" % (d, center.x, center.y, center.z, angle / math.pi))

        R = A.Length
        PathLog.debug("arc: p1=(%.2f, %.2f) p2=(%.2f, %.2f) -> center=(%.2f, %.2f)" % (startPoint.x, startPoint.y, endPoint.x, endPoint.y, center.x, center.y))
        PathLog.debug("arc: A=(%.2f, %.2f) B=(%.2f, %.2f) -> d=%.2f" % (A.x, A.y, B.x, B.y, d))
        PathLog.debug("arc: R=%.2f angle=%.2f" % (R, angle/math.pi))
        if isRoughly(startPoint.z, endPoint.z):
            midPoint = center + Vector(math.cos(angle), math.sin(angle), 0) * R
            PathLog.debug("arc: (%.2f, %.2f) -> (%.2f, %.2f) -> (%.2f, %.2f)" % (startPoint.x, startPoint.y, midPoint.x, midPoint.y, endPoint.x, endPoint.y))
            return Part.Edge(Part.Arc(startPoint, midPoint, endPoint))

        # It's a Helix
        #print('angle: A=%.2f B=%.2f' % (getAngle(A)/math.pi, getAngle(B)/math.pi))
        if cmd.Name in CmdMoveCW:
            cw = True
        else:
            cw = False
        angle = diffAngle(getAngle(A), getAngle(B), 'CW' if cw else 'CCW')
        height = endPoint.z - startPoint.z
        pitch = height * math.fabs(2 * math.pi / angle)
        if angle > 0:
            cw = not cw
        #print("Helix: R=%.2f h=%.2f angle=%.2f pitch=%.2f" % (R, height, angle/math.pi, pitch))
        helix = Part.makeHelix(pitch, height, R, 0, not cw)
        helix.rotate(Vector(), Vector(0,0,1), 180 * getAngle(A) / math.pi)
        e = helix.Edges[0]
        helix.translate(startPoint - e.valueAt(e.FirstParameter))
        return helix.Edges[0]
    return None
Exemplo n.º 58
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 = []
        finalDepths = []

        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.info(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.info(
                            "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.info("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
                                        break
                            if rtn is False:
                                if obj.AttemptInverseAngle is True and obj.InverseAngle is False:
                                    (clnBase, clnStock,
                                     angle) = self.applyInverseAngle(
                                         obj, clnBase, clnStock, axis, angle)
                                else:
                                    PathLog.info(
                                        translate(
                                            "Path",
                                            "Consider toggling the InverseAngle property and recomputing the operation."
                                        ))

                            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

                                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,
                                     praInfo) = self.faceRotationAnalysis(
                                         obj, norm, surf)  # pylint: disable=unused-variable
                                    if rtn is True:
                                        PathLog.debug(
                                            "Face not aligned after initial rotation."
                                        )
                                        if obj.AttemptInverseAngle is True and obj.InverseAngle is False:
                                            (clnBase, clnStock,
                                             angle) = self.applyInverseAngle(
                                                 obj, clnBase, clnStock, axis,
                                                 angle)
                                        else:
                                            PathLog.info(
                                                translate(
                                                    "Path",
                                                    "Consider toggling the InverseAngle property and recomputing the operation."
                                                ))
                                    else:
                                        PathLog.debug(
                                            "Face appears to be oriented correctly."
                                        )

                                    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.info(
                                    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.error(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:
                    finDep = max(obj.FinalDepth.Value, f.BoundBox.ZMin)
                    f.translate(FreeCAD.Vector(0, 0, finDep - 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)
                    if obj.UseOutline:
                        wire = TechDraw.findShapeOutline(
                            shape, 1, FreeCAD.Vector(0, 0, 1))
                        wire.translate(
                            FreeCAD.Vector(
                                0, 0,
                                obj.FinalDepth.Value - wire.BoundBox.ZMin))
                        self.horizontal.append(Part.Face(wire))
                    else:
                        self.horizontal.append(shape)

                for face in self.horizontal:
                    # extrude all faces up to StartDepth and those are the removal shapes
                    (strDep, finDep) = self.calculateStartFinalDepths(
                        obj, face, stock)
                    finalDepths.append(finDep)
                    extent = FreeCAD.Vector(0, 0, strDep - finDep)
                    self.removalshapes.append(
                        (face.removeSplitter().extrude(extent), False,
                         'pathPocketShape', angle, axis, strDep, finDep))
                    PathLog.debug(
                        "Extent depths are str: {}, and fin: {}".format(
                            strDep, finDep))
                # Efor face

            # Adjust obj.FinalDepth.Value as needed.
            if len(finalDepths) > 0:
                finalDep = min(finalDepths)
                if subCount == 1:
                    obj.FinalDepth.Value = finalDep
        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
Exemplo n.º 59
0
    def getLeadEnd(self, obj, queue, action):
        '''returns the Gcode of LeadOut.'''
         # pylint: disable=unused-argument
        results = []
        horizFeed = PathDressup.toolController(obj.Base).HorizFeed.Value
        R = obj.Length.Value  # Radius of roll or length
        arcs_identical = False

        # Set the correct twist command
        if self.getDirectionOfPath(obj) == 'right':
            arcdir = "G2"
        else:
            arcdir = "G3"

        if queue[1].Name == "G1":  # line
            p0 = queue[0].Placement.Base
            p1 = queue[1].Placement.Base
            v = self.normalize(p1.sub(p0))
        else:  # dealing with a circle
            p0 = queue[0].Placement.Base
            p1 = queue[1].Placement.Base
            v = self.normalize(p1.sub(p0))

        if self.getDirectionOfPath(obj) == 'right':
            off_v = FreeCAD.Vector(v.y*R, -v.x*R, 0.0)
        else:
            off_v = FreeCAD.Vector(-v.y*R, v.x*R, 0.0)

        # Check if we leave at line or arc command
        if queue[1].Name in movecommands and queue[1].Name not in arccommands:
            # We have a line move
            vec = p1.sub(p0)
            vec_n = self.normalize(vec)
            vec_inv = self.invert(vec_n)
            vec_off = self.multiply(vec_inv,  obj.ExtendLeadOut)
            #PathLog.debug("LineCMD: {}, Vxinv: {}, Vyinv: {}, Vxoff: {}, Vyoff: {}".format(queue[0].Name, vec_inv.x, vec_inv.y,  vec_off.x,  vec_off.y))
        else:
            # We have an arc move
            pij = copy.deepcopy(p0)
            pij.x += queue[1].Parameters['I']
            pij.y += queue[1].Parameters['J']
            ve = pij.sub(p1)

            if arcdir == queue[1].Name:
                arcs_identical = True

            if arcdir == "G2":
                vec_rot = self.rotate(ve,  -90)
            else:
                vec_rot = self.rotate(ve,  90)

            vec_n = self.normalize(vec_rot)
            v = vec_n

            if arcdir == "G3":
                off_v = FreeCAD.Vector(-v.y*R, v.x*R, 0.0)
            else:
                off_v = FreeCAD.Vector(v.y*R, -v.x*R, 0.0)

            vec_inv = self.invert(vec_rot)

            vec_off = self.multiply(vec_inv,  obj.ExtendLeadOut)

        offsetvector = FreeCAD.Vector(v.x*R-vec_off.x, v.y*R-vec_off.y, 0.0)
        if obj.RadiusCenter == 'Radius':
            leadend = (p1.add(off_v)).add(offsetvector)  # Rmode
            if arcs_identical:
                t = p1.sub(leadend)
                t = p1.add(t)
                leadend = t
                off_v = self.multiply(off_v,   -1)
        else:
            leadend = p1.add(off_v)  # Dmode

        IJ = off_v  # .negative()
        #results.append(queue[1])
        if obj.StyleOff == 'Arc':
            if obj.ExtendLeadOut != 0:
                extendcommand = Path.Command('G1', {"X": p1.x-vec_off.x, "Y": p1.y-vec_off.y, "F": horizFeed})
                results.append(extendcommand)
            arcmove = Path.Command(arcdir, {"X": leadend.x, "Y": leadend.y, "I": IJ.x, "J": IJ.y, "F": horizFeed})  # add G2/G3 move
            results.append(arcmove)
        elif obj.StyleOff == 'Tangent':
            extendcommand = Path.Command('G1', {"X": leadend.x, "Y": leadend.y, "F": horizFeed})
            results.append(extendcommand)
        else:
            PathLog.debug(" CURRENT_IN Perp")

        if obj.UseMachineCRC:  # crc off
            results.append(Path.Command('G40', {}))

        return results
Exemplo n.º 60
0
    def areaOpShapes(self, obj):
        '''areaOpShapes(obj) ... return top face'''
        # Facing is done either against base objects
        holeShape = None

        PathLog.debug('depthparams: {}'.format([i for i in self.depthparams]))

        if obj.Base:
            PathLog.debug("obj.Base: {}".format(obj.Base))
            self.removalshapes = []
            faces = []
            holes = []
            holeEnvs = []
            oneBase = [obj.Base[0][0], True]
            sub0 = getattr(obj.Base[0][0].Shape, obj.Base[0][1][0])
            minHeight = sub0.BoundBox.ZMax

            for b in obj.Base:
                for sub in b[1]:
                    shape = getattr(b[0].Shape, sub)
                    if isinstance(shape, Part.Face):
                        faces.append(shape)
                        if shape.BoundBox.ZMin < minHeight:
                            minHeight = shape.BoundBox.ZMin
                        # Limit to one model base per operation
                        if oneBase[0] is not b[0]:
                            oneBase[1] = False
                        if numpy.isclose(abs(shape.normalAt(0, 0).z),
                                         1):  # horizontal face
                            # Analyze internal closed wires to determine if raised or a recess
                            for wire in shape.Wires[1:]:
                                if obj.ExcludeRaisedAreas:
                                    ip = self.isPocket(b[0], shape, wire)
                                    if ip is False:
                                        holes.append((b[0].Shape, wire))
                                else:
                                    holes.append((b[0].Shape, wire))
                    else:
                        PathLog.warning(
                            'The base subobject, "{0}," is not a face. Ignoring "{0}."'
                            .format(sub))

            if obj.ExcludeRaisedAreas and len(holes) > 0:
                for shape, wire in holes:
                    f = Part.makeFace(wire, 'Part::FaceMakerSimple')
                    env = PathUtils.getEnvelope(shape,
                                                subshape=f,
                                                depthparams=self.depthparams)
                    holeEnvs.append(env)
                    holeShape = Part.makeCompound(holeEnvs)

            PathLog.debug("Working on a collection of faces {}".format(faces))
            planeshape = Part.makeCompound(faces)

        # If no base object, do planing of top surface of entire model
        else:
            planeshape = Part.makeCompound([base.Shape for base in self.model])
            PathLog.debug("Working on a shape {}".format(obj.Label))

        # Find the correct shape depending on Boundary shape.
        PathLog.debug("Boundary Shape: {}".format(obj.BoundaryShape))
        bb = planeshape.BoundBox

        # Apply offset for clearing edges
        offset = 0
        if obj.ClearEdges:
            offset = self.radius + 0.1

        bb.XMin = bb.XMin - offset
        bb.YMin = bb.YMin - offset
        bb.XMax = bb.XMax + offset
        bb.YMax = bb.YMax + offset

        if obj.BoundaryShape == 'Boundbox':
            bbperim = Part.makeBox(bb.XLength, bb.YLength, 1,
                                   FreeCAD.Vector(bb.XMin, bb.YMin, bb.ZMin),
                                   FreeCAD.Vector(0, 0, 1))
            env = PathUtils.getEnvelope(partshape=bbperim,
                                        depthparams=self.depthparams)
            if obj.ExcludeRaisedAreas and oneBase[1]:
                includedFaces = self.getAllIncludedFaces(oneBase[0],
                                                         env,
                                                         faceZ=minHeight)
                if len(includedFaces) > 0:
                    includedShape = Part.makeCompound(includedFaces)
                    includedEnv = PathUtils.getEnvelope(
                        oneBase[0].Shape,
                        subshape=includedShape,
                        depthparams=self.depthparams)
                    env = env.cut(includedEnv)
        elif obj.BoundaryShape == 'Stock':
            stock = PathUtils.findParentJob(obj).Stock.Shape
            env = stock

            if obj.ExcludeRaisedAreas and oneBase[1]:
                includedFaces = self.getAllIncludedFaces(oneBase[0],
                                                         stock,
                                                         faceZ=minHeight)
                if len(includedFaces) > 0:
                    stockEnv = PathUtils.getEnvelope(
                        partshape=stock, depthparams=self.depthparams)
                    includedShape = Part.makeCompound(includedFaces)
                    includedEnv = PathUtils.getEnvelope(
                        oneBase[0].Shape,
                        subshape=includedShape,
                        depthparams=self.depthparams)
                    env = stockEnv.cut(includedEnv)
        elif obj.BoundaryShape == 'Perimeter':
            if obj.ClearEdges:
                psZMin = planeshape.BoundBox.ZMin
                ofstShape = PathUtils.getOffsetArea(planeshape,
                                                    self.radius * 1.25,
                                                    plane=planeshape)
                ofstShape.translate(
                    FreeCAD.Vector(0.0, 0.0, psZMin - ofstShape.BoundBox.ZMin))
                env = PathUtils.getEnvelope(partshape=ofstShape,
                                            depthparams=self.depthparams)
            else:
                env = PathUtils.getEnvelope(partshape=planeshape,
                                            depthparams=self.depthparams)
        elif obj.BoundaryShape == 'Face Region':
            baseShape = oneBase[0].Shape
            psZMin = planeshape.BoundBox.ZMin
            ofst = 0.0
            if obj.ClearEdges:
                ofst = self.tool.Diameter * 0.51
            ofstShape = PathUtils.getOffsetArea(planeshape,
                                                ofst,
                                                plane=planeshape)
            ofstShape.translate(
                FreeCAD.Vector(0.0, 0.0, psZMin - ofstShape.BoundBox.ZMin))

            # Calculate custom depth params for removal shape envelope, with start and final depth buffers
            custDepthparams = self._customDepthParams(
                obj, obj.StartDepth.Value + 0.2,
                obj.FinalDepth.Value - 0.1)  # only an envelope
            ofstShapeEnv = PathUtils.getEnvelope(partshape=ofstShape,
                                                 depthparams=custDepthparams)
            if obj.ExcludeRaisedAreas:
                env = ofstShapeEnv.cut(baseShape)
                env.translate(FreeCAD.Vector(
                    0.0, 0.0,
                    -0.00001))  # lower removal shape into buffer zone
            else:
                env = ofstShapeEnv

        if holeShape:
            PathLog.debug("Processing holes and face ...")
            holeEnv = PathUtils.getEnvelope(partshape=holeShape,
                                            depthparams=self.depthparams)
            newEnv = env.cut(holeEnv)
            tup = newEnv, False, 'pathMillFace'
        else:
            PathLog.debug("Processing solid face ...")
            tup = env, False, 'pathMillFace'

        self.removalshapes.append(tup)
        obj.removalshape = self.removalshapes[0][0]  # save removal shape

        return self.removalshapes