Пример #1
0
    def test13(self):
        """Verify setting other modul's log level doesn't change this one's."""
        # if this test fails then most likely the global RESET is broken
        PathLog.setLevel(PathLog.Level.DEBUG, 'SomeOtherModule')

        self.assertEqual(PathLog.getLevel(), PathLog.Level.NOTICE)
        self.assertEqual(PathLog.getLevel(self.MODULE), PathLog.Level.NOTICE)
Пример #2
0
    def useTempJobClones(self, cloneName):
        '''useTempJobClones(cloneName)
            Manage use of temporary model clones for rotational operation calculations.
            Clones are stored in 'rotJobClones' group.'''
        if FreeCAD.ActiveDocument.getObject('rotJobClones'):
            if cloneName == 'Start':
                if PathLog.getLevel(PathLog.thisModule()) < 4:
                    for cln in FreeCAD.ActiveDocument.getObject(
                            'rotJobClones').Group:
                        FreeCAD.ActiveDocument.removeObject(cln.Name)
            elif cloneName == 'Delete':
                if PathLog.getLevel(PathLog.thisModule()) < 4:
                    for cln in FreeCAD.ActiveDocument.getObject(
                            'rotJobClones').Group:
                        FreeCAD.ActiveDocument.removeObject(cln.Name)
                    FreeCAD.ActiveDocument.removeObject('rotJobClones')
                else:
                    FreeCAD.ActiveDocument.getObject(
                        'rotJobClones').purgeTouched()
        else:
            FreeCAD.ActiveDocument.addObject("App::DocumentObjectGroup",
                                             "rotJobClones")
            if FreeCAD.GuiUp:
                FreeCADGui.ActiveDocument.getObject(
                    'rotJobClones').Visibility = False

        if cloneName != 'Start' and cloneName != 'Delete':
            FreeCAD.ActiveDocument.getObject('rotJobClones').addObject(
                FreeCAD.ActiveDocument.getObject(cloneName))
            if FreeCAD.GuiUp:
                FreeCADGui.ActiveDocument.getObject(
                    cloneName).Visibility = False
Пример #3
0
 def test14(self):
     """Verify resetting log level for module falls back to global level."""
     PathLog.setLevel(PathLog.Level.DEBUG, self.MODULE)
     self.assertEqual(PathLog.getLevel(self.MODULE), PathLog.Level.DEBUG)
     # changing global log level does not affect module
     PathLog.setLevel(PathLog.Level.ERROR)
     self.assertEqual(PathLog.getLevel(self.MODULE), PathLog.Level.DEBUG)
     # resetting module log level restores global log level for module
     PathLog.setLevel(PathLog.Level.RESET, self.MODULE)
     self.assertEqual(PathLog.getLevel(self.MODULE), PathLog.Level.ERROR)
     # changing the global log level will also change the module log level
     PathLog.setLevel(PathLog.Level.DEBUG)
     self.assertEqual(PathLog.getLevel(self.MODULE), PathLog.Level.DEBUG)
Пример #4
0
def getEnvelope(partshape, subshape=None, depthparams=None):
    '''
    getEnvelope(partshape, stockheight=None)
    returns a shape corresponding to the partshape silhouette extruded to height.
    if stockheight is given, the returned shape is extruded to that height otherwise the returned shape
    is the height of the original shape boundbox
    partshape = solid object
    stockheight = float - Absolute Z height of the top of material before cutting.
    '''
    PathLog.track(partshape, subshape, depthparams)

    # if partshape.Volume == 0.0:  #Not a 3D object
    #     return None

    zShift = 0
    if subshape is not None:
        if isinstance(subshape, Part.Face):
            PathLog.debug('processing a face')
            sec = Part.makeCompound([subshape])
        else:
            area = Path.Area(Fill=2, Coplanar=0).add(subshape)
            area.setPlane(makeWorkplane(partshape))
            PathLog.debug("About to section with params: {}".format(
                area.getParams()))
            sec = area.makeSections(heights=[0.0], project=True)[0].getShape()

    # zShift = partshape.BoundBox.ZMin - subshape.BoundBox.ZMin
        PathLog.debug('partshapeZmin: {}, subshapeZMin: {}, zShift: {}'.format(
            partshape.BoundBox.ZMin, subshape.BoundBox.ZMin, zShift))

    else:
        area = Path.Area(Fill=2, Coplanar=0).add(partshape)
        area.setPlane(makeWorkplane(partshape))
        sec = area.makeSections(heights=[0.0], project=True)[0].getShape()

    # If depthparams are passed, use it to calculate bottom and height of
    # envelope
    if depthparams is not None:
        #        eLength = float(stockheight)-partshape.BoundBox.ZMin
        eLength = depthparams.safe_height - depthparams.final_depth
        #envelopeshape = sec.extrude(FreeCAD.Vector(0, 0, eLength))
        zShift = depthparams.final_depth - sec.BoundBox.ZMin
        PathLog.debug('boundbox zMIN: {} elength: {} zShift {}'.format(
            partshape.BoundBox.ZMin, eLength, zShift))
    else:
        eLength = partshape.BoundBox.ZLength - sec.BoundBox.ZMin

    # Shift the section based on selection and depthparams.
    newPlace = FreeCAD.Placement(FreeCAD.Vector(0, 0, zShift),
                                 sec.Placement.Rotation)
    sec.Placement = newPlace

    # Extrude the section to top of Boundbox or desired height
    envelopeshape = sec.extrude(FreeCAD.Vector(0, 0, eLength))
    if PathLog.getLevel(PathLog.thisModule()) == PathLog.Level.DEBUG:
        removalshape = FreeCAD.ActiveDocument.addObject(
            "Part::Feature", "Envelope")
        removalshape.Shape = envelopeshape
    return envelopeshape
Пример #5
0
def debugMarker(vector, label, color=None, radius=0.5):
    if PathLog.getLevel(LOG_MODULE) == PathLog.Level.DEBUG:
        obj = FreeCAD.ActiveDocument.addObject("Part::Sphere", label)
        obj.Label = label
        obj.Radius = radius
        obj.Placement = FreeCAD.Placement(vector, FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), 0))
        if color:
            obj.ViewObject.ShapeColor = color
Пример #6
0
def debugMarker(vector, label, color=None, radius=0.5):
    if PathLog.getLevel(PathLog.thisModule()) == PathLog.Level.DEBUG:
        obj = FreeCAD.ActiveDocument.addObject("Part::Sphere", label)
        obj.Label = label
        obj.Radius = radius
        obj.Placement = FreeCAD.Placement(vector, FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), 0))
        if color:
            obj.ViewObject.ShapeColor = color
def debugEdge(edge, prefix, force=False):
    if force or PathLog.getLevel(PathLog.thisModule()) == PathLog.Level.DEBUG:
        pf = edge.valueAt(edge.FirstParameter)
        pl = edge.valueAt(edge.LastParameter)
        if type(edge.Curve) == Part.Line or type(edge.Curve) == Part.LineSegment:
            print("%s %s((%.2f, %.2f, %.2f) - (%.2f, %.2f, %.2f))" % (prefix, type(edge.Curve), pf.x, pf.y, pf.z, pl.x, pl.y, pl.z))
        else:
            pm = edge.valueAt((edge.FirstParameter+edge.LastParameter)/2)
            print("%s %s((%.2f, %.2f, %.2f) - (%.2f, %.2f, %.2f) - (%.2f, %.2f, %.2f))" % (prefix, type(edge.Curve), pf.x, pf.y, pf.z, pm.x, pm.y, pm.z, pl.x, pl.y, pl.z))
Пример #8
0
def debugEdge(edge, prefix, force=False):
    if force or PathLog.getLevel(PathLog.thisModule()) == PathLog.Level.DEBUG:
        pf = edge.valueAt(edge.FirstParameter)
        pl = edge.valueAt(edge.LastParameter)
        if type(edge.Curve) == Part.Line or type(edge.Curve) == Part.LineSegment:
            print("%s %s((%.2f, %.2f, %.2f) - (%.2f, %.2f, %.2f))" % (prefix, type(edge.Curve), pf.x, pf.y, pf.z, pl.x, pl.y, pl.z))
        else:
            pm = edge.valueAt((edge.FirstParameter+edge.LastParameter)/2)
            print("%s %s((%.2f, %.2f, %.2f) - (%.2f, %.2f, %.2f) - (%.2f, %.2f, %.2f))" % (prefix, type(edge.Curve), pf.x, pf.y, pf.z, pm.x, pm.y, pm.z, pl.x, pl.y, pl.z))
Пример #9
0
def debugCircle(vector, r, label, color=None):
    if PathLog.getLevel(LOG_MODULE) == PathLog.Level.DEBUG:
        obj = FreeCAD.ActiveDocument.addObject("Part::Cylinder", label)
        obj.Label = label
        obj.Radius = r
        obj.Height = 1
        obj.Placement = FreeCAD.Placement(vector, FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), 0))
        obj.ViewObject.Transparency = 90
        if color:
            obj.ViewObject.ShapeColor = color
Пример #10
0
def debugCylinder(vector, r, height, label, color=None):
    if PathLog.getLevel(PathLog.thisModule()) == PathLog.Level.DEBUG:
        obj = FreeCAD.ActiveDocument.addObject("Part::Cylinder", label)
        obj.Label = label
        obj.Radius = r
        obj.Height = height
        obj.Placement = FreeCAD.Placement(vector, FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), 0))
        obj.ViewObject.Transparency = 90
        if color:
            obj.ViewObject.ShapeColor = color
Пример #11
0
def getEnvelope(partshape, subshape=None, depthparams=None):
    '''
    getEnvelope(partshape, stockheight=None)
    returns a shape corresponding to the partshape silhouette extruded to height.
    if stockheight is given, the returned shape is extruded to that height otherwise the returned shape
    is the height of the original shape boundbox
    partshape = solid object
    stockheight = float - Absolute Z height of the top of material before cutting.
    '''
    PathLog.track(partshape, subshape, depthparams)

    # if partshape.Volume == 0.0:  #Not a 3D object
    #     return None


    zShift = 0
    if subshape is not None:
        if isinstance(subshape, Part.Face):
            PathLog.debug('processing a face')
            sec = Part.makeCompound([subshape])
        else:
            area = Path.Area(Fill=2, Coplanar=0).add(subshape)
            area.setPlane(makeWorkplane(partshape))
            PathLog.debug("About to section with params: {}".format(area.getParams()))
            sec = area.makeSections(heights=[0.0], project=True)[0].getShape()

       # zShift = partshape.BoundBox.ZMin - subshape.BoundBox.ZMin
        PathLog.debug('partshapeZmin: {}, subshapeZMin: {}, zShift: {}'.format(partshape.BoundBox.ZMin, subshape.BoundBox.ZMin, zShift))

    else:
        area = Path.Area(Fill=2, Coplanar=0).add(partshape)
        area.setPlane(makeWorkplane(partshape))
        sec = area.makeSections(heights=[0.0], project=True)[0].getShape()

    # If depthparams are passed, use it to calculate bottom and height of
    # envelope
    if depthparams is not None:
#        eLength = float(stockheight)-partshape.BoundBox.ZMin
        eLength = depthparams.safe_height - depthparams.final_depth
        #envelopeshape = sec.extrude(FreeCAD.Vector(0, 0, eLength))
        zShift = depthparams.final_depth - sec.BoundBox.ZMin
        PathLog.debug('boundbox zMIN: {} elength: {} zShift {}'.format(partshape.BoundBox.ZMin, eLength, zShift))
    else:
        eLength = partshape.BoundBox.ZLength - sec.BoundBox.ZMin

    # Shift the section based on selection and depthparams.
    newPlace = FreeCAD.Placement(FreeCAD.Vector(0, 0, zShift), sec.Placement.Rotation)
    sec.Placement = newPlace

    # Extrude the section to top of Boundbox or desired height
    envelopeshape = sec.extrude(FreeCAD.Vector(0, 0, eLength))
    if PathLog.getLevel(PathLog.thisModule()) == PathLog.Level.DEBUG:
        removalshape = FreeCAD.ActiveDocument.addObject("Part::Feature", "Envelope")
        removalshape.Shape = envelopeshape
    return envelopeshape
def debugCone(vector, r1, r2, height, label, color=None):
    if PathLog.getLevel(PathLog.thisModule()) == PathLog.Level.DEBUG:
        obj = FreeCAD.ActiveDocument.addObject("Part::Cone", label)
        obj.Label = label
        obj.Radius1 = r1
        obj.Radius2 = r2
        obj.Height = height
        obj.Placement = FreeCAD.Placement(vector, FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), 0))
        obj.ViewObject.Transparency = 90
        if color:
            obj.ViewObject.ShapeColor = color
Пример #13
0
    def processTags(self, obj):
        tagID = 0
        if PathLog.getLevel(PathLog.thisModule()) == PathLog.Level.DEBUG:
            for tag in self.tags:
                tagID += 1
                if tag.enabled:
                    PathLog.debug("x=%s, y=%s, z=%s" % (tag.x, tag.y, self.pathData.minZ))
                    # debugMarker(FreeCAD.Vector(tag.x, tag.y, self.pathData.minZ), "tag-%02d" % tagID , (1.0, 0.0, 1.0), 0.5)
                    # if tag.angle != 90:
                    #    debugCone(tag.originAt(self.pathData.minZ), tag.r1, tag.r2, tag.actualHeight, "tag-%02d" % tagID)
                    # else:
                    #    debugCylinder(tag.originAt(self.pathData.minZ), tag.fullWidth()/2, tag.actualHeight, "tag-%02d" % tagID)

        obj.Path = self.createPath(obj, self.pathData, self.tags)
Пример #14
0
    def updateUI(self):

        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")
Пример #15
0
    def processTags(self, obj):
        tagID = 0
        if PathLog.getLevel(PathLog.thisModule()) == PathLog.Level.DEBUG:
            for tag in self.tags:
                tagID += 1
                if tag.enabled:
                    PathLog.debug("x=%s, y=%s, z=%s" % (tag.x, tag.y, self.pathData.minZ))
                    #debugMarker(FreeCAD.Vector(tag.x, tag.y, self.pathData.minZ), "tag-%02d" % tagID , (1.0, 0.0, 1.0), 0.5)
                    #if tag.angle != 90:
                    #    debugCone(tag.originAt(self.pathData.minZ), tag.r1, tag.r2, tag.actualHeight, "tag-%02d" % tagID)
                    #else:
                    #    debugCylinder(tag.originAt(self.pathData.minZ), tag.fullWidth()/2, tag.actualHeight, "tag-%02d" % tagID)

        obj.Path = self.createPath(obj, self.pathData, self.tags)
    def processTags(self, obj):
        global failures # pylint: disable=global-statement
        failures = []
        tagID = 0
        if PathLog.getLevel(PathLog.thisModule()) == PathLog.Level.DEBUG:
            for tag in self.tags:
                tagID += 1
                if tag.enabled:
                    PathLog.debug("x=%s, y=%s, z=%s" % (tag.x, tag.y, self.pathData.minZ))
                    # debugMarker(FreeCAD.Vector(tag.x, tag.y, self.pathData.minZ), "tag-%02d" % tagID , (1.0, 0.0, 1.0), 0.5)
                    # if not PathGeom.isRoughly(90, tag.angle):
                    #    debugCone(tag.originAt(self.pathData.minZ), tag.r1, tag.r2, tag.actualHeight, "tag-%02d" % tagID)
                    # else:
                    #    debugCylinder(tag.originAt(self.pathData.minZ), tag.fullWidth()/2, tag.actualHeight, "tag-%02d" % tagID)

        obj.Path = self.createPath(obj, self.pathData, self.tags)
Пример #17
0
    def insertBone(self, bone):
        PathLog.debug(">----------------------------------- %d --------------------------------------" % bone.boneId)
        self.boneShapes = []
        blacklisted, inaccessible = self.boneIsBlacklisted(bone)
        enabled = not blacklisted
        self.bones.append((bone.boneId, bone.location(), enabled, inaccessible))

        self.boneId = bone.boneId
        if False and PathLog.getLevel(LOG_MODULE) == PathLog.Level.DEBUG and bone.boneId > 2:
            commands = self.boneCommands(bone, False)
        else:
            commands = self.boneCommands(bone, enabled)
        bone.commands = commands

        self.shapes[bone.boneId] = self.boneShapes
        PathLog.debug("<----------------------------------- %d --------------------------------------" % bone.boneId)
        return commands
Пример #18
0
    def insertBone(self, bone):
        PathLog.debug(">----------------------------------- %d --------------------------------------" % bone.boneId)
        self.boneShapes = []
        blacklisted, inaccessible = self.boneIsBlacklisted(bone)
        enabled = not blacklisted
        self.bones.append((bone.boneId, bone.location(), enabled, inaccessible))

        self.boneId = bone.boneId
        if False and PathLog.getLevel(LOG_MODULE) == PathLog.Level.DEBUG and bone.boneId > 2:
            commands = self.boneCommands(bone, False)
        else:
            commands = self.boneCommands(bone, enabled)
        bone.commands = commands

        self.shapes[bone.boneId] = self.boneShapes
        PathLog.debug("<----------------------------------- %d --------------------------------------" % bone.boneId)
        return commands
Пример #19
0
    def __init__(self, op, obj, feature, sub, length, direction):
        PathLog.debug("Extension(%s, %s, %s, %.2f, %s" %
                      (obj.Label, feature, sub, length, direction))
        self.op = op
        self.obj = obj
        self.feature = feature
        self.sub = sub
        self.length = length
        self.direction = direction
        self.extFaces = None
        self.isDebug = True if PathLog.getLevel(
            PathLog.thisModule()) == 4 else False

        self.avoid = False
        if sub.startswith("Avoid_"):
            self.avoid = True

        self.wire = None
Пример #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")
Пример #21
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)
            PathLog.info('object name %s' % self.obj.Name)
            if hasattr(self.obj.Proxy, "shapes"):
                PathLog.info("showing shapes attribute")
                for shapes in self.obj.Proxy.shapes.values():
                    for shape in shapes:
                        Part.show(shape)
            else:
                PathLog.info("no shapes attribute found")
Пример #22
0
    def _separateWireAtVertexes(self, wire, VV1, VV2):
        PathLog.debug('_separateWireAtVertexes()')
        tolerance = self.JOB.GeometryTolerance.Value
        grps = [[], []]
        wireIdxs = [[], []]
        V1 = FreeCAD.Vector(VV1.X, VV1.Y, VV1.Z)
        V2 = FreeCAD.Vector(VV2.X, VV2.Y, VV2.Z)

        lenE = len(wire.Edges)
        FLGS = list()
        for e in range(0, lenE):
            FLGS.append(0)

        chk4 = False
        for e in range(0, lenE):
            v = 0
            E = wire.Edges[e]
            fv0 = FreeCAD.Vector(E.Vertexes[0].X, E.Vertexes[0].Y,
                                 E.Vertexes[0].Z)
            fv1 = FreeCAD.Vector(E.Vertexes[1].X, E.Vertexes[1].Y,
                                 E.Vertexes[1].Z)

            if fv0.sub(V1).Length < tolerance:
                v = 1
                if fv1.sub(V2).Length < tolerance:
                    v += 3
                    chk4 = True
            elif fv1.sub(V1).Length < tolerance:
                v = 1
                if fv0.sub(V2).Length < tolerance:
                    v += 3
                    chk4 = True

            if fv0.sub(V2).Length < tolerance:
                v = 3
                if fv1.sub(V1).Length < tolerance:
                    v += 1
                    chk4 = True
            elif fv1.sub(V2).Length < tolerance:
                v = 3
                if fv0.sub(V1).Length < tolerance:
                    v += 1
                    chk4 = True
            FLGS[e] += v
        # Efor
        PathLog.debug('_separateWireAtVertexes() FLGS: \n{}'.format(FLGS))

        PRE = list()
        POST = list()
        IDXS = list()
        IDX1 = list()
        IDX2 = list()
        for e in range(0, lenE):
            f = FLGS[e]
            PRE.append(f)
            POST.append(f)
            IDXS.append(e)
            IDX1.append(e)
            IDX2.append(e)

        PRE.extend(FLGS)
        PRE.extend(POST)
        lenFULL = len(PRE)
        IDXS.extend(IDX1)
        IDXS.extend(IDX2)

        if chk4 is True:
            # find beginning 1 edge
            begIdx = None
            begFlg = False
            for e in range(0, lenFULL):
                f = PRE[e]
                i = IDXS[e]
                if f == 4:
                    begIdx = e
                    grps[0].append(f)
                    wireIdxs[0].append(i)
                    break
            # find first 3 edge
            endIdx = None
            for e in range(begIdx + 1, lenE + begIdx):
                f = PRE[e]
                i = IDXS[e]
                grps[1].append(f)
                wireIdxs[1].append(i)
        else:
            # find beginning 1 edge
            begIdx = None
            begFlg = False
            for e in range(0, lenFULL):
                f = PRE[e]
                if f == 1:
                    if begFlg is False:
                        begFlg = True
                    else:
                        begIdx = e
                        break
            # find first 3 edge and group all first wire edges
            endIdx = None
            for e in range(begIdx, lenE + begIdx):
                f = PRE[e]
                i = IDXS[e]
                if f == 3:
                    grps[0].append(f)
                    wireIdxs[0].append(i)
                    endIdx = e
                    break
                else:
                    grps[0].append(f)
                    wireIdxs[0].append(i)
            # Collect remaining edges
            for e in range(endIdx + 1, lenFULL):
                f = PRE[e]
                i = IDXS[e]
                if f == 1:
                    grps[1].append(f)
                    wireIdxs[1].append(i)
                    break
                else:
                    wireIdxs[1].append(i)
                    grps[1].append(f)
            # Efor
        # Eif

        if PathLog.getLevel(PathLog.thisModule()) != 4:
            PathLog.debug('grps[0]: {}'.format(grps[0]))
            PathLog.debug('grps[1]: {}'.format(grps[1]))
            PathLog.debug('wireIdxs[0]: {}'.format(wireIdxs[0]))
            PathLog.debug('wireIdxs[1]: {}'.format(wireIdxs[1]))
            PathLog.debug('PRE: {}'.format(PRE))
            PathLog.debug('IDXS: {}'.format(IDXS))

        return (wireIdxs[0], wireIdxs[1])
Пример #23
0
    def _getCutAreaCrossSection(self, obj, base, origWire, flatWire):
        PathLog.debug('_getCutAreaCrossSection()')
        FCAD = FreeCAD.ActiveDocument
        tolerance = self.JOB.GeometryTolerance.Value
        toolDiam = 2 * self.radius  # self.radius defined in PathAreaOp or PathProfileBase modules
        minBfr = toolDiam * 1.25
        bbBfr = (self.ofstRadius * 2) * 1.25
        if bbBfr < minBfr:
            bbBfr = minBfr
        fwBB = flatWire.BoundBox
        wBB = origWire.BoundBox
        minArea = (self.ofstRadius - tolerance)**2 * math.pi

        useWire = origWire.Wires[0]
        numOrigEdges = len(useWire.Edges)
        sdv = wBB.ZMax
        fdv = obj.FinalDepth.Value
        extLenFwd = sdv - fdv
        WIRE = flatWire.Wires[0]
        numEdges = len(WIRE.Edges)

        # Identify first/last edges and first/last vertex on wire
        begE = WIRE.Edges[0]  # beginning edge
        endE = WIRE.Edges[numEdges - 1]  # ending edge
        blen = begE.Length
        elen = endE.Length
        Vb = begE.Vertexes[0]  # first vertex of wire
        Ve = endE.Vertexes[1]  # last vertex of wire
        pb = FreeCAD.Vector(Vb.X, Vb.Y, fdv)
        pe = FreeCAD.Vector(Ve.X, Ve.Y, fdv)

        # Identify endpoints connecting circle center and diameter
        vectDist = pe.sub(pb)
        diam = vectDist.Length
        cntr = vectDist.multiply(0.5).add(pb)
        R = diam / 2

        pl = FreeCAD.Placement()
        pl.Rotation = FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), 0)
        pl.Base = FreeCAD.Vector(0, 0, 0)

        # Obtain beginning point perpendicular points
        if blen > 0.1:
            bcp = begE.valueAt(begE.getParameterByLength(
                0.1))  # point returned 0.1 mm along edge
        else:
            bcp = FreeCAD.Vector(begE.Vertexes[1].X, begE.Vertexes[1].Y, fdv)
        if elen > 0.1:
            ecp = endE.valueAt(endE.getParameterByLength(
                elen - 0.1))  # point returned 0.1 mm along edge
        else:
            ecp = FreeCAD.Vector(endE.Vertexes[1].X, endE.Vertexes[1].Y, fdv)

        # Create intersection tags for determining which side of wire to cut
        (begInt, begExt, iTAG,
         eTAG) = self._makeIntersectionTags(useWire, numOrigEdges, fdv)
        if not begInt or not begExt:
            return False
        self.iTAG = iTAG
        self.eTAG = eTAG

        # Create extended wire boundbox, and extrude
        extBndbox = self._makeExtendedBoundBox(wBB, bbBfr, fdv)
        extBndboxEXT = extBndbox.extrude(FreeCAD.Vector(0, 0, extLenFwd))

        # Cut model(selected edges) from extended edges boundbox
        cutArea = extBndboxEXT.cut(base.Shape)
        if PathLog.getLevel(PathLog.thisModule()) == 4:
            CA = FCAD.addObject('Part::Feature', 'tmpCutArea')
            CA.Shape = cutArea
            CA.recompute()
            CA.purgeTouched()
            self.tmpGrp.addObject(CA)

        # Get top and bottom faces of cut area (CA), and combine faces when necessary
        topFc = list()
        botFc = list()
        bbZMax = cutArea.BoundBox.ZMax
        bbZMin = cutArea.BoundBox.ZMin
        for f in range(0, len(cutArea.Faces)):
            FcBB = cutArea.Faces[f].BoundBox
            if abs(FcBB.ZMax - bbZMax) < tolerance and abs(FcBB.ZMin -
                                                           bbZMax) < tolerance:
                topFc.append(f)
            if abs(FcBB.ZMax - bbZMin) < tolerance and abs(FcBB.ZMin -
                                                           bbZMin) < tolerance:
                botFc.append(f)
        if len(topFc) == 0:
            PathLog.error('Failed to identify top faces of cut area.')
            return False
        topComp = Part.makeCompound([cutArea.Faces[f] for f in topFc])
        topComp.translate(FreeCAD.Vector(
            0, 0,
            fdv - topComp.BoundBox.ZMin))  # Translate face to final depth
        if len(botFc) > 1:
            PathLog.debug('len(botFc) > 1')
            bndboxFace = Part.Face(extBndbox.Wires[0])
            tmpFace = Part.Face(extBndbox.Wires[0])
            for f in botFc:
                Q = tmpFace.cut(cutArea.Faces[f])
                tmpFace = Q
            botComp = bndboxFace.cut(tmpFace)
        else:
            botComp = Part.makeCompound([
                cutArea.Faces[f] for f in botFc
            ])  # Part.makeCompound([CA.Shape.Faces[f] for f in botFc])
        botComp.translate(FreeCAD.Vector(
            0, 0,
            fdv - botComp.BoundBox.ZMin))  # Translate face to final depth

        # Make common of the two
        comFC = topComp.common(botComp)

        # Determine with which set of intersection tags the model intersects
        (cmnIntArea,
         cmnExtArea) = self._checkTagIntersection(iTAG, eTAG, 'QRY', comFC)
        if cmnExtArea > cmnIntArea:
            PathLog.debug('Cutting on Ext side.')
            self.cutSide = 'E'
            self.cutSideTags = eTAG
            tagCOM = begExt.CenterOfMass
        else:
            PathLog.debug('Cutting on Int side.')
            self.cutSide = 'I'
            self.cutSideTags = iTAG
            tagCOM = begInt.CenterOfMass

        # Make two beginning style(oriented) 'L' shape stops
        begStop = self._makeStop('BEG', bcp, pb, 'BegStop')
        altBegStop = self._makeStop('END', bcp, pb, 'BegStop')

        # Identify to which style 'L' stop the beginning intersection tag is closest,
        # and create partner end 'L' stop geometry, and save for application later
        lenBS_extETag = begStop.CenterOfMass.sub(tagCOM).Length
        lenABS_extETag = altBegStop.CenterOfMass.sub(tagCOM).Length
        if lenBS_extETag < lenABS_extETag:
            endStop = self._makeStop('END', ecp, pe, 'EndStop')
            pathStops = Part.makeCompound([begStop, endStop])
        else:
            altEndStop = self._makeStop('BEG', ecp, pe, 'EndStop')
            pathStops = Part.makeCompound([altBegStop, altEndStop])
        pathStops.translate(FreeCAD.Vector(0, 0,
                                           fdv - pathStops.BoundBox.ZMin))

        # Identify closed wire in cross-section that corresponds to user-selected edge(s)
        workShp = comFC
        fcShp = workShp
        wire = origWire
        WS = workShp.Wires
        lenWS = len(WS)
        if lenWS < 3:
            wi = 0
        else:
            wi = None
            for wvt in wire.Vertexes:
                for w in range(0, lenWS):
                    twr = WS[w]
                    for v in range(0, len(twr.Vertexes)):
                        V = twr.Vertexes[v]
                        if abs(V.X - wvt.X) < tolerance:
                            if abs(V.Y - wvt.Y) < tolerance:
                                # Same vertex found.  This wire to be used for offset
                                wi = w
                                break
            # Efor

            if wi is None:
                PathLog.error(
                    'The cut area cross-section wire does not coincide with selected edge. Wires[] index is None.'
                )
                return False
            else:
                PathLog.debug('Cross-section Wires[] index is {}.'.format(wi))

            nWire = Part.Wire(Part.__sortEdges__(workShp.Wires[wi].Edges))
            fcShp = Part.Face(nWire)
            fcShp.translate(FreeCAD.Vector(0, 0, fdv - workShp.BoundBox.ZMin))
        # Eif

        # verify that wire chosen is not inside the physical model
        if wi > 0:  # and isInterior is False:
            PathLog.debug(
                'Multiple wires in cut area. First choice is not 0. Testing.')
            testArea = fcShp.cut(base.Shape)

            isReady = self._checkTagIntersection(iTAG, eTAG, self.cutSide,
                                                 testArea)
            PathLog.debug('isReady {}.'.format(isReady))

            if isReady is False:
                PathLog.debug('Using wire index {}.'.format(wi - 1))
                pWire = Part.Wire(
                    Part.__sortEdges__(workShp.Wires[wi - 1].Edges))
                pfcShp = Part.Face(pWire)
                pfcShp.translate(
                    FreeCAD.Vector(0, 0, fdv - workShp.BoundBox.ZMin))
                workShp = pfcShp.cut(fcShp)

            if testArea.Area < minArea:
                PathLog.debug(
                    'offset area is less than minArea of {}.'.format(minArea))
                PathLog.debug('Using wire index {}.'.format(wi - 1))
                pWire = Part.Wire(
                    Part.__sortEdges__(workShp.Wires[wi - 1].Edges))
                pfcShp = Part.Face(pWire)
                pfcShp.translate(
                    FreeCAD.Vector(0, 0, fdv - workShp.BoundBox.ZMin))
                workShp = pfcShp.cut(fcShp)
        # Eif

        # Add path stops at ends of wire
        cutShp = workShp.cut(pathStops)
        return cutShp
Пример #24
0
 def test10(self):
     """Verify default log levels is NOTICE."""
     self.assertEqual(PathLog.getLevel(), PathLog.Level.NOTICE)
     self.assertEqual(PathLog.getLevel(self.MODULE), PathLog.Level.NOTICE)
Пример #25
0
def addDebugDisplay():
    return PathLog.getLevel(PathLog.thisModule()) == PathLog.Level.DEBUG
Пример #26
0
    def areaOpShapes(self, obj):
        '''areaOpShapes(obj) ... return shapes representing the solids to be removed.'''
        PathLog.track()
        PathLog.debug("----- areaOpShapes() in PathPocketShape.py")

        self.isDebug = True if PathLog.getLevel(PathLog.thisModule()) == 4 else False
        baseSubsTuples = []
        allTuples = []
        subCount = 0

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

            if obj.EnableRotation == 'Off':
                stock = PathUtils.findParentJob(obj).Stock
                for (base, subList) in obj.Base:
                    tup = (base, subList, 0.0, 'X', stock)
                    baseSubsTuples.append(tup)
            else:
                PathLog.debug('... Rotation is active')
                # method call here
                for p in range(0, len(obj.Base)):
                    (bst, at) = self.process_base_geometry_with_rotation(obj, p, subCount)
                    allTuples.extend(at)
                    baseSubsTuples.extend(bst)

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

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

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

                # add faces for extensions
                self.exts = [] # pylint: disable=attribute-defined-outside-init
                for ext in self.getExtensions(obj):
                    wire = ext.getWire()
                    if wire:
                        face = Part.Face(wire)
                        self.horiz.append(face)
                        self.exts.append(face)

                # check all faces and see if they are touching/overlapping and combine those into a compound
                self.horizontal = [] # pylint: disable=attribute-defined-outside-init
                for shape in PathGeom.combineConnectedShapes(self.horiz):
                    shape.sewShape()
                    # shape.tessellate(0.1)
                    shpZMin = shape.BoundBox.ZMin
                    PathLog.debug('PathGeom.combineConnectedShapes shape.BoundBox.ZMin: {}'.format(shape.BoundBox.ZMin))
                    if obj.UseOutline:
                        wire = TechDraw.findShapeOutline(shape, 1, FreeCAD.Vector(0, 0, 1))
                        wFace = Part.Face(wire)
                        if wFace.BoundBox.ZMin != shpZMin:
                            wFace.translate(FreeCAD.Vector(0, 0, shpZMin - wFace.BoundBox.ZMin))
                        self.horizontal.append(wFace)
                        PathLog.debug('PathGeom.combineConnectedShapes shape.BoundBox.ZMin: {}'.format(wFace.BoundBox.ZMin))
                    else:
                        self.horizontal.append(shape)

                # move all horizontal faces to FinalDepth
                # extrude all faces up to StartDepth and those are the removal shapes
                start_dep = obj.StartDepth.Value
                clrnc = 0.5
                # self._addDebugObject('subBase', subBase.Shape)
                for face in self.horizontal:
                    isFaceUp = True
                    invZ = 0.0
                    useAngle = angle
                    faceZMin = face.BoundBox.ZMin
                    adj_final_dep = obj.FinalDepth.Value
                    trans = obj.FinalDepth.Value - face.BoundBox.ZMin
                    PathLog.debug('face.BoundBox.ZMin: {}'.format(face.BoundBox.ZMin))

                    if obj.EnableRotation != 'Off':
                        PathLog.debug('... running isFaceUp()')
                        isFaceUp = self.isFaceUp(subBase, face)
                        # Determine if face is really oriented toward Z+ (rotational purposes)
                        # ignore for cylindrical faces
                        if not isFaceUp:
                            PathLog.debug('... NOT isFaceUp')
                            useAngle += 180.0
                            invZ = (-2 * face.BoundBox.ZMin)
                            face.translate(FreeCAD.Vector(0.0, 0.0, invZ))
                            faceZMin = face.BoundBox.ZMin  # reset faceZMin
                            PathLog.debug('... face.BoundBox.ZMin: {}'.format(face.BoundBox.ZMin))
                        else:
                            PathLog.debug('... isFaceUp')
                        if useAngle > 180.0:
                            useAngle -= 360.0

                        # Apply LimitDepthToFace property for rotational operations
                        if obj.LimitDepthToFace:
                            if obj.FinalDepth.Value < face.BoundBox.ZMin:
                                PathLog.debug('obj.FinalDepth.Value < face.BoundBox.ZMin')
                                # Raise FinalDepth to face depth
                                adj_final_dep = faceZMin  # face.BoundBox.ZMin  # faceZMin
                                # Ensure StartDepth is above FinalDepth
                                if start_dep <= adj_final_dep:
                                    start_dep = adj_final_dep + 1.0
                                    msg = translate('PathPocketShape', 'Start Depth is lower than face depth. Setting to:')
                                    PathLog.warning(msg + ' {} mm.'.format(start_dep))
                                PathLog.debug('LimitDepthToFace adj_final_dep: {}'.format(adj_final_dep))
                    # Eif

                    face.translate(FreeCAD.Vector(0.0, 0.0, adj_final_dep - faceZMin - clrnc))
                    zExtVal = start_dep - adj_final_dep + (2 * clrnc)
                    extShp = face.removeSplitter().extrude(FreeCAD.Vector(0, 0, zExtVal))
                    self.removalshapes.append((extShp, False, 'pathPocketShape', useAngle, axis, start_dep, adj_final_dep))
                    PathLog.debug("Extent values are strDep: {}, finDep: {},  extrd: {}".format(start_dep, adj_final_dep, zExtVal))
                # Efor face
            # Efor

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

            self.removalshapes = [] # pylint: disable=attribute-defined-outside-init
            self.bodies = [] # pylint: disable=attribute-defined-outside-init
            for outline in self.outlines:
                outline.translate(FreeCAD.Vector(0, 0, stockBB.ZMin - 1))
                body = outline.extrude(FreeCAD.Vector(0, 0, stockBB.ZLength + 2))
                self.bodies.append(body)
                self.removalshapes.append((self.stock.Shape.cut(body), False, 'pathPocketShape', 0.0, 'X', strDep, finDep))

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

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

        return self.removalshapes
Пример #27
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))
Пример #28
0
    def opExecute(self, obj, getsim=False):  # pylint: disable=arguments-differ
        '''opExecute(obj, getsim=False) ... implementation of Path.Area ops.
        determines the parameters for _buildPathArea().
        Do not overwrite, implement
            areaOpAreaParams(obj, isHole) ... op specific area param dictionary
            areaOpPathParams(obj, isHole) ... op specific path param dictionary
            areaOpShapes(obj)             ... the shape for path area to process
            areaOpUseProjection(obj)      ... return true if operation can use projection
        instead.'''
        PathLog.track()

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        self.useTempJobClones(
            'Delete')  # Delete temp job clone group and contents
        self.guiMessage('title', None,
                        show=True)  # Process GUI messages to user
        for ton in self.tempObjectNames:  # remove temporary objects by name
            FreeCAD.ActiveDocument.removeObject(ton)
        PathLog.debug("obj.Name: " + str(obj.Name) + "\n\n")
        return sims
Пример #29
0
    def test11(self):
        """Verify setting global log level."""
        PathLog.setLevel(PathLog.Level.DEBUG)

        self.assertEqual(PathLog.getLevel(), PathLog.Level.DEBUG)
        self.assertEqual(PathLog.getLevel(self.MODULE), PathLog.Level.DEBUG)
Пример #30
0
    def _extractPathWire(self, obj, base, flatWire, cutShp):
        PathLog.debug('_extractPathWire()')

        subLoops = list()
        rtnWIRES = list()
        osWrIdxs = list()
        subDistFactor = 1.0  # Raise to include sub wires at greater distance from original
        fdv = obj.FinalDepth.Value
        wire = flatWire
        lstVrtIdx = len(wire.Vertexes) - 1
        lstVrt = wire.Vertexes[lstVrtIdx]
        frstVrt = wire.Vertexes[0]
        cent0 = FreeCAD.Vector(frstVrt.X, frstVrt.Y, fdv)
        cent1 = FreeCAD.Vector(lstVrt.X, lstVrt.Y, fdv)

        pl = FreeCAD.Placement()
        pl.Rotation = FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), 0)
        pl.Base = FreeCAD.Vector(0, 0, 0)

        # Calculate offset shape, containing cut region
        ofstShp = self._extractFaceOffset(obj, cutShp, False)

        # CHECK for ZERO area of offset shape
        try:
            osArea = ofstShp.Area
        except Exception as ee:
            PathLog.error('No area to offset shape returned.')
            return False

        if PathLog.getLevel(PathLog.thisModule()) == 4:
            os = FreeCAD.ActiveDocument.addObject('Part::Feature',
                                                  'tmpOffsetShape')
            os.Shape = ofstShp
            os.recompute()
            os.purgeTouched()
            self.tmpGrp.addObject(os)

        numOSWires = len(ofstShp.Wires)
        for w in range(0, numOSWires):
            osWrIdxs.append(w)

        # Identify two vertexes for dividing offset loop
        NEAR0 = self._findNearestVertex(ofstShp, cent0)
        min0i = 0
        min0 = NEAR0[0][4]
        for n in range(0, len(NEAR0)):
            N = NEAR0[n]
            if N[4] < min0:
                min0 = N[4]
                min0i = n
        (w0, vi0, pnt0, vrt0, d0) = NEAR0[0]  # min0i
        if PathLog.getLevel(PathLog.thisModule()) == 4:
            near0 = FreeCAD.ActiveDocument.addObject('Part::Feature',
                                                     'tmpNear0')
            near0.Shape = Part.makeLine(cent0, pnt0)
            near0.recompute()
            near0.purgeTouched()
            self.tmpGrp.addObject(near0)

        NEAR1 = self._findNearestVertex(ofstShp, cent1)
        min1i = 0
        min1 = NEAR1[0][4]
        for n in range(0, len(NEAR1)):
            N = NEAR1[n]
            if N[4] < min1:
                min1 = N[4]
                min1i = n
        (w1, vi1, pnt1, vrt1, d1) = NEAR1[0]  # min1i
        if PathLog.getLevel(PathLog.thisModule()) == 4:
            near1 = FreeCAD.ActiveDocument.addObject('Part::Feature',
                                                     'tmpNear1')
            near1.Shape = Part.makeLine(cent1, pnt1)
            near1.recompute()
            near1.purgeTouched()
            self.tmpGrp.addObject(near1)

        if w0 != w1:
            PathLog.warning(
                'Offset wire endpoint indexes are not equal - w0, w1: {}, {}'.
                format(w0, w1))

        if PathLog.getLevel(PathLog.thisModule()) == 4:
            PathLog.debug('min0i is {}.'.format(min0i))
            PathLog.debug('min1i is {}.'.format(min1i))
            PathLog.debug('NEAR0[{}] is {}.'.format(w0, NEAR0[w0]))
            PathLog.debug('NEAR1[{}] is {}.'.format(w1, NEAR1[w1]))
            PathLog.debug('NEAR0 is {}.'.format(NEAR0))
            PathLog.debug('NEAR1 is {}.'.format(NEAR1))

        mainWire = ofstShp.Wires[w0]

        # Check for additional closed loops in offset wire by checking distance to iTAG or eTAG elements
        if numOSWires > 1:
            # check all wires for proximity(children) to intersection tags
            tagsComList = list()
            for T in self.cutSideTags.Faces:
                tcom = T.CenterOfMass
                tv = FreeCAD.Vector(tcom.x, tcom.y, 0.0)
                tagsComList.append(tv)
            subDist = self.ofstRadius * subDistFactor
            for w in osWrIdxs:
                if w != w0:
                    cutSub = False
                    VTXS = ofstShp.Wires[w].Vertexes
                    for V in VTXS:
                        v = FreeCAD.Vector(V.X, V.Y, 0.0)
                        for t in tagsComList:
                            if t.sub(v).Length < subDist:
                                cutSub = True
                                break
                        if cutSub is True:
                            break
                    if cutSub is True:
                        sub = Part.Wire(
                            Part.__sortEdges__(ofstShp.Wires[w].Edges))
                        subLoops.append(sub)
                # Eif

        # Break offset loop into two wires - one of which is the desired profile path wire.
        (edgeIdxs0,
         edgeIdxs1) = self._separateWireAtVertexes(mainWire,
                                                   mainWire.Vertexes[vi0],
                                                   mainWire.Vertexes[vi1])
        edgs0 = list()
        edgs1 = list()
        for e in edgeIdxs0:
            edgs0.append(mainWire.Edges[e])
        for e in edgeIdxs1:
            edgs1.append(mainWire.Edges[e])
        part0 = Part.Wire(Part.__sortEdges__(edgs0))
        part1 = Part.Wire(Part.__sortEdges__(edgs1))

        # Determine which part is nearest original edge(s)
        distToPart0 = self._distMidToMid(wire.Wires[0], part0.Wires[0])
        distToPart1 = self._distMidToMid(wire.Wires[0], part1.Wires[0])
        if distToPart0 < distToPart1:
            rtnWIRES.append(part0)
        else:
            rtnWIRES.append(part1)
        rtnWIRES.extend(subLoops)

        return rtnWIRES
Пример #31
0
    def execute(self, obj, getsim=False):
        PathLog.track()
        commandlist = []
        simlist = []
        commandlist.append(Path.Command("(" + obj.Label + ")"))
        if not obj.Active:
            path = Path.Path("(inactive operation)")
            obj.Path = path
            obj.ViewObject.Visibility = False
            return

        parentJob = PathUtils.findParentJob(obj)
        if parentJob is None:
            return
        baseobject = parentJob.Base
        if baseobject is None:
            return

        self.depthparams = 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=0.0,
            final_depth=obj.FinalDepth.Value,
            user_depths=None)

        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."
            )
        else:
            self.vertFeed = toolLoad.VertFeed.Value
            self.horizFeed = toolLoad.HorizFeed.Value
            self.vertRapid = toolLoad.VertRapid.Value
            self.horizRapid = toolLoad.HorizRapid.Value
            tool = toolLoad.Proxy.getTool(toolLoad)
            if not tool or tool.Diameter == 0:
                FreeCAD.Console.PrintError(
                    "No Tool found or diameter is zero. We need a tool to build a Path."
                )
                return
            else:
                self.radius = tool.Diameter / 2

        if obj.Base:
            PathLog.debug("base items exist.  Processing...")
            for b in obj.Base:
                PathLog.debug("Base item: {}".format(b))
                for sub in b[1]:
                    if "Face" in sub:
                        shape = Part.makeCompound([getattr(b[0].Shape, sub)])
                        #shape = getattr(b[0].Shape, sub)
                    else:
                        edges = [getattr(b[0].Shape, sub) for sub in b[1]]
                        shape = Part.makeFace(edges, 'Part::FaceMakerSimple')

                    env = PathUtils.getEnvelope(baseobject.Shape,
                                                subshape=shape,
                                                depthparams=self.depthparams)
                    removal = env.cut(baseobject.Shape)

                    if PathLog.getLevel(
                            PathLog.thisModule()) == PathLog.Level.DEBUG:
                        removalshape = FreeCAD.ActiveDocument.addObject(
                            "Part::Feature", "removalshape")
                        removalshape.Shape = removal

                    try:
                        (pp, sim) = self._buildPathArea(obj,
                                                        removal,
                                                        getsim=getsim)
                        if sim is not None:
                            simlist.append(sim)
                        commandlist.extend(pp.Commands)
                    except Exception as e:
                        FreeCAD.Console.PrintError(e)
                        FreeCAD.Console.PrintError(
                            "Something unexpected happened. Unable to generate a pocket path. Check project and tool config."
                        )
        else:  # process the job base object as a whole
            PathLog.debug("processing the whole job base object")

            env = PathUtils.getEnvelope(baseobject.Shape,
                                        subshape=None,
                                        depthparams=self.depthparams)
            removal = env.cut(baseobject.Shape)
            if PathLog.getLevel(PathLog.thisModule()) == PathLog.Level.DEBUG:
                removalshape = FreeCAD.ActiveDocument.addObject(
                    "Part::Feature", "removalshape")
                removalshape.Shape = removal
            try:
                (pp, sim) = self._buildPathArea(obj, removal, getsim=getsim)
                commandlist.extend(pp.Commands)
                if sim is not None:
                    simlist.append(sim)

                #commandlist.extend(self._buildPathArea(obj, env.cut(baseobject.Shape)).Commands)
            except Exception as e:
                FreeCAD.Console.PrintError(e)
                FreeCAD.Console.PrintError(
                    "Something unexpected happened. Unable to generate a pocket path. Check project and tool config."
                )

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

        path = Path.Path(commandlist)
        obj.Path = path
        obj.ViewObject.Visibility = True

        PathLog.debug(simlist)
        simshape = None
        if len(simlist) > 1:
            simshape = simlist[0].fuse(simlist[1:])
        elif len(simlist) == 1:
            simshape = simlist[0]

        if simshape is not None and PathLog.getLevel(
                PathLog.thisModule()) == PathLog.Level.DEBUG:
            sim = FreeCAD.ActiveDocument.addObject("Part::Feature", "simshape")
            sim.Shape = simshape
        return simshape
Пример #32
0
def addDebugDisplay():
    return PathLog.getLevel(PathLog.thisModule()) == PathLog.Level.DEBUG
Пример #33
0
    def opExecute(self, obj, getsim=False):  # pylint: disable=arguments-differ
        '''opExecute(obj, getsim=False) ... implementation of Path.Area ops.
        determines the parameters for _buildPathArea().
        Do not overwrite, implement
            areaOpAreaParams(obj, isHole) ... op specific area param dictionary
            areaOpPathParams(obj, isHole) ... op specific path param dictionary
            areaOpShapes(obj)             ... the shape for path area to process
            areaOpUseProjection(obj)      ... return true if operation can use projection
        instead.'''
        PathLog.track()

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        self.useTempJobClones(
            'Delete')  # Delete temp job clone group and contents
        self.guiMessage('title', None,
                        show=True)  # Process GUI messages to user
        PathLog.debug("obj.Name: " + str(obj.Name) + "\n\n")
        return sims
Пример #34
0
    def _makeStop(self, sType, pA, pB, lbl):
        rad = self.radius
        ofstRad = self.ofstRadius
        extra = self.radius / 10

        pl = FreeCAD.Placement()
        pl.Rotation = FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), 0)
        pl.Base = FreeCAD.Vector(0, 0, 0)

        E = FreeCAD.Vector(pB.x, pB.y, 0)  # endpoint
        C = FreeCAD.Vector(pA.x, pA.y, 0)  # checkpoint
        lenEC = E.sub(C).Length

        if self.useComp is True or (self.useComp is False
                                    and self.offsetExtra != 0):
            # 'L' stop shape and edge legend
            # --1--
            # |   |
            # 2   6
            # |   |
            # |   ----5----|
            # |            4
            # -----3-------|
            # positive dist in _makePerp2DVector() is CCW rotation
            p1 = E
            if sType == 'BEG':
                p2 = self._makePerp2DVector(C, E, -0.25)  # E1
                p3 = self._makePerp2DVector(p1, p2, ofstRad + 1 + extra)  # E2
                p4 = self._makePerp2DVector(p2, p3,
                                            0.25 + ofstRad + extra)  # E3
                p5 = self._makePerp2DVector(p3, p4, 1 + extra)  # E4
                p6 = self._makePerp2DVector(p4, p5, ofstRad + extra)  # E5
            elif sType == 'END':
                p2 = self._makePerp2DVector(C, E, 0.25)  # E1
                p3 = self._makePerp2DVector(p1, p2,
                                            -1 * (ofstRad + 1 + extra))  # E2
                p4 = self._makePerp2DVector(p2, p3, -1 *
                                            (0.25 + ofstRad + extra))  # E3
                p5 = self._makePerp2DVector(p3, p4, -1 * (1 + extra))  # E4
                p6 = self._makePerp2DVector(p4, p5,
                                            -1 * (ofstRad + extra))  # E5
            p7 = E  # E6
            L1 = Part.makeLine(p1, p2)
            L2 = Part.makeLine(p2, p3)
            L3 = Part.makeLine(p3, p4)
            L4 = Part.makeLine(p4, p5)
            L5 = Part.makeLine(p5, p6)
            L6 = Part.makeLine(p6, p7)
            wire = Part.Wire([L1, L2, L3, L4, L5, L6])
        else:
            # 'L' stop shape and edge legend
            # :
            # |----2-------|
            # 3            1
            # |-----4------|
            # positive dist in _makePerp2DVector() is CCW rotation
            p1 = E
            if sType == 'BEG':
                p2 = self._makePerp2DVector(
                    C, E, -1 * (0.25 + abs(self.offsetExtra)))  # left, 0.25
                p3 = self._makePerp2DVector(p1, p2,
                                            0.25 + abs(self.offsetExtra))
                p4 = self._makePerp2DVector(
                    p2, p3, (0.5 + abs(self.offsetExtra)))  #      FIRST POINT
                p5 = self._makePerp2DVector(
                    p3, p4,
                    0.25 + abs(self.offsetExtra))  # E1                SECOND
            elif sType == 'END':
                p2 = self._makePerp2DVector(
                    C, E, (0.25 + abs(self.offsetExtra)))  # left, 0.25
                p3 = self._makePerp2DVector(
                    p1, p2, -1 * (0.25 + abs(self.offsetExtra)))
                p4 = self._makePerp2DVector(
                    p2, p3,
                    -1 * (0.5 + abs(self.offsetExtra)))  #      FIRST POINT
                p5 = self._makePerp2DVector(
                    p3, p4, -1 *
                    (0.25 + abs(self.offsetExtra)))  # E1                SECOND
            p6 = p1  # E4
            L1 = Part.makeLine(p1, p2)
            L2 = Part.makeLine(p2, p3)
            L3 = Part.makeLine(p3, p4)
            L4 = Part.makeLine(p4, p5)
            L5 = Part.makeLine(p5, p6)
            wire = Part.Wire([L1, L2, L3, L4, L5])
        # Eif
        face = Part.Face(wire)
        if PathLog.getLevel(PathLog.thisModule()) == 4:
            os = FreeCAD.ActiveDocument.addObject('Part::Feature', 'tmp' + lbl)
            os.Shape = face
            os.recompute()
            os.purgeTouched()
            self.tmpGrp.addObject(os)

        return face
Пример #35
0
    def areaOpShapes(self, obj):
        '''areaOpShapes(obj) ... returns envelope for all wires formed by the base edges.'''
        PathLog.track()

        self.tmpGrp = FreeCAD.ActiveDocument.addObject(
            'App::DocumentObjectGroup', 'tmpDebugGrp')
        tmpGrpNm = self.tmpGrp.Name
        self.JOB = PathUtils.findParentJob(obj)

        self.offsetExtra = abs(obj.OffsetExtra.Value)

        if obj.UseComp:
            self.useComp = True
            self.ofstRadius = self.radius + self.offsetExtra
            self.commandlist.append(
                Path.Command("(Compensated Tool Path. Diameter: " +
                             str(self.radius * 2) + ")"))
        else:
            self.useComp = False
            self.ofstRadius = self.offsetExtra
            self.commandlist.append(Path.Command("(Uncompensated Tool Path)"))

        shapes = []
        if obj.Base:
            basewires = []

            zMin = None
            for b in obj.Base:
                edgelist = []
                for sub in b[1]:
                    edgelist.append(getattr(b[0].Shape, sub))
                basewires.append((b[0], DraftGeomUtils.findWires(edgelist)))
                if zMin is None or b[0].Shape.BoundBox.ZMin < zMin:
                    zMin = b[0].Shape.BoundBox.ZMin

            PathLog.debug(
                'PathProfileEdges areaOpShapes():: len(basewires) is {}'.
                format(len(basewires)))
            for base, wires in basewires:
                for wire in wires:
                    if wire.isClosed() is True:
                        # f = Part.makeFace(wire, 'Part::FaceMakerSimple')
                        # if planar error, Comment out previous line, uncomment the next two
                        (origWire,
                         flatWire) = self._flattenWire(obj, wire,
                                                       obj.FinalDepth.Value)
                        f = origWire.Shape.Wires[0]
                        if f is not False:
                            # shift the compound to the bottom of the base object for proper sectioning
                            zShift = zMin - f.BoundBox.ZMin
                            newPlace = FreeCAD.Placement(
                                FreeCAD.Vector(0, 0, zShift),
                                f.Placement.Rotation)
                            f.Placement = newPlace
                            env = PathUtils.getEnvelope(
                                base.Shape,
                                subshape=f,
                                depthparams=self.depthparams)
                            shapes.append((env, False))
                        else:
                            PathLog.error(
                                translate(
                                    'PathProfileEdges',
                                    'The selected edge(s) are inaccessible.'))
                    else:
                        if self.JOB.GeometryTolerance.Value == 0.0:
                            msg = self.JOB.Label + '.GeometryTolerance = 0.0.'
                            msg += translate(
                                'PathProfileEdges',
                                'Please set to an acceptable value greater than zero.'
                            )
                            PathLog.error(msg)
                        else:
                            cutWireObjs = False
                            (origWire, flatWire) = self._flattenWire(
                                obj, wire, obj.FinalDepth.Value)
                            cutShp = self._getCutAreaCrossSection(
                                obj, base, origWire, flatWire)
                            if cutShp is not False:
                                cutWireObjs = self._extractPathWire(
                                    obj, base, flatWire, cutShp)

                            if cutWireObjs is not False:
                                for cW in cutWireObjs:
                                    shapes.append((cW, False))
                                    self.profileEdgesIsOpen = True
                            else:
                                PathLog.error(
                                    translate(
                                        'PathProfileEdges',
                                        'The selected edge(s) are inaccessible.'
                                    ))

            # Delete the temporary objects
            if PathLog.getLevel(PathLog.thisModule()) != 4:
                for to in self.tmpGrp.Group:
                    FreeCAD.ActiveDocument.removeObject(to.Name)
                FreeCAD.ActiveDocument.removeObject(tmpGrpNm)
            else:
                if FreeCAD.GuiUp:
                    import FreeCADGui
                    FreeCADGui.ActiveDocument.getObject(
                        tmpGrpNm).Visibility = False

        return shapes
Пример #36
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

        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)):
                (bst, at) = self.process_base_geometry_with_rotation(
                    obj, p, subCount)
                allTuples.extend(at)
                baseSubsTuples.extend(bst)

        for base, subs, angle, axis, stock in baseSubsTuples:
            # rotate shorter angle in opposite direction
            if angle > 180:
                angle -= 360
            elif angle < -180:
                angle += 360

            # Re-analyze rotated model for drillable holes
            if obj.EnableRotation != 'Off':
                rotated_features = self.findHoles(obj, base)

            for sub in subs:
                PathLog.debug('sub, angle, axis: {}, {}, {}'.format(
                    sub, angle, axis))
                if self.isHoleEnabled(obj, base, sub):
                    pos = self.holePosition(obj, base, sub)
                    if pos:
                        # Identify face to which edge belongs
                        sub_shape = base.Shape.getElement(sub)

                        # Default is to treat selection as 'Face' shape
                        holeBtm = sub_shape.BoundBox.ZMin

                        if obj.EnableRotation != 'Off':
                            # Update Start and Final depths due to rotation, if auto defaults are active
                            parent_face = self._find_parent_face_of_edge(
                                rotated_features, sub_shape)
                            if parent_face:
                                PathLog.debug('parent_face found')
                                holeBtm = parent_face.BoundBox.ZMin
                                if obj.OpStartDepth == obj.StartDepth:
                                    obj.StartDepth.Value = parent_face.BoundBox.ZMax
                                    PathLog.debug('new StartDepth: {}'.format(
                                        obj.StartDepth.Value))
                                if obj.OpFinalDepth == obj.FinalDepth:
                                    obj.FinalDepth.Value = holeBtm
                                    PathLog.debug(
                                        'new FinalDepth: {}'.format(holeBtm))
                            else:
                                PathLog.debug('NO parent_face identified')

                        if base.Shape.getElement(sub).ShapeType == 'Edge':
                            msg = translate(
                                "Path",
                                "Verify Final Depth of holes based on edges. {} depth is: {} mm"
                                .format(sub, round(holeBtm, 4))) + "  "
                            msg += translate(
                                "Path",
                                "Always select the bottom edge of the hole when using an edge."
                            )
                            PathLog.warning(msg)

                        # Warn user if Final Depth set lower than bottom of hole
                        if obj.FinalDepth.Value < holeBtm:
                            msg = translate(
                                "Path",
                                "Final Depth setting is below the hole bottom for {}."
                                .format(sub)) + '  '
                            msg += translate(
                                "Path",
                                "{} depth is calculated at {} mm".format(
                                    sub, round(holeBtm, 4)))
                            PathLog.warning(msg)

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

        # haveLocations are populated from user-provided (x, y) coordinates
        # provided by the user in the Base Locations tab of the Task Editor window
        if haveLocations(self, obj):
            for location in obj.Locations:
                # holes.append({'x': location.x, 'y': location.y, 'r': 0, 'angle': 0.0, 'axis': 'X', 'holeBtm': obj.FinalDepth.Value})
                holes.append({
                    'x':
                    location.x,
                    'y':
                    location.y,
                    'r':
                    0,
                    'angle':
                    0.0,
                    'axis':
                    'X',
                    'trgtDep':
                    obj.FinalDepth.Value,
                    'stkTop':
                    PathUtils.findParentJob(obj).Stock.Shape.BoundBox.ZMax
                })

        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))
Пример #37
0
    def test12(self):
        """Verify setting module log level."""
        PathLog.setLevel(PathLog.Level.DEBUG, self.MODULE)

        self.assertEqual(PathLog.getLevel(), PathLog.Level.NOTICE)
        self.assertEqual(PathLog.getLevel(self.MODULE), PathLog.Level.DEBUG)