def addBaseGeometry(self, selection):
        added = False
        shapes = self.obj.BaseShapes
        for sel in selection:
            job = PathUtils.findParentJob(self.obj)
            base = job.Proxy.resourceClone(job, sel.Object)
            if not base:
                PathLog.notice((translate("Path", "%s is not a Base Model object of the job %s")+"\n") % (sel.Object.Label, job.Label))
                continue
            if base in shapes:
                PathLog.notice((translate("Path", "Base shape %s already in the list")+"\n") % (sel.Object.Label))
                continue
            if base.isDerivedFrom('Part::Part2DObject'):
                if sel.HasSubObjects:
                    # selectively add some elements of the drawing to the Base
                    for sub in sel.SubElementNames:
                        if 'Vertex' in sub:
                            PathLog.info(translate("Path", "Ignoring vertex"))
                        else:
                            self.obj.Proxy.addBase(self.obj, base, sub)
                else:
                    # when adding an entire shape to BaseShapes we can take its sub shapes out of Base
                    self.obj.Base = [(p,el) for p,el in self.obj.Base if p != base]
                    shapes.append(base)
                    self.obj.BaseShapes = shapes
                added = True
            else:
                # user wants us to engrave an edge of face of a base model
                base = self.super().addBaseGeometry(selection)
                added = added or base

        return added
 def addNewTagAt(self, point, obj):
     if point and obj and self.obj.Proxy.pointIsOnPath(self.obj, point):
         PathLog.info("addNewTagAt(%.2f, %.2f)" % (point.x, point.y))
         self.Positions.append(FreeCAD.Vector(point.x, point.y, 0))
         self.updateTagsView()
     else:
         print("ignore new tag at %s (obj=%s, on-path=%d" % (point, obj, 0))
Example #3
0
    def onDocumentRestored(self, obj):
        features = self.opFeatures(obj)
        if FeatureBaseGeometry & features and 'App::PropertyLinkSubList' == obj.getTypeIdOfProperty('Base'):
            PathLog.info("Replacing link property with global link (%s)." % obj.State)
            base = obj.Base
            obj.removeProperty('Base')
            self.addBaseProperty(obj)
            obj.Base = base
            obj.touch()
            obj.Document.recompute()

        if FeatureTool & features and not hasattr(obj, 'OpToolDiameter'):
            self.addOpValues(obj, ['tooldia'])

        if FeatureStepDown & features and not hasattr(obj, 'OpStartDepth'):
            if PathGeom.isRoughly(obj.StepDown.Value, 1):
                obj.setExpression('StepDown', 'OpToolDiameter')

        if FeatureDepths & features and not hasattr(obj, 'OpStartDepth'):
            self.addOpValues(obj, ['start', 'final'])
            if not hasattr(obj, 'StartDepthLock') or not obj.StartDepthLock:
                obj.setExpression('StartDepth', 'OpStartDepth')
            if FeatureNoFinalDepth & features:
                obj.setEditorMode('OpFinalDepth', 2)
            elif not hasattr(obj, 'FinalDepthLock') or not obj.FinalDepthLock:
                obj.setExpression('FinalDepth', 'OpFinalDepth')
    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)
Example #5
0
def isVertical(obj):
    '''isVertical(obj) ... answer True if obj points into Z'''
    if type(obj) == FreeCAD.Vector:
        return isRoughly(obj.x, 0) and isRoughly(obj.y, 0)

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

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

    PathLog.error(translate('PathGeom', "isVertical(%s) not supported") % obj)
    return None
    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
    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
    def setup(self, obj, initial):
        PathLog.info("Here we go ... ")
        if initial:
            if hasattr(obj.Base, "BoneBlacklist"):
                # dressing up a bone dressup
                obj.Side = obj.Base.Side
            else:
                # otherwise dogbones are opposite of the base path's side
                side = Side.Right
                if hasattr(obj.Base, 'Side') and obj.Base.Side == 'Inside':
                    side = Side.Left
                if hasattr(obj.Base, 'Directin') and obj.Base.Direction == 'CCW':
                    side = Side.oppositeOf(side)
                obj.Side = side

        self.toolRadius = 5
        tc = PathDressup.toolController(obj.Base)
        if tc is None or tc.ToolNumber == 0:
            self.toolRadius = 5
        else:
            tool = tc.Proxy.getTool(tc)  # PathUtils.getTool(obj, tc.ToolNumber)
            if not tool or tool.Diameter == 0:
                self.toolRadius = 5
            else:
                self.toolRadius = tool.Diameter / 2

        self.shapes = {}
        self.dbg = []
Example #9
0
    def setup(self, obj):
        PathLog.info("Here we go ... ")
        if hasattr(obj.Base, "BoneBlacklist"):
            # dressing up a bone dressup
            obj.Side = obj.Base.Side
        else:
            # otherwise dogbones are opposite of the base path's side
            if obj.Base.Side == Side.Left:
                obj.Side = Side.Right
            elif obj.Base.Side == Side.Right:
                obj.Side = Side.Left
            else:
                # This will cause an error, which is fine for now 'cause I don't know what to do here
                obj.Side = 'On'

        self.toolRadius = 5
        toolLoad = obj.ToolController
        if toolLoad is None or toolLoad.ToolNumber == 0:
            self.toolRadius = 5
        else:
            tool = toolLoad.Proxy.getTool(toolLoad) #PathUtils.getTool(obj, toolLoad.ToolNumber)
            if not tool or tool.Diameter == 0:
                self.toolRadius = 5
            else:
                self.toolRadius = tool.Diameter / 2

        self.shapes = {}
        self.dbg = []
Example #10
0
 def editObject(self, obj):
     if obj:
         if obj == self.obj.Base:
             return self.openTaskPanel('Base')
         if obj == self.obj.Stock:
             return self.openTaskPanel('Stock')
         PathLog.info("Expected a specific object to edit - %s not recognized" % obj.Label)
     return self.openTaskPanel()
Example #11
0
    def exec_(self):
        restoreTC   = self.obj.Proxy.templateAttrs(self.obj)

        rc = False
        if not self.editor.form.exec_():
            PathLog.info("revert")
            self.obj.Proxy.setFromTemplate(self.obj, restoreTC)
            rc = True
        return rc
 def setOperationProperties(self, obj, opName):
     PathLog.track(obj.Label, opName)
     try:
         op = _RegisteredOps[opName]
         for prop in op.properties():
             propName = OpPropertyName(opName, prop)
             if hasattr(self.obj, propName):
                 setattr(obj, prop, getattr(self.obj, propName))
     except Exception as exc:
         PathLog.info("SetupSheet has no support for {}".format(opName))
Example #13
0
 def setupTaskPanel(self, panel):
     '''setupTaskPanel(panel) ... internal function to start the editor.'''
     self.panel = panel
     FreeCADGui.Control.closeDialog()
     FreeCADGui.Control.showDialog(panel)
     panel.setupUi()
     job = self.Object.Proxy.getJob(self.Object)
     if job:
         job.ViewObject.Proxy.setupEditVisibility(job)
     else:
         PathLog.info("did not find no job")
Example #14
0
    def edgeForCmd(cls, cmd, startPoint):
        """(cmd, startPoint).
        Returns an Edge representing the given command, assuming a given startPoint."""

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

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

            if cls.isRoughly(d, 0, 0.005):
                PathLog.info("Half circle arc at: (%.2f, %.2f, %.2f)" % (center.x, center.y, center.z))
                # we're dealing with half a circle here
                angle = cls.getAngle(A) + math.pi/2
                if cmd.Name in cls.CmdMoveCW:
                    angle -= math.pi
            else:
                C = A + B
                angle = cls.getAngle(C)
                PathLog.info("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 cls.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' % (cls.getAngle(A)/math.pi, cls.getAngle(B)/math.pi))
            if cmd.Name in cls.CmdMoveCW:
                cw = True
            else:
                cw = False
            angle = cls.diffAngle(cls.getAngle(A), cls.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 * cls.getAngle(A) / math.pi)
            e = helix.Edges[0]
            helix.translate(startPoint - e.valueAt(e.FirstParameter))
            return helix.Edges[0]
        return None
Example #15
0
 def reject(self, resetEdit=True):
     PathLog.track()
     self.preCleanup()
     FreeCAD.ActiveDocument.abortTransaction()
     if self.deleteOnReject:
         PathLog.info("Uncreate Job")
         FreeCAD.ActiveDocument.openTransaction(translate("Path_Job", "Uncreate Job"))
         FreeCAD.ActiveDocument.removeObject(self.obj.Name)
         FreeCAD.ActiveDocument.commitTransaction()
     self.cleanup(resetEdit)
     return True
Example #16
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(-)")
 def sortedTags(self, tags):
     ordered = []
     for edge in self.bottomEdges:
         ts = [t for t in tags if PathGeom.isRoughly(0, Part.Vertex(t.originAt(self.minZ)).distToShape(edge)[0], 0.1)]
         for t in sorted(ts, key=lambda t: (t.originAt(self.minZ) - edge.valueAt(edge.FirstParameter)).Length):
             tags.remove(t)
             ordered.append(t)
     # disable all tags that are not on the base wire.
     for tag in tags:
         PathLog.info("Tag #%d (%.2f, %.2f, %.2f) not on base wire - disabling\n" % (len(ordered), tag.x, tag.y, self.minZ))
         tag.enabled = False
         ordered.append(tag)
     return ordered
Example #18
0
    def setupBaseModel(self, obj, models=None):
        PathLog.track(obj.Label, models)
        if not hasattr(obj, 'Model'):
            obj.addProperty("App::PropertyLink", "Model", "Base", QtCore.QT_TRANSLATE_NOOP("PathJob", "The base objects for all operations"))
            model = FreeCAD.ActiveDocument.addObject("App::DocumentObjectGroup", "Model")
            if model.ViewObject:
                model.ViewObject.Visibility = False
            if models:
                model.addObjects([createModelResourceClone(obj, base) for base in models])
            obj.Model = model

        if hasattr(obj, 'Base'):
            PathLog.info("Converting Job.Base to new Job.Model for {}".format(obj.Label))
            obj.Model.addObject(obj.Base)
            obj.Base = None
            obj.removeProperty('Base')
Example #19
0
    def opOnChanged(self, obj, prop):
        '''opOnChanged(obj, prop) ... base implemenation of the notification framework - do not overwrite.
        The base implementation takes a stab at determining Heights and Depths if the operations's Base
        changes.
        Do not overwrite, overwrite areaOpOnChanged(obj, prop) instead.'''
        #PathLog.track(obj.Label, prop)
        if prop in ['AreaParams', 'PathParams', 'removalshape']:
            obj.setEditorMode(prop, 2)

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

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

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

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

        self.areaOpOnChanged(obj, prop)
Example #20
0
    def updateUI(self):
        customSelected = self.obj.Incision == Incision.Custom
        self.form.custom.setEnabled(customSelected)
        self.form.customLabel.setEnabled(customSelected)
        self.updateBoneList()

        if PathLog.getLevel(LOG_MODULE) == PathLog.Level.DEBUG:
            for obj in FreeCAD.ActiveDocument.Objects:
                if obj.Name.startswith('Shape'):
                    FreeCAD.ActiveDocument.removeObject(obj.Name)
            print('object name %s' % self.obj.Name)
            if hasattr(self.obj.Proxy, "shapes"):
                PathLog.info("showing shapes attribute")
                for shapes in self.obj.Proxy.shapes.itervalues():
                    for shape in shapes:
                        Part.show(shape)
            else:
                PathLog.info("no shapes attribute found")
Example #21
0
    def opSetDefaultValues(self, obj):
        '''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) instead.'''
        PathLog.info("opSetDefaultValues(%s)" % (obj.Label))
        if PathOp.FeatureDepths & self.opFeatures(obj):
            try:
                shape = self.areaOpShapeForDepths(obj)
            except:
                shape = None

            if shape:
                bb = shape.BoundBox
                obj.StartDepth      = bb.ZMax
                obj.FinalDepth      = bb.ZMin
                if PathOp.FeatureStepDown & self.opFeatures(obj):
                    obj.StepDown        = 1.0
            else:
                obj.StartDepth      =  1.0
                obj.FinalDepth      =  0.0
                if PathOp.FeatureStepDown & self.opFeatures(obj):
                    obj.StepDown        =  1.0

        if PathOp.FeatureHeights & self.opFeatures(obj):
            try:
                shape = self.areaOpShapeForDepths(obj)
            except:
                shape = None

            if shape:
                bb = shape.BoundBox
                obj.ClearanceHeight = bb.ZMax + 5.0
                obj.SafeHeight      = bb.ZMax + 3.0
            else:
                obj.ClearanceHeight = 10.0
                obj.SafeHeight      =  8.0

        self.areaOpSetDefaultValues(obj)
Example #22
0
    def addBaseGeometry(self, selection):
        added = False
        shapes = self.obj.BaseShapes
        for sel in selection:
            if sel.Object in shapes:
                PathLog.notice((translate("Path", "Base shape %s already in the list")+"\n") % (sel.Object.Label))
                continue
            if sel.Object.isDerivedFrom('Part::Part2DObject'):
                if sel.HasSubObjects:
                    for sub in sel.SubElementNames:
                        if 'Vertex' in sub:
                            PathLog.info(translate("Path", "Ignoring vertex"))
                        else:
                            self.obj.Proxy.addBase(self.obj, sel.Object, sub)
                else:
                    self.obj.Base = [(p,el) for p,el in self.obj.Base if p != sel.Object]
                    shapes.append(sel.Object)
                    self.obj.BaseShapes = shapes
                added = True
            else:
                base = self.super().addBaseGeometry(selection)
                added = added or base

        return added
Example #23
0
    def onDocumentRestored(self, obj):
        features = self.opFeatures(obj)
        if FeatureBaseGeometry & features and 'App::PropertyLinkSubList' == obj.getTypeIdOfProperty('Base'):
            PathLog.info("Replacing link property with global link (%s)." % obj.State)
            base = obj.Base
            obj.removeProperty('Base')
            self.addBaseProperty(obj)
            obj.Base = base
            obj.touch()
            obj.Document.recompute()

        if FeatureTool & features and not hasattr(obj, 'OpToolDiameter'):
            self.addOpValues(obj, ['tooldia'])

        if FeatureDepths & features and not hasattr(obj, 'OpStartDepth'):
            self.addOpValues(obj, ['start', 'final'])
            if FeatureNoFinalDepth & features:
                obj.setEditorMode('OpFinalDepth', 2)

        if not hasattr(obj, 'OpStockZMax'):
            self.addOpValues(obj, ['stockz'])

        self.setEditorModes(obj, features)
        self.opOnDocumentRestored(obj)
Example #24
0
    def __review(self, obj):
        "checks the selected job for common errors"
        clean = True

        # if obj.X_Max == obj.X_Min or obj.Y_Max == obj.Y_Min:
        #     FreeCAD.Console.PrintWarning(translate("Path_Sanity", "It appears the machine limits haven't been set.  Not able to check path extents.")+"\n")

        if obj.PostProcessor == '':
            FreeCAD.Console.PrintWarning(
                translate("Path_Sanity",
                          "A Postprocessor has not been selected.") + "\n")
            clean = False

        if obj.PostProcessorOutputFile == '':
            FreeCAD.Console.PrintWarning(
                translate(
                    "Path_Sanity",
                    "No output file is named. You'll be prompted during postprocessing."
                ) + "\n")
            clean = False

        for tc in obj.ToolController:
            PathLog.info("Checking: {}.{}".format(obj.Label, tc.Label))
            clean &= self.__checkTC(tc)

        for op in obj.Operations.Group:
            PathLog.info("Checking: {}.{}".format(obj.Label, op.Label))

            if isinstance(op.Proxy,
                          PathScripts.PathProfileContour.ObjectContour):
                if op.Active:
                    # simobj = op.Proxy.execute(op, getsim=True)
                    # if simobj is not None:
                    #     print ('collision detected')
                    #     PC.getCollisionObject(self.baseobj, simobj)
                    #     clean = False
                    pass

            if isinstance(op.Proxy,
                          PathScripts.PathProfileFaces.ObjectProfile):
                if op.Active:
                    # simobj = op.Proxy.execute(op, getsim=True)
                    # if simobj is not None:
                    #     print ('collision detected')
                    #     PC.getCollisionObject(self.baseobj, simobj)
                    #     clean = False
                    pass

            if isinstance(op.Proxy,
                          PathScripts.PathProfileEdges.ObjectProfile):
                if op.Active:
                    # simobj = op.Proxy.execute(op, getsim=True)
                    # if simobj is not None:
                    #     print ('collision detected')
                    #     PC.getCollisionObject(self.baseobj, simobj)
                    #     clean = False
                    pass

            if isinstance(op.Proxy, PathScripts.PathPocket.ObjectPocket):
                if op.Active:
                    # simobj = op.Proxy.execute(op, getsim=True)
                    # if simobj is not None:
                    #     print ('collision detected')
                    #     PC.getCollisionObject(self.baseobj, simobj)
                    #     clean = False
                    pass

            if isinstance(op.Proxy, PathScripts.PathPocketShape.ObjectPocket):
                if op.Active:
                    # simobj = op.Proxy.execute(op, getsim=True)
                    # if simobj is not None:
                    #     print ('collision detected')
                    #     PC.getCollisionObject(self.baseobj, simobj)
                    #     clean = False
                    pass

        if not any(op.Active
                   for op in obj.Operations.Group):  #no active operations
            FreeCAD.Console.PrintWarning(
                translate(
                    "Path_Sanity",
                    "No active operations was found. Post processing will not result in any tooling."
                ))
            clean = False

        if len(obj.ToolController) == 0:  #need at least one active TC
            FreeCAD.Console.PrintWarning(
                translate(
                    "Path_Sanity",
                    "A Tool Controller was not found. Default values are used which is dangerous. Please add a Tool Controller."
                ) + "\n")
            clean = False

        if clean:
            FreeCAD.Console.PrintMessage(
                translate(
                    "Path_Sanity",
                    "No issues detected, {} has passed basic sanity check.").
                format(obj.Label))
Example #25
0
    def opExecute(self, obj, getsim=False):
        '''opExecute(obj, getsim=False) ... implementation of Path.Area ops.
        determines the parameters for _buildPathArea().
        Do not overwrite, implement
            areaOpAreaParams(obj, isHole) ... op specific area param dictionary
            areaOpPathParams(obj, isHole) ... op specific path param dictionary
            areaOpShapes(obj)             ... the shape for path area to process
            areaOpUseProjection(obj)      ... return true if operation can use projection
        instead.'''
        PathLog.track()
        PathLog.info("\n----- opExecute() in PathAreaOp.py")
        # PathLog.debug("OpDepths are Start: {}, and Final: {}".format(obj.OpStartDepth.Value, obj.OpFinalDepth.Value))
        # PathLog.debug("Depths are Start: {}, and Final: {}".format(obj.StartDepth.Value, obj.FinalDepth.Value))
        # PathLog.debug("initOpDepths are Start: {}, and Final: {}".format(self.initOpStartDepth, self.initOpFinalDepth))

        # Instantiate class variables for operation reference
        self.endVector = None
        self.rotateFlag = False
        self.leadIn = 2.0  # self.safOfst / 2.0
        self.cloneNames = []
        self.guiMsgs = [
        ]  # list of message tuples (title, msg) to be displayed in GUI
        self.stockBB = PathUtils.findParentJob(obj).Stock.Shape.BoundBox
        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]
            (self.safOfset, self.safOfst) = opHeights[1]

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

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

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

            # Create visual axises when debugging.
            if PathLog.getLevel(PathLog.thisModule()) == 4:
                self.visualAxis()
        else:
            self.strDep = obj.StartDepth.Value
            self.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
        self.axialRapid = 360 / safeCircum * self.horizRapid

        # 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(
            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)  # list of tuples (shape, isHole, sub, angle, axis)

        # 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]
            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(
                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:
                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

        # 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))
        return sims
Example #26
0
 def addToolController(self, tc):
     group = self.obj.ToolController
     PathLog.info("addToolController(%s): %s" % (tc.Label, [t.Label for t in group]))
     if tc.Name not in [str(t.Name) for t in group]:
         group.append(tc)
         self.obj.ToolController = group
Example #27
0
    def circularHoleExecute(self, obj, holes):
        """circularHoleExecute(obj, holes) ... generate drill operation for each hole in holes."""
        PathLog.track()
        machine = PathMachineState.MachineState()

        self.commandlist.append(Path.Command("(Begin Drilling)"))

        # rapid to clearance height
        command = Path.Command("G0", {"Z": obj.ClearanceHeight.Value})
        machine.addCommand(command)
        self.commandlist.append(command)

        self.commandlist.append(Path.Command("G90"))  # Absolute distance mode

        # Calculate offsets to add to target edge
        endoffset = 0.0
        if obj.ExtraOffset == "Drill Tip":
            endoffset = PathUtils.drillTipLength(self.tool)
        elif obj.ExtraOffset == "2x Drill Tip":
            endoffset = PathUtils.drillTipLength(self.tool) * 2

        # http://linuxcnc.org/docs/html/gcode/g-code.html#gcode:g98-g99
        self.commandlist.append(Path.Command(obj.ReturnLevel))

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

        # This section is technical debt. The computation of the
        # target shapes should be factored out for re-use.
        # This will likely mean refactoring upstream CircularHoleBase to pass
        # spotshapes instead of holes.

        startHeight = obj.StartDepth.Value + self.job.SetupSheet.SafeHeightOffset.Value

        edgelist = []
        for hole in holes:
            v1 = FreeCAD.Vector(hole["x"], hole["y"], obj.StartDepth.Value)
            v2 = FreeCAD.Vector(hole["x"], hole["y"],
                                obj.FinalDepth.Value - endoffset)
            edgelist.append(Part.makeLine(v1, v2))

        # iterate the edgelist and generate gcode
        for edge in edgelist:

            PathLog.debug(edge)

            # move to hole location

            startPoint = edge.Vertexes[0].Point

            command = Path.Command("G0", {
                "X": startPoint.x,
                "Y": startPoint.y
            })
            self.commandlist.append(command)
            machine.addCommand(command)

            command = Path.Command("G0", {"Z": startHeight})
            self.commandlist.append(command)
            machine.addCommand(command)

            # command = Path.Command("G1", {"Z": obj.StartDepth.Value})
            # self.commandlist.append(command)
            # machine.addCommand(command)

            # Technical Debt:  We are assuming the edges are aligned.
            # This assumption should be corrected and the necessary rotations
            # performed to align the edge with the Z axis for drilling

            # Perform drilling
            dwelltime = obj.DwellTime if obj.DwellEnabled else 0.0
            peckdepth = obj.PeckDepth.Value if obj.PeckEnabled else 0.0
            repeat = 1  # technical debt:  Add a repeat property for user control

            try:
                drillcommands = generator.generate(edge, dwelltime, peckdepth,
                                                   repeat,
                                                   obj.RetractHeight.Value)

            except ValueError as e:  # any targets that fail the generator are ignored
                PathLog.info(e)
                continue

            for command in drillcommands:
                self.commandlist.append(command)
                machine.addCommand(command)

        # Cancel canned drilling cycle
        self.commandlist.append(Path.Command("G80"))
        command = Path.Command("G0", {"Z": obj.SafeHeight.Value})
        self.commandlist.append(command)
        machine.addCommand(command)

        # Apply feedrates to commands
        PathFeedRate.setFeedRate(self.commandlist, obj.ToolController)
Example #28
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 = []
            # ----------------------------------------------------------------------
            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)
                        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)

                            # 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

                                PathLog.debug(
                                    translate(
                                        'Path',
                                        "Base Geometry sub: {}".format(sub)))
                                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)

                                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)
                                    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)))
                            # Eif
                        # Efor
                # Efor
                # if False:
                #     if False:
                #         (Tags, Grps) = self.sortTuplesByIndex(allTuples, 2)  # return (TagList, GroupList)
                #         subList = []
                #         for o in range(0, len(Tags)):
                #             subList = []
                #             for (base, sub, tag, angle, axis, stock) in Grps[o]:
                #                 subList.append(sub)
                #             pair = base, subList, angle, axis, stock
                #             baseSubsTuples.append(pair)
                #     if False:
                #         for (bs, sb, tg, agl, ax, stk) in allTuples:
                #             pair = bs, [sb], agl, ax, stk
                #             baseSubsTuples.append(pair)
            # ----------------------------------------------------------------------

            for o in baseSubsTuples:
                self.horiz = []
                self.vert = []
                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)
                    self.vWires = [
                        TechDraw.findShapeOutline(shape, 1,
                                                  FreeCAD.Vector(0, 0, 1))
                        for shape in self.vertical
                    ]
                    for wire in self.vWires:
                        w = PathGeom.removeDuplicateEdges(wire)
                        face = Part.Face(w)
                        # face.tessellate(0.1)
                        if PathGeom.isRoughly(face.Area, 0):
                            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 = []
                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 = []
                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:
                finalDepths = min(finalDepths)
                if subCount == 1:
                    obj.FinalDepth.Value = finDep
        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
            ]
            stockBB = self.stock.Shape.BoundBox

            self.removalshapes = []
            self.bodies = []
            for outline in self.outlines:
                outline.translate(FreeCAD.Vector(0, 0, stockBB.ZMin - 1))
                body = outline.extrude(
                    FreeCAD.Vector(0, 0, stockBB.ZLength + 2))
                self.bodies.append(body)
                # self.removalshapes.append((self.stock.Shape.cut(body), False))
                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:
            shape.tessellate(0.05)  # originally 0.1

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

        # if PathLog.getLevel(PathLog.thisModule()) != 4:
        # if self.delTempNameList > 0:
        #    for tmpNm in self.tempNameList:
        #        FreeCAD.ActiveDocument.removeObject(tmpNm)

        return self.removalshapes
Example #29
0
 def foldsBackOrTurns(self, chord, side):
     dir = chord.getDirectionOf(self)
     PathLog.info("  - direction = %s/%s" % (dir, side))
     return dir == 'Back' or dir == side
Example #30
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
Example #31
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':
                    if obj.OpFinalDepth == obj.FinalDepth:
                        (fzmin, fzmax) = self.getMinMaxOfFaces(Faces)
                        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)

                    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
Example #32
0
 def foldsBackOrTurns(self, chord, side):
     direction = chord.getDirectionOf(self)
     PathLog.info("  - direction = %s/%s" % (direction, side))
     return direction == 'Back' or direction == side
Example #33
0
    def smoothChordCommands(self, bone, inChord, outChord, edge, wire, corner, smooth, color=None):
        if smooth == 0:
            PathLog.info(" No smoothing requested")
            return [bone.lastCommand, outChord.g1Command(bone.F)]

        d = 'in'
        refPoint = inChord.Start
        if smooth == Smooth.Out:
            d = 'out'
            refPoint = outChord.End

        if DraftGeomUtils.areColinear(inChord.asEdge(), outChord.asEdge()):
            PathLog.info(" straight edge %s" % d)
            return [outChord.g1Command(bone.F)]

        pivot = None
        pivotDistance = 0

        PathLog.info("smooth:  (%.2f, %.2f)-(%.2f, %.2f)" % (edge.Vertexes[0].Point.x, edge.Vertexes[0].Point.y, edge.Vertexes[1].Point.x, edge.Vertexes[1].Point.y))
        for e in wire.Edges:
            self.dbg.append(e)
            if type(e.Curve) == Part.LineSegment or type(e.Curve) == Part.Line:
                PathLog.debug("         (%.2f, %.2f)-(%.2f, %.2f)" % (e.Vertexes[0].Point.x, e.Vertexes[0].Point.y, e.Vertexes[1].Point.x, e.Vertexes[1].Point.y))
            else:
                PathLog.debug("         (%.2f, %.2f)^%.2f" % (e.Curve.Center.x, e.Curve.Center.y, e.Curve.Radius))
            for pt in DraftGeomUtils.findIntersection(edge, e, True, findAll=True):
                if not PathGeom.pointsCoincide(pt, corner) and self.pointIsOnEdge(pt, e):
                    # debugMarker(pt, "candidate-%d-%s" % (self.boneId, d), color, 0.05)
                    PathLog.debug("         -> candidate")
                    distance = (pt - refPoint).Length
                    if not pivot or pivotDistance > distance:
                        pivot = pt
                        pivotDistance = distance
                else:
                    PathLog.debug("         -> corner intersect")

        if pivot:
            # debugCircle(pivot, self.toolRadius, "pivot.%d-%s" % (self.boneId, d), color)

            pivotEdge = Part.Edge(Part.Circle(pivot, FreeCAD.Vector(0, 0, 1), self.toolRadius))
            t1 = self.findPivotIntersection(pivot, pivotEdge, inChord.asEdge(), inChord.End, d, color)
            t2 = self.findPivotIntersection(pivot, pivotEdge, outChord.asEdge(), inChord.End, d, color)

            commands = []
            if not PathGeom.pointsCoincide(t1, inChord.Start):
                PathLog.debug("  add lead in")
                commands.append(Chord(inChord.Start, t1).g1Command(bone.F))
            if bone.obj.Side == Side.Left:
                PathLog.debug("  add g3 command")
                commands.append(Chord(t1, t2).g3Command(pivot, bone.F))
            else:
                PathLog.debug("  add g2 command center=(%.2f, %.2f) -> from (%2f, %.2f) to (%.2f, %.2f" % (pivot.x, pivot.y, t1.x, t1.y, t2.x, t2.y))
                commands.append(Chord(t1, t2).g2Command(pivot, bone.F))
            if not PathGeom.pointsCoincide(t2, outChord.End):
                PathLog.debug("  add lead out")
                commands.append(Chord(t2, outChord.End).g1Command(bone.F))

            # debugMarker(pivot, "pivot.%d-%s"     % (self.boneId, d), color, 0.2)
            # debugMarker(t1,    "pivot.%d-%s.in"  % (self.boneId, d), color, 0.1)
            # debugMarker(t2,    "pivot.%d-%s.out" % (self.boneId, d), color, 0.1)

            return commands

        PathLog.info(" no pivot found - straight command")
        return [inChord.g1Command(bone.F), outChord.g1Command(bone.F)]
Example #34
0
    def execute(self, obj, forReal=True):
        if not obj.Base:
            return
        if forReal and not obj.Base.isDerivedFrom("Path::Feature"):
            return
        if not obj.Base.Path:
            return
        if not obj.Base.Path.Commands:
            return

        self.setup(obj, False)

        commands = []           # the dressed commands
        lastChord = Chord()     # the last chord
        lastCommand = None      # the command that generated the last chord
        lastBone = None         # track last bone for optimizations
        oddsAndEnds = []        # track chords that are connected to plunges - in case they form a loop

        boneId = 1
        self.bones = []
        self.locationBlacklist = set()
        # boneIserted = False

        for (i, thisCommand) in enumerate(obj.Base.Path.Commands):
            # if i > 14:
            #    if lastCommand:
            #        commands.append(lastCommand)
            #        lastCommand = None
            #    commands.append(thisCommand)
            #    continue
            PathLog.info("%3d: %s" % (i, thisCommand))
            if thisCommand.Name in movecommands:
                thisChord = lastChord.moveToParameters(thisCommand.Parameters)
                thisIsACandidate = self.canAttachDogbone(thisCommand, thisChord)

                if thisIsACandidate and lastCommand and self.shouldInsertDogbone(obj, lastChord, thisChord):
                    PathLog.info("  Found bone corner")
                    bone = Bone(boneId, obj, lastCommand, lastChord, thisChord, Smooth.InAndOut, thisCommand.Parameters.get('F'))
                    bones = self.insertBone(bone)
                    boneId += 1
                    if lastBone:
                        PathLog.info("  removing potential path crossing")
                        # debugMarker(thisChord.Start, "it", (1.0, 0.0, 1.0))
                        commands, bones = self.removePathCrossing(commands, lastBone, bone)
                    commands.extend(bones[:-1])
                    lastCommand = bones[-1]
                    lastBone = bone
                elif lastCommand and thisChord.isAPlungeMove():
                    PathLog.info("  Looking for connection in odds and ends")
                    haveNewLastCommand = False
                    for chord in (chord for chord in oddsAndEnds if lastChord.connectsTo(chord)):
                        if self.shouldInsertDogbone(obj, lastChord, chord):
                            PathLog.info("    and there is one")
                            bone = Bone(boneId, obj, lastCommand, lastChord, chord, Smooth.In, lastCommand.Parameters.get('F'))
                            bones = self.insertBone(bone)
                            boneId += 1
                            if lastBone:
                                PathLog.info("    removing potential path crossing")
                                # debugMarker(chord.Start, "it", (0.0, 1.0, 1.0))
                                commands, bones = self.removePathCrossing(commands, lastBone, bone)
                            commands.extend(bones[:-1])
                            lastCommand = bones[-1]
                            haveNewLastCommand = True
                    if not haveNewLastCommand:
                        commands.append(lastCommand)
                    lastCommand = None
                    commands.append(thisCommand)
                    lastBone = None
                elif thisIsACandidate:
                    PathLog.info("  is a candidate, keeping for later")
                    if lastCommand:
                        commands.append(lastCommand)
                    lastCommand = thisCommand
                    lastBone = None
                else:
                    PathLog.info("  nope")
                    if lastCommand:
                        commands.append(lastCommand)
                        lastCommand = None
                    commands.append(thisCommand)
                    lastBone = None

                if lastChord.isAPlungeMove() and thisIsACandidate:
                    PathLog.info("  adding to odds and ends")
                    oddsAndEnds.append(thisChord)

                lastChord = thisChord
            else:
                PathLog.info("  Clean slate")
                if lastCommand:
                    commands.append(lastCommand)
                    lastCommand = None
                commands.append(thisCommand)
                lastBone = None
        # for cmd in commands:
        #    PathLog.debug("cmd = '%s'" % cmd)
        path = Path.Path(commands)
        obj.Path = path
Example #35
0
    def setup(self, obj, initial):
        PathLog.info("Here we go ... ")
        if initial:
            if hasattr(obj.Base, "BoneBlacklist"):
                # dressing up a bone dressup
                obj.Side = obj.Base.Side
            else:
                PathLog.info("Default side = right")
                # otherwise dogbones are opposite of the base path's side
                side = Side.Right
                if hasattr(obj.Base, 'Side') and obj.Base.Side == 'Inside':
                    PathLog.info("inside -> side = left")
                    side = Side.Left
                else:
                    PathLog.info("not inside -> side stays right")
                if hasattr(obj.Base, 'Direction') and obj.Base.Direction == 'CCW':
                    PathLog.info("CCW -> switch sides")
                    side = Side.oppositeOf(side)
                else:
                    PathLog.info("CW -> stay on side")
                obj.Side = side

        self.toolRadius = 5
        tc = PathDressup.toolController(obj.Base)
        if tc is None or tc.ToolNumber == 0:
            self.toolRadius = 5
        else:
            tool = tc.Proxy.getTool(tc)  # PathUtils.getTool(obj, tc.ToolNumber)
            if not tool or float(tool.Diameter) == 0:
                self.toolRadius = 5
            else:
                self.toolRadius = float(tool.Diameter) / 2

        self.shapes = {}
        self.dbg = []
Example #36
0
    def __init__(self, obj, deleteOnReject, opPage, selectionFactory):
        PathLog.track(obj.Label, deleteOnReject, opPage, selectionFactory)
        FreeCAD.ActiveDocument.openTransaction(
            translate("Path", "AreaOp Operation"))
        self.deleteOnReject = deleteOnReject
        self.featurePages = []

        features = obj.Proxy.opFeatures(obj)
        opPage.features = features

        if PathOp.FeatureBaseGeometry & features:
            if hasattr(opPage, 'taskPanelBaseGeometryPage'):
                self.featurePages.append(
                    opPage.taskPanelBaseGeometryPage(obj, features))
            else:
                self.featurePages.append(
                    TaskPanelBaseGeometryPage(obj, features))

        if PathOp.FeatureLocations & features:
            if hasattr(opPage, 'taskPanelBaseLocationPage'):
                self.featurePages.append(
                    opPage.taskPanelBaseLocationPage(obj, features))
            else:
                self.featurePages.append(
                    TaskPanelBaseLocationPage(obj, features))

        if PathOp.FeatureDepths & features:
            if hasattr(opPage, 'taskPanelDepthsPage'):
                self.featurePages.append(
                    opPage.taskPanelDepthsPage(obj, features))
            else:
                self.featurePages.append(TaskPanelDepthsPage(obj, features))

        if PathOp.FeatureHeights & features:
            if hasattr(opPage, 'taskPanelHeightsPage'):
                self.featurePages.append(
                    opPage.taskPanelHeightsPage(obj, features))
            else:
                self.featurePages.append(TaskPanelHeightsPage(obj, features))

        self.featurePages.append(opPage)

        for page in self.featurePages:
            page.initPage(obj)
            page.onDirtyChanged(self.pageDirtyChanged)

        taskPanelLayout = PathPreferences.defaultTaskPanelLayout()

        if taskPanelLayout < 2:
            opTitle = opPage.getTitle(obj)
            opPage.setTitle(translate('PathOp', 'Operation'))
            toolbox = QtGui.QToolBox()
            if taskPanelLayout == 0:
                for page in self.featurePages:
                    toolbox.addItem(page.form, page.getTitle(obj))
                toolbox.setCurrentIndex(len(self.featurePages) - 1)
            else:
                for page in reversed(self.featurePages):
                    toolbox.addItem(page.form, page.getTitle(obj))
            PathLog.info("Title: '%s'" % opTitle)
            toolbox.setWindowTitle(opTitle)
            if opPage.getIcon(obj):
                toolbox.setWindowIcon(QtGui.QIcon(opPage.getIcon(obj)))

            self.form = toolbox
        elif taskPanelLayout == 2:
            forms = []
            for page in self.featurePages:
                page.form.setWindowTitle(page.getTitle(obj))
                forms.append(page.form)
            self.form = forms
        elif taskPanelLayout == 3:
            forms = []
            for page in reversed(self.featurePages):
                page.form.setWindowTitle(page.getTitle(obj))
                forms.append(page.form)
            self.form = forms

        self.selectionFactory = selectionFactory
        self.obj = obj
        self.isdirty = deleteOnReject
Example #37
0
    def areaOpShapes(self, obj):
        '''areaOpShapes(obj) ... return top face'''
        # Facing is done either against base objects
        holeShape = None

        if obj.Base:
            PathLog.debug("obj.Base: {}".format(obj.Base))
            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
                        if oneBase[0] is not b[0]:
                            oneBase[1] = False
                        if numpy.isclose(abs(shape.normalAt(0, 0).z),
                                         1):  # horizontal face
                            for wire in shape.Wires[1:]:
                                if obj.ExcludeRaisedAreas is True:
                                    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.error(
                            'The base subobject, "{0}," is not a face. Ignoring "{0}."'
                            .format(sub))

            if obj.ExcludeRaisedAreas is True 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 == True:
            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 is True and oneBase[1] is True:
                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 is True and oneBase[1] is True:
                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)
        else:
            env = PathUtils.getEnvelope(partshape=planeshape,
                                        depthparams=self.depthparams)

        if holeShape is not None:
            PathLog.info("Processing holes...")
            holeEnv = PathUtils.getEnvelope(partshape=holeShape,
                                            depthparams=self.depthparams)
            newEnv = env.cut(holeEnv)
            return [(newEnv, False)]
        else:
            return [(env, False)]
Example #38
0
def Execute(op, obj):
    # pylint: disable=global-statement
    global sceneGraph
    global topZ

    if FreeCAD.GuiUp:
        sceneGraph = FreeCADGui.ActiveDocument.ActiveView.getSceneGraph()

    PathLog.info("*** Adaptive toolpath processing started...\n")

    # hide old toolpaths during recalculation
    obj.Path = Path.Path("(Calculating...)")

    if FreeCAD.GuiUp:
        #store old visibility state
        job = op.getJob(obj)
        oldObjVisibility = obj.ViewObject.Visibility
        oldJobVisibility = job.ViewObject.Visibility

        obj.ViewObject.Visibility = False
        job.ViewObject.Visibility = False

        FreeCADGui.updateGui()

    try:
        helixDiameter = obj.HelixDiameterLimit.Value
        topZ = op.stock.Shape.BoundBox.ZMax
        obj.Stopped = False
        obj.StopProcessing = False
        if obj.Tolerance < 0.001:
            obj.Tolerance = 0.001

        # Get list of working edges for adaptive algorithm
        pathArray = op.pathArray
        if not pathArray:
            PathLog.error("No wire data returned.")
            return

        path2d = convertTo2d(pathArray)

        stockPaths = []
        if hasattr(op.stock, "StockType") and op.stock.StockType == "CreateCylinder":
            stockPaths.append([discretize(op.stock.Shape.Edges[0])])

        else:
            stockBB = op.stock.Shape.BoundBox
            v = []
            v.append(FreeCAD.Vector(stockBB.XMin, stockBB.YMin, 0))
            v.append(FreeCAD.Vector(stockBB.XMax, stockBB.YMin, 0))
            v.append(FreeCAD.Vector(stockBB.XMax, stockBB.YMax, 0))
            v.append(FreeCAD.Vector(stockBB.XMin, stockBB.YMax, 0))
            v.append(FreeCAD.Vector(stockBB.XMin, stockBB.YMin, 0))
            stockPaths.append([v])

        stockPath2d = convertTo2d(stockPaths)

        opType = area.AdaptiveOperationType.ClearingInside
        if obj.OperationType == "Clearing":
            if obj.Side == "Outside":
                opType = area.AdaptiveOperationType.ClearingOutside

            else:
                opType = area.AdaptiveOperationType.ClearingInside

        else:  # profiling
            if obj.Side == "Outside":
                opType = area.AdaptiveOperationType.ProfilingOutside

            else:
                opType = area.AdaptiveOperationType.ProfilingInside

        keepToolDownRatio = 3.0
        if hasattr(obj, 'KeepToolDownRatio'):
            keepToolDownRatio = float(obj.KeepToolDownRatio)

        # put here all properties that influence calculation of adaptive base paths,

        inputStateObject = {
            "tool": float(op.tool.Diameter),
            "tolerance": float(obj.Tolerance),
            "geometry": path2d,
            "stockGeometry": stockPath2d,
            "stepover": float(obj.StepOver),
            "effectiveHelixDiameter": float(helixDiameter),
            "operationType": obj.OperationType,
            "side": obj.Side,
            "forceInsideOut": obj.ForceInsideOut,
            "finishingProfile": obj.FinishingProfile,
            "keepToolDownRatio": keepToolDownRatio,
            "stockToLeave": float(obj.StockToLeave)
        }

        inputStateChanged = False
        adaptiveResults = None

        if obj.AdaptiveOutputState is not None and obj.AdaptiveOutputState != "":
            adaptiveResults = obj.AdaptiveOutputState

        if json.dumps(obj.AdaptiveInputState) != json.dumps(inputStateObject):
            inputStateChanged = True
            adaptiveResults = None

        # progress callback fn, if return true it will stop processing
        def progressFn(tpaths):
            if FreeCAD.GuiUp:
                for path in tpaths: #path[0] contains the MotionType, #path[1] contains list of points
                    if path[0] == area.AdaptiveMotionType.Cutting:
                        sceneDrawPath(path[1],(0,0,1))

                    else:
                        sceneDrawPath(path[1],(1,0,1))

                FreeCADGui.updateGui()

            return obj.StopProcessing

        start = time.time()

        if inputStateChanged or adaptiveResults is None:
            a2d = area.Adaptive2d()
            a2d.stepOverFactor = 0.01 * obj.StepOver
            a2d.toolDiameter = float(op.tool.Diameter)
            a2d.helixRampDiameter = helixDiameter
            a2d.keepToolDownDistRatio = keepToolDownRatio
            a2d.stockToLeave = float(obj.StockToLeave)
            a2d.tolerance = float(obj.Tolerance)
            a2d.forceInsideOut = obj.ForceInsideOut
            a2d.finishingProfile = obj.FinishingProfile
            a2d.opType = opType

            # EXECUTE
            results = a2d.Execute(stockPath2d, path2d, progressFn)

            # need to convert results to python object to be JSON serializable
            adaptiveResults = []
            for result in results:
                adaptiveResults.append({
                    "HelixCenterPoint": result.HelixCenterPoint,
                    "StartPoint": result.StartPoint,
                    "AdaptivePaths": result.AdaptivePaths,
                    "ReturnMotionType": result.ReturnMotionType})

        # GENERATE
        GenerateGCode(op, obj, adaptiveResults, helixDiameter)

        if not obj.StopProcessing:
            PathLog.info("*** Done. Elapsed time: %f sec\n\n" % (time.time()-start))
            obj.AdaptiveOutputState = adaptiveResults
            obj.AdaptiveInputState = inputStateObject

        else:
            PathLog.info("*** Processing cancelled (after: %f sec).\n\n" % (time.time()-start))

    finally:
        if FreeCAD.GuiUp:
            obj.ViewObject.Visibility = oldObjVisibility
            job.ViewObject.Visibility = oldJobVisibility
            sceneClean()
    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
Example #40
0
    def __init__(self, obj, deleteOnReject, opPage, selectionFactory):
        PathLog.track(obj.Label, deleteOnReject, opPage, selectionFactory)
        FreeCAD.ActiveDocument.openTransaction(translate("Path", "AreaOp Operation"))
        self.deleteOnReject = deleteOnReject
        self.featurePages = []

        features = obj.Proxy.opFeatures(obj)
        opPage.features = features

        if PathOp.FeatureBaseGeometry & features:
            if hasattr(opPage, 'taskPanelBaseGeometryPage'):
                self.featurePages.append(opPage.taskPanelBaseGeometryPage(obj, features))
            else:
                self.featurePages.append(TaskPanelBaseGeometryPage(obj, features))

        if PathOp.FeatureLocations & features:
            if hasattr(opPage, 'taskPanelBaseLocationPage'):
                self.featurePages.append(opPage.taskPanelBaseLocationPage(obj, features))
            else:
                self.featurePages.append(TaskPanelBaseLocationPage(obj, features))

        if PathOp.FeatureDepths & features or PathOp.FeatureStepDown:
            if hasattr(opPage, 'taskPanelDepthsPage'):
                self.featurePages.append(opPage.taskPanelDepthsPage(obj, features))
            else:
                self.featurePages.append(TaskPanelDepthsPage(obj, features))

        if PathOp.FeatureHeights & features:
            if hasattr(opPage, 'taskPanelHeightsPage'):
                self.featurePages.append(opPage.taskPanelHeightsPage(obj, features))
            else:
                self.featurePages.append(TaskPanelHeightsPage(obj, features))

        self.featurePages.append(opPage)

        for page in self.featurePages:
            page.initPage(obj)
            page.onDirtyChanged(self.pageDirtyChanged)

        taskPanelLayout = PathPreferences.defaultTaskPanelLayout()

        if taskPanelLayout < 2:
            opTitle = opPage.getTitle(obj)
            opPage.setTitle(translate('PathOp', 'Operation'))
            toolbox = QtGui.QToolBox()
            if taskPanelLayout == 0:
                for page in self.featurePages:
                    toolbox.addItem(page.form, page.getTitle(obj))
                toolbox.setCurrentIndex(len(self.featurePages)-1)
            else:
                for page in reversed(self.featurePages):
                    toolbox.addItem(page.form, page.getTitle(obj))
            PathLog.info("Title: '%s'" % opTitle)
            toolbox.setWindowTitle(opTitle)
            if opPage.getIcon(obj):
                toolbox.setWindowIcon(QtGui.QIcon(opPage.getIcon(obj)))

            self.form = toolbox
        elif taskPanelLayout == 2:
            forms = []
            for page in self.featurePages:
                page.form.setWindowTitle(page.getTitle(obj))
                forms.append(page.form)
            self.form = forms
        elif taskPanelLayout == 3:
            forms = []
            for page in reversed(self.featurePages):
                page.form.setWindowTitle(page.getTitle(obj))
                forms.append(page.form)
            self.form = forms

        self.selectionFactory = selectionFactory
        self.obj = obj
        self.isdirty = deleteOnReject
Example #41
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))
Example #42
0
    def smoothChordCommands(self, bone, inChord, outChord, edge, wire, corner, smooth, color=None):
        if smooth == 0:
            PathLog.info(" No smoothing requested")
            return [bone.lastCommand, outChord.g1Command(bone.F)]

        d = 'in'
        refPoint = inChord.Start
        if smooth == Smooth.Out:
            d = 'out'
            refPoint = outChord.End

        if DraftGeomUtils.areColinear(inChord.asEdge(), outChord.asEdge()):
            PathLog.info(" straight edge %s" % d)
            return [outChord.g1Command(bone.F)]

        pivot = None
        pivotDistance = 0

        PathLog.info("smooth:  (%.2f, %.2f)-(%.2f, %.2f)" % (edge.Vertexes[0].Point.x, edge.Vertexes[0].Point.y, edge.Vertexes[1].Point.x, edge.Vertexes[1].Point.y))
        for e in wire.Edges:
            self.dbg.append(e)
            if type(e.Curve) == Part.LineSegment or type(e.Curve) == Part.Line:
                PathLog.debug("         (%.2f, %.2f)-(%.2f, %.2f)" % (e.Vertexes[0].Point.x, e.Vertexes[0].Point.y, e.Vertexes[1].Point.x, e.Vertexes[1].Point.y))
            else:
                PathLog.debug("         (%.2f, %.2f)^%.2f" % (e.Curve.Center.x, e.Curve.Center.y, e.Curve.Radius))
            for pt in DraftGeomUtils.findIntersection(edge, e, True, findAll=True):
                if not PathGeom.pointsCoincide(pt, corner) and self.pointIsOnEdge(pt, e):
                    # debugMarker(pt, "candidate-%d-%s" % (self.boneId, d), color, 0.05)
                    PathLog.debug("         -> candidate")
                    distance = (pt - refPoint).Length
                    if not pivot or pivotDistance > distance:
                        pivot = pt
                        pivotDistance = distance
                else:
                    PathLog.debug("         -> corner intersect")

        if pivot:
            # debugCircle(pivot, self.toolRadius, "pivot.%d-%s" % (self.boneId, d), color)

            pivotEdge = Part.Edge(Part.Circle(pivot, FreeCAD.Vector(0, 0, 1), self.toolRadius))
            t1 = self.findPivotIntersection(pivot, pivotEdge, inChord.asEdge(), inChord.End, d, color)
            t2 = self.findPivotIntersection(pivot, pivotEdge, outChord.asEdge(), inChord.End, d, color)

            commands = []
            if not PathGeom.pointsCoincide(t1, inChord.Start):
                PathLog.debug("  add lead in")
                commands.append(Chord(inChord.Start, t1).g1Command(bone.F))
            if bone.obj.Side == Side.Left:
                PathLog.debug("  add g3 command")
                commands.append(Chord(t1, t2).g3Command(pivot, bone.F))
            else:
                PathLog.debug("  add g2 command center=(%.2f, %.2f) -> from (%2f, %.2f) to (%.2f, %.2f" % (pivot.x, pivot.y, t1.x, t1.y, t2.x, t2.y))
                commands.append(Chord(t1, t2).g2Command(pivot, bone.F))
            if not PathGeom.pointsCoincide(t2, outChord.End):
                PathLog.debug("  add lead out")
                commands.append(Chord(t2, outChord.End).g1Command(bone.F))

            # debugMarker(pivot, "pivot.%d-%s"     % (self.boneId, d), color, 0.2)
            # debugMarker(t1,    "pivot.%d-%s.in"  % (self.boneId, d), color, 0.1)
            # debugMarker(t2,    "pivot.%d-%s.out" % (self.boneId, d), color, 0.1)

            return commands

        PathLog.info(" no pivot found - straight command")
        return [inChord.g1Command(bone.F), outChord.g1Command(bone.F)]
Example #43
0
def combineHorizontalFaces(faces):
    '''combineHorizontalFaces(faces)...
    This function successfully identifies and combines multiple connected faces and
    works on multiple independent faces with multiple connected faces within the list.
    The return value is list of simplifed faces.
    The Adaptive op is not concerned with which hole edges belong to which face.

    Attempts to do the same shape connecting failed with TechDraw.findShapeOutline() and
    PathGeom.combineConnectedShapes(), so this algorithm was created.
    '''
    horizontal = list()
    offset = 10.0
    topFace = None
    innerFaces = list()

    # Verify all incoming faces are at Z=0.0
    for f in faces:
        if f.BoundBox.ZMin != 0.0:
            f.translate(FreeCAD.Vector(0.0, 0.0, 0.0 - f.BoundBox.ZMin))

    # Make offset compound boundbox solid and cut incoming face extrusions from it
    allFaces = Part.makeCompound(faces)
    if hasattr(allFaces, "Area") and isRoughly(allFaces.Area, 0.0):
        msg = translate(
            'PathGeom',
            'Zero working area to process. Check your selection and settings.')
        PathLog.info(msg)
        return horizontal

    afbb = allFaces.BoundBox
    bboxFace = makeBoundBoxFace(afbb, offset, -5.0)
    bboxSolid = bboxFace.extrude(FreeCAD.Vector(0.0, 0.0, 10.0))
    extrudedFaces = list()
    for f in faces:
        extrudedFaces.append(f.extrude(FreeCAD.Vector(0.0, 0.0, 6.0)))

    # Fuse all extruded faces together
    allFacesSolid = extrudedFaces.pop()
    for i in range(len(extrudedFaces)):
        temp = extrudedFaces.pop().fuse(allFacesSolid)
        allFacesSolid = temp
    cut = bboxSolid.cut(allFacesSolid)

    # Debug
    # Part.show(cut)
    # FreeCAD.ActiveDocument.ActiveObject.Label = "cut"

    # Identify top face and floating inner faces that are the holes in incoming faces
    for f in cut.Faces:
        fbb = f.BoundBox
        if isRoughly(fbb.ZMin, 5.0) and isRoughly(fbb.ZMax, 5.0):
            if (isRoughly(afbb.XMin - offset, fbb.XMin)
                    and isRoughly(afbb.XMax + offset, fbb.XMax)
                    and isRoughly(afbb.YMin - offset, fbb.YMin)
                    and isRoughly(afbb.YMax + offset, fbb.YMax)):
                topFace = f
            else:
                innerFaces.append(f)

    if not topFace:
        return horizontal

    outer = [Part.Face(w) for w in topFace.Wires[1:]]

    if outer:
        for f in outer:
            f.translate(FreeCAD.Vector(0.0, 0.0, 0.0 - f.BoundBox.ZMin))

        if innerFaces:
            # inner = [Part.Face(f.Wire1) for f in innerFaces]
            inner = innerFaces

            for f in inner:
                f.translate(FreeCAD.Vector(0.0, 0.0, 0.0 - f.BoundBox.ZMin))
            innerComp = Part.makeCompound(inner)
            outerComp = Part.makeCompound(outer)
            cut = outerComp.cut(innerComp)
            for f in cut.Faces:
                horizontal.append(f)
        else:
            horizontal = outer

    return horizontal
Example #44
0
    def execute(self, obj, forReal=True):
        if not obj.Base:
            return
        if forReal and not obj.Base.isDerivedFrom("Path::Feature"):
            return
        if not obj.Base.Path:
            return
        if not obj.Base.Path.Commands:
            return

        self.setup(obj, False)

        commands = []           # the dressed commands
        lastChord = Chord()     # the last chord
        lastCommand = None      # the command that generated the last chord
        lastBone = None         # track last bone for optimizations
        oddsAndEnds = []        # track chords that are connected to plunges - in case they form a loop

        boneId = 1
        self.bones = []
        self.locationBlacklist = set()
        # boneIserted = False

        for (i, thisCommand) in enumerate(obj.Base.Path.Commands):
            # if i > 14:
            #    if lastCommand:
            #        commands.append(lastCommand)
            #        lastCommand = None
            #    commands.append(thisCommand)
            #    continue
            PathLog.info("%3d: %s" % (i, thisCommand))
            if thisCommand.Name in movecommands:
                thisChord = lastChord.moveToParameters(thisCommand.Parameters)
                thisIsACandidate = self.canAttachDogbone(thisCommand, thisChord)

                if thisIsACandidate and lastCommand and self.shouldInsertDogbone(obj, lastChord, thisChord):
                    PathLog.info("  Found bone corner")
                    bone = Bone(boneId, obj, lastCommand, lastChord, thisChord, Smooth.InAndOut, thisCommand.Parameters.get('F'))
                    bones = self.insertBone(bone)
                    boneId += 1
                    if lastBone:
                        PathLog.info("  removing potential path crossing")
                        # debugMarker(thisChord.Start, "it", (1.0, 0.0, 1.0))
                        commands, bones = self.removePathCrossing(commands, lastBone, bone)
                    commands.extend(bones[:-1])
                    lastCommand = bones[-1]
                    lastBone = bone
                elif lastCommand and thisChord.isAPlungeMove():
                    PathLog.info("  Looking for connection in odds and ends")
                    haveNewLastCommand = False
                    for chord in (chord for chord in oddsAndEnds if lastChord.connectsTo(chord)):
                        if self.shouldInsertDogbone(obj, lastChord, chord):
                            PathLog.info("    and there is one")
                            bone = Bone(boneId, obj, lastCommand, lastChord, chord, Smooth.In, lastCommand.Parameters.get('F'))
                            bones = self.insertBone(bone)
                            boneId += 1
                            if lastBone:
                                PathLog.info("    removing potential path crossing")
                                # debugMarker(chord.Start, "it", (0.0, 1.0, 1.0))
                                commands, bones = self.removePathCrossing(commands, lastBone, bone)
                            commands.extend(bones[:-1])
                            lastCommand = bones[-1]
                            haveNewLastCommand = True
                    if not haveNewLastCommand:
                        commands.append(lastCommand)
                    lastCommand = None
                    commands.append(thisCommand)
                    lastBone = None
                elif thisIsACandidate:
                    PathLog.info("  is a candidate, keeping for later")
                    if lastCommand:
                        commands.append(lastCommand)
                    lastCommand = thisCommand
                    lastBone = None
                else:
                    PathLog.info("  nope")
                    if lastCommand:
                        commands.append(lastCommand)
                        lastCommand = None
                    commands.append(thisCommand)
                    lastBone = None

                if lastChord.isAPlungeMove() and thisIsACandidate:
                    PathLog.info("  adding to odds and ends")
                    oddsAndEnds.append(thisChord)

                lastChord = thisChord
            else:
                PathLog.info("  Clean slate")
                if lastCommand:
                    commands.append(lastCommand)
                    lastCommand = None
                commands.append(thisCommand)
                lastBone = None
        # for cmd in commands:
        #    PathLog.debug("cmd = '%s'" % cmd)
        path = Path.Path(commands)
        obj.Path = path
Example #45
0
    def __review(self, obj):
        "checks the selected job for common errors"
        clean = True

        # if obj.X_Max == obj.X_Min or obj.Y_Max == obj.Y_Min:
        #     FreeCAD.Console.PrintWarning(translate("Path_Sanity", "It appears the machine limits haven't been set.  Not able to check path extents.")+"\n")

        if obj.PostProcessor == '':
            FreeCAD.Console.PrintWarning(translate("Path_Sanity", "A Postprocessor has not been selected.")+"\n")
            clean = False

        if obj.PostProcessorOutputFile == '':
            FreeCAD.Console.PrintWarning(translate("Path_Sanity", "No output file is named. You'll be prompted during postprocessing.")+"\n")
            clean = False

        for tc in obj.ToolController:
            PathLog.info("Checking: {}.{}".format(obj.Label, tc.Label))
            clean &= self.__checkTC(tc)

        for op in obj.Operations.Group:
            PathLog.info("Checking: {}.{}".format(obj.Label, op.Label))

            if isinstance(op.Proxy, PathScripts.PathProfileContour.ObjectContour):
                if op.Active:
                    # simobj = op.Proxy.execute(op, getsim=True)
                    # if simobj is not None:
                    #     print ('collision detected')
                    #     PC.getCollisionObject(self.baseobj, simobj)
                    #     clean = False
                    pass

            if isinstance(op.Proxy, PathScripts.PathProfileFaces.ObjectProfile):
                if op.Active:
                    # simobj = op.Proxy.execute(op, getsim=True)
                    # if simobj is not None:
                    #     print ('collision detected')
                    #     PC.getCollisionObject(self.baseobj, simobj)
                    #     clean = False
                    pass

            if isinstance(op.Proxy, PathScripts.PathProfileEdges.ObjectProfile):
                if op.Active:
                    # simobj = op.Proxy.execute(op, getsim=True)
                    # if simobj is not None:
                    #     print ('collision detected')
                    #     PC.getCollisionObject(self.baseobj, simobj)
                    #     clean = False
                    pass

            if isinstance(op.Proxy, PathScripts.PathPocket.ObjectPocket):
                if op.Active:
                    # simobj = op.Proxy.execute(op, getsim=True)
                    # if simobj is not None:
                    #     print ('collision detected')
                    #     PC.getCollisionObject(self.baseobj, simobj)
                    #     clean = False
                    pass

            if isinstance(op.Proxy, PathScripts.PathPocketShape.ObjectPocket):
                if op.Active:
                    # simobj = op.Proxy.execute(op, getsim=True)
                    # if simobj is not None:
                    #     print ('collision detected')
                    #     PC.getCollisionObject(self.baseobj, simobj)
                    #     clean = False
                    pass

        if not any(op.Active for op in obj.Operations.Group): #no active operations
            FreeCAD.Console.PrintWarning(translate("Path_Sanity", "No active operations was found. Post processing will not result in any tooling."))
            clean = False

        if len(obj.ToolController) == 0: #need at least one active TC
            FreeCAD.Console.PrintWarning(translate("Path_Sanity", "A Tool Controller was not found. Default values are used which is dangerous. Please add a Tool Controller.")+"\n")
            clean = False

        if clean:
            FreeCAD.Console.PrintMessage(translate("Path_Sanity", "No issues detected, {} has passed basic sanity check.").format(obj.Label))
Example #46
0
    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
Example #47
0
    def execute(self, obj):
        PathLog.track()
        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 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

        # Build preliminary comments
        output = ""
        output += "(" + 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))

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

        # pocket = Path.Area(PocketMode=4,SectionCount=-1,SectionMode=1,Stepdown=0.499)
        # pocket.setParams(PocketExtraOffset = obj.PassExtension.Value, ToolRadius = self.radius)
        # pocket.add(planeshape, op=1)
        # #Part.show(contourwire)
        # path = Path.fromShapes(pocket.getShape())

        edgelist = contourwire.Edges
        edgelist = Part.__sortEdges__(edgelist)

        # use libarea to build the pattern
        a = area.Area()
        c = PathScripts.PathKurveUtils.makeAreaCurve(edgelist, 'CW')
        PathLog.debug(c.text())
        a.append(c)
        a.Reorder()
        output += self.buildpathlibarea(obj, a)

        path = Path.Path(output)
        if len(path.Commands) == 0:
            FreeCAD.Console.PrintMessage(translate("PathMillFace", "The selected settings did not produce a valid path.\n"))

        obj.Path = path
        obj.ViewObject.Visibility = True