Exemplo n.º 1
0
    def circularHoleExecute(self, obj, holes):
        '''circularHoleExecute(obj, holes) ... generate drill operation for each hole in holes.'''
        PathLog.track()

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

        # rapid to clearance height
        self.commandlist.append(Path.Command('G0', {'Z': obj.ClearanceHeight.Value, 'F': self.vertRapid}))

        tiplength = 0.0
        if obj.ExtraOffset == 'Drill Tip':
            tiplength = PathUtils.drillTipLength(self.tool)
        elif obj.ExtraOffset == '2x Drill Tip':
            tiplength = PathUtils.drillTipLength(self.tool) * 2

        holes = PathUtils.sort_jobs(holes, ['x', 'y'])
        self.commandlist.append(Path.Command('G90'))
        self.commandlist.append(Path.Command(obj.ReturnLevel))

        cmd = "G81"
        cmdParams = {}
        cmdParams['Z'] = obj.FinalDepth.Value - tiplength
        cmdParams['F'] = self.vertFeed
        cmdParams['R'] = obj.RetractHeight.Value

        if obj.PeckEnabled and obj.PeckDepth.Value > 0:
            cmd = "G83"
            cmdParams['Q'] = obj.PeckDepth.Value
        elif obj.DwellEnabled and obj.DwellTime > 0:
            cmd = "G82"
            cmdParams['P'] = obj.DwellTime

        # parentJob = PathUtils.findParentJob(obj)
        # startHeight = obj.StartDepth.Value + parentJob.SetupSheet.SafeHeightOffset.Value
        startHeight = obj.StartDepth.Value + self.job.SetupSheet.SafeHeightOffset.Value

        for p in holes:
            params = {}
            params['X'] = p['x']
            params['Y'] = p['y']

            # move to hole location
            self.commandlist.append(Path.Command('G0', {'X': p['x'], 'Y': p['y'], 'F': self.horizRapid}))
            self.commandlist.append(Path.Command('G0', {'Z': startHeight, 'F': self.vertRapid}))
            self.commandlist.append(Path.Command('G1', {'Z': obj.StartDepth.Value, 'F': self.vertFeed}))

            # Update changes to parameters
            params.update(cmdParams)

            # Perform canned drilling cycle
            self.commandlist.append(Path.Command(cmd, params))

            # Cancel canned drilling cycle
            self.commandlist.append(Path.Command('G80'))
            self.commandlist.append(Path.Command('G0', {'Z': obj.SafeHeight.Value}))
Exemplo n.º 2
0
    def opExecute(self, obj, getsim=False):
        '''opExecute(obj, getsim=False) ... implementation of Path.Area ops.
        determines the parameters for _buildPathArea().
        Do not overwrite, implement
            areaOpAreaParams(obj, isHole) ... op specific area param dictionary
            areaOpPathParams(obj, isHole) ... op specific path param dictionary
            areaOpShapes(obj)             ... the shape for path area to process
            areaOpUseProjection(obj)      ... return true if operation can use projection
        instead.'''
        PathLog.track()
        self.endVector = None

        finish_step = obj.FinishDepth.Value if hasattr(obj, "FinishDepth") else 0.0
        self.depthparams = PathUtils.depth_params(
                clearance_height=obj.ClearanceHeight.Value,
                safe_height=obj.SafeHeight.Value,
                start_depth=obj.StartDepth.Value,
                step_down=obj.StepDown.Value,
                z_finish_step=finish_step,
                final_depth=obj.FinalDepth.Value,
                user_depths=None)

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

        shapes = self.areaOpShapes(obj)

        jobs = [{
            'x': s[0].BoundBox.XMax,
            'y': s[0].BoundBox.YMax,
            'shape': s
        } for s in shapes]

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

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

        sims = []
        for (shape, isHole) in shapes:
            try:
                (pp, sim) = self._buildPathArea(obj, shape, isHole, start, getsim)
                self.commandlist.extend(pp.Commands)
                sims.append(sim)
            except Exception as e:
                FreeCAD.Console.PrintError(e)
                FreeCAD.Console.PrintError("Something unexpected happened. Check project and tool config.")

            if self.areaOpRetractTool(obj):
                self.endVector = None

        return sims
Exemplo n.º 3
0
    def circularHoleExecute(self, obj, holes):
        '''circularHoleExecute(obj, holes) ... generate drill operation for each hole in holes.'''
        PathLog.track()

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

        # rapid to clearance height
        self.commandlist.append(
            Path.Command('G0', {
                'Z': obj.ClearanceHeight.Value,
                'F': self.vertRapid
            }))

        tiplength = 0.0
        if obj.AddTipLength:
            tiplength = PathUtils.drillTipLength(self.tool)

        holes = PathUtils.sort_jobs(holes, ['x', 'y'])
        self.commandlist.append(Path.Command('G90'))
        self.commandlist.append(Path.Command(obj.ReturnLevel))

        # ml: I'm not sure whey these were here, they seem redundant
        ## rapid to first hole location, with spindle still retracted:
        #p0 = holes[0]
        #self.commandlist.append(Path.Command('G0', {'X': p0['x'], 'Y': p0['y'], 'F': self.horizRapid}))
        ## move tool to clearance plane
        #self.commandlist.append(Path.Command('G0', {'Z': obj.ClearanceHeight.Value, 'F': self.vertRapid}))

        cmd = "G81"
        cmdParams = {}
        cmdParams['Z'] = obj.FinalDepth.Value - tiplength
        cmdParams['F'] = self.vertFeed
        cmdParams['R'] = obj.RetractHeight.Value
        if obj.PeckEnabled and obj.PeckDepth.Value > 0:
            cmd = "G83"
            cmdParams['Q'] = obj.PeckDepth.Value
        elif obj.DwellEnabled and obj.DwellTime > 0:
            cmd = "G82"
            cmdParams['P'] = obj.DwellTime

        for p in holes:
            params = {}
            params['X'] = p['x']
            params['Y'] = p['y']
            params.update(cmdParams)
            self.commandlist.append(Path.Command(cmd, params))

        self.commandlist.append(Path.Command('G80'))
Exemplo n.º 4
0
    def circularHoleExecute(self, obj, holes):
        '''circularHoleExecute(obj, holes) ... generate drill operation for each hole in holes.'''
        PathLog.track()

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

        # rapid to clearance height
        self.commandlist.append(Path.Command('G0', {'Z': obj.ClearanceHeight.Value, 'F': self.vertRapid}))

        tiplength = 0.0
        if obj.AddTipLength:
            tiplength = PathUtils.drillTipLength(self.tool)

        holes = PathUtils.sort_jobs(holes, ['x', 'y'])
        self.commandlist.append(Path.Command('G90'))
        self.commandlist.append(Path.Command(obj.ReturnLevel))

        # ml: I'm not sure whey these were here, they seem redundant
        ## rapid to first hole location, with spindle still retracted:
        #p0 = holes[0]
        #self.commandlist.append(Path.Command('G0', {'X': p0['x'], 'Y': p0['y'], 'F': self.horizRapid}))
        ## move tool to clearance plane
        #self.commandlist.append(Path.Command('G0', {'Z': obj.ClearanceHeight.Value, 'F': self.vertRapid}))

        cmd = "G81"
        cmdParams = {}
        cmdParams['Z'] = obj.FinalDepth.Value - tiplength
        cmdParams['F'] = self.vertFeed
        cmdParams['R'] = obj.RetractHeight.Value
        if obj.PeckEnabled and obj.PeckDepth.Value > 0:
            cmd = "G83"
            cmdParams['Q'] = obj.PeckDepth.Value
        elif obj.DwellEnabled and obj.DwellTime > 0:
            cmd = "G82"
            cmdParams['P'] = obj.DwellTime

        for p in holes:
            params = {}
            params['X'] = p['x']
            params['Y'] = p['y']
            params.update(cmdParams)
            self.commandlist.append(Path.Command(cmd, params))

        self.commandlist.append(Path.Command('G80'))
Exemplo n.º 5
0
    def execute(self, obj):
        PathLog.track()

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

        output = ""
        if obj.Comment != "":
            output += '(' + str(obj.Comment) + ')\n'

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

        tiplength = 0.0
        if obj.AddTipLength:
            tiplength = PathUtils.drillTipLength(tool)

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

            # Arch PanelSheet
            if hasattr(baseobject, "Proxy"):
                holes = []
                if isinstance(baseobject.Proxy, ArchPanel.PanelSheet):
                    baseobject.Proxy.execute(baseobject)
                    i = 0
                    holeshapes = baseobject.Proxy.getHoles(baseobject,
                                                           transform=True)
                    tooldiameter = obj.ToolController.Proxy.getTool(
                        obj.ToolController).Diameter
                    for holeshape in holeshapes:
                        PathLog.debug('Entering new HoleShape')
                        for wire in holeshape.Wires:
                            PathLog.debug('Entering new Wire')
                            for edge in wire.Edges:
                                if PathUtils.isDrillable(
                                        baseobject, edge, tooldiameter):
                                    PathLog.debug(
                                        'Found drillable hole edges: {}'.
                                        format(edge))
                                    x = edge.Curve.Center.x
                                    y = edge.Curve.Center.y
                                    diameter = edge.BoundBox.XLength
                                    holes.append({
                                        'x':
                                        x,
                                        'y':
                                        y,
                                        'featureName':
                                        baseobject.Name + '.' + 'Drill' +
                                        str(i),
                                        'd':
                                        diameter
                                    })
                                    i = i + 1
            else:
                holes = self.findHoles(obj, baseobject.Shape)
                for i in range(len(holes)):
                    holes[i]['featureName'] = baseobject.Name + '.' + holes[i][
                        'featureName']
            names = []
            positions = []
            enabled = []
            diameters = []
            for h in holes:
                if len(names) == 0:
                    self.setDepths(obj, baseobject, h)
                names.append(h['featureName'])
                positions.append(FreeCAD.Vector(h['x'], h['y'], 0))
                enabled.append(1)
                diameters.append(h['d'])
            obj.Names = names
            obj.Positions = positions
            obj.Enabled = enabled
            obj.Diameters = diameters

        locations = []
        output = "(Begin Drilling)\n"

        for i in range(len(obj.Names)):
            if obj.Enabled[i] > 0:
                locations.append({
                    'x': obj.Positions[i].x,
                    'y': obj.Positions[i].y
                })
        if len(locations) > 0:
            locations = PathUtils.sort_jobs(locations, ['x', 'y'])
            output += "G90 " + obj.ReturnLevel + "\n"
            # rapid to clearance height
            output += "G0 Z" + str(
                obj.ClearanceHeight.Value) + "F " + PathUtils.fmt(
                    self.vertRapid) + "\n"
            # rapid to first hole location, with spindle still retracted:

            p0 = locations[0]
            output += "G0 X" + fmt(p0['x']) + " Y" + fmt(
                p0['y']) + "F " + PathUtils.fmt(self.horizRapid) + "\n"
            # move tool to clearance plane
            output += "G0 Z" + fmt(
                obj.ClearanceHeight.Value) + "F " + PathUtils.fmt(
                    self.vertRapid) + "\n"
            pword = ""
            qword = ""
            if obj.PeckDepth.Value > 0 and obj.PeckEnabled:
                cmd = "G83"
                qword = " Q" + fmt(obj.PeckDepth.Value)
            elif obj.DwellTime > 0 and obj.DwellEnabled:
                cmd = "G82"
                pword = " P" + fmt(obj.DwellTime)
            else:
                cmd = "G81"
            for p in locations:
                output += cmd + \
                    " X" + fmt(p['x']) + \
                    " Y" + fmt(p['y']) + \
                    " Z" + fmt(obj.FinalDepth.Value - tiplength) + qword + pword + \
                    " R" + str(obj.RetractHeight.Value) + \
                    " F" + str(self.vertFeed) + "\n" \

            output += "G80\n"

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        self.useTempJobClones(
            'Delete')  # Delete temp job clone group and contents
        self.guiMessage('title', None,
                        show=True)  # Process GUI messages to user
        for ton in self.tempObjectNames:  # remove temporary objects by name
            FreeCAD.ActiveDocument.removeObject(ton)
        PathLog.debug("obj.Name: " + str(obj.Name) + "\n\n")
        return sims
Exemplo n.º 7
0
    def circularHoleExecute(self, obj, holes):
        '''circularHoleExecute(obj, holes) ... generate drill operation for each hole in holes.'''
        PathLog.track()
        PathLog.debug("\ncircularHoleExecute() in PathDrilling.py")

        lastAxis = None
        lastAngle = 0.0

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

        # rapid to clearance height
        self.commandlist.append(
            Path.Command('G0', {
                'Z': obj.ClearanceHeight.Value,
                'F': self.vertRapid
            }))

        tiplength = 0.0
        if obj.AddTipLength:
            tiplength = PathUtils.drillTipLength(self.tool)

        holes = PathUtils.sort_jobs(holes, ['x', 'y'])
        self.commandlist.append(Path.Command('G90'))
        self.commandlist.append(Path.Command(obj.ReturnLevel))

        for p in holes:
            cmd = "G81"
            cmdParams = {}
            cmdParams['Z'] = p['trgtDep'] - tiplength
            cmdParams['F'] = self.vertFeed
            cmdParams['R'] = obj.RetractHeight.Value
            if obj.PeckEnabled and obj.PeckDepth.Value > 0:
                cmd = "G83"
                cmdParams['Q'] = obj.PeckDepth.Value
            elif obj.DwellEnabled and obj.DwellTime > 0:
                cmd = "G82"
                cmdParams['P'] = obj.DwellTime

            params = {}
            params['X'] = p['x']
            params['Y'] = p['y']
            params.update(cmdParams)
            if obj.EnableRotation != 'Off':
                angle = p['angle']
                axis = p['axis']
                # Rotate model to index for hole
                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'

                # Set initial values for last axis and angle
                if lastAxis is None:
                    lastAxis = axisOfRot
                    lastAngle = angle

                # Handle axial and angular transitions
                if axisOfRot != lastAxis:
                    self.commandlist.append(
                        Path.Command('G0', {
                            'Z': obj.SafeHeight.Value,
                            'F': self.vertRapid
                        }))
                    self.commandlist.append(
                        Path.Command('G0', {
                            lastAxis: 0.0,
                            'F': self.axialRapid
                        }))
                elif angle != lastAngle:
                    self.commandlist.append(
                        Path.Command('G0', {
                            'Z': obj.SafeHeight.Value,
                            'F': self.vertRapid
                        }))

                # Prepare for drilling cycle
                self.commandlist.append(
                    Path.Command('G0', {
                        axisOfRot: angle,
                        'F': self.axialRapid
                    }))
                self.commandlist.append(
                    Path.Command('G0', {
                        'X': p['x'],
                        'Y': p['y'],
                        'F': self.horizRapid
                    }))
                self.commandlist.append(
                    Path.Command('G1', {
                        'Z': p['stkTop'],
                        'F': self.vertFeed
                    }))

            # Perform and cancel canned drilling cycle
            self.commandlist.append(Path.Command(cmd, params))
            self.commandlist.append(
                Path.Command('G0', {'Z': obj.SafeHeight.Value}))

            # shift axis and angle values
            if obj.EnableRotation != 'Off':
                lastAxis = axisOfRot
                lastAngle = angle

        if obj.EnableRotation != 'Off':
            self.commandlist.append(
                Path.Command('G0', {
                    'Z': obj.SafeHeight.Value,
                    'F': self.vertRapid
                }))
            self.commandlist.append(
                Path.Command('G0', {
                    lastAxis: 0.0,
                    'F': self.axialRapid
                }))
Exemplo n.º 8
0
    def opExecute(self, obj, getsim=False):  # pylint: disable=arguments-differ
        '''opExecute(obj, getsim=False) ... implementation of Path.Area ops.
        determines the parameters for _buildPathArea().
        Do not overwrite, implement
            areaOpAreaParams(obj, isHole) ... op specific area param dictionary
            areaOpPathParams(obj, isHole) ... op specific path param dictionary
            areaOpShapes(obj)             ... the shape for path area to process
            areaOpUseProjection(obj)      ... return true if operation can use projection
        instead.'''
        PathLog.track()

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        self.useTempJobClones(
            'Delete')  # Delete temp job clone group and contents
        self.guiMessage('title', None,
                        show=True)  # Process GUI messages to user
        PathLog.debug("obj.Name: " + str(obj.Name) + "\n\n")
        return sims
Exemplo n.º 9
0
    def circularHoleExecute(self, obj, holes):
        '''circularHoleExecute(obj, holes) ... generate drill operation for each hole in holes.'''
        PathLog.track()
        PathLog.debug("\ncircularHoleExecute() in PathDrilling.py")

        lastAxis = None
        lastAngle = 0.0

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

        # rapid to clearance height
        self.commandlist.append(Path.Command('G0', {'Z': obj.ClearanceHeight.Value, 'F': self.vertRapid}))

        tiplength = 0.0
        if obj.ExtraOffset == 'Drill Tip':
            tiplength = PathUtils.drillTipLength(self.tool)
        elif obj.ExtraOffset == '2x Drill Tip':
            tiplength = PathUtils.drillTipLength(self.tool) * 2

        holes = PathUtils.sort_jobs(holes, ['x', 'y'])
        self.commandlist.append(Path.Command('G90'))
        self.commandlist.append(Path.Command(obj.ReturnLevel))

        for p in holes:
            cmd = "G81"
            cmdParams = {}
            cmdParams['Z'] = p['trgtDep'] - tiplength
            cmdParams['F'] = self.vertFeed
            cmdParams['R'] = obj.RetractHeight.Value
            if obj.PeckEnabled and obj.PeckDepth.Value > 0:
                cmd = "G83"
                cmdParams['Q'] = obj.PeckDepth.Value
            elif obj.DwellEnabled and obj.DwellTime > 0:
                cmd = "G82"
                cmdParams['P'] = obj.DwellTime

            params = {}
            params['X'] = p['x']
            params['Y'] = p['y']
            if obj.EnableRotation != 'Off':
                angle = p['angle']
                axis = p['axis']
                # Rotate model to index for hole
                if axis == 'X':
                    axisOfRot = 'A'
                elif axis == 'Y':
                    axisOfRot = 'B'
                elif axis == 'Z':
                    axisOfRot = 'C'
                else:
                    axisOfRot = 'A'

                # Set initial values for last axis and angle
                if lastAxis is None:
                    lastAxis = axisOfRot
                    lastAngle = angle

                # Handle axial and angular transitions
                if axisOfRot != lastAxis:
                    self.commandlist.append(Path.Command('G0', {'Z': obj.SafeHeight.Value, 'F': self.vertRapid}))
                    self.commandlist.append(Path.Command('G0', {lastAxis: 0.0, 'F': self.axialRapid}))
                elif angle != lastAngle:
                    self.commandlist.append(Path.Command('G0', {'Z': obj.SafeHeight.Value, 'F': self.vertRapid}))

                # Prepare for drilling cycle
                self.commandlist.append(Path.Command('G0', {axisOfRot: angle, 'F': self.axialRapid}))
                self.commandlist.append(Path.Command('G0', {'X': p['x'], 'Y': p['y'], 'F': self.horizRapid}))
                self.commandlist.append(Path.Command('G1', {'Z': obj.StartDepth.Value, 'F': self.vertFeed}))

                # Update retract height due to rotation
                self.opSetDefaultRetractHeight(obj)
                cmdParams['R'] = obj.RetractHeight.Value

            # Update changes to parameters
            params.update(cmdParams)

            # Perform canned drilling cycle
            self.commandlist.append(Path.Command(cmd, params))

            # Cancel canned drilling cycle
            self.commandlist.append(Path.Command('G80'))
            self.commandlist.append(Path.Command('G0', {'Z': obj.SafeHeight.Value}))

            # shift axis and angle values
            if obj.EnableRotation != 'Off':
                lastAxis = axisOfRot
                lastAngle = angle

        if obj.EnableRotation != 'Off':
            self.commandlist.append(Path.Command('G0', {'Z': obj.SafeHeight.Value, 'F': self.vertRapid}))
            self.commandlist.append(Path.Command('G0', {lastAxis: 0.0, 'F': self.axialRapid}))
Exemplo n.º 10
0
    def execute(self, obj):
        from Part import Circle, Cylinder, Plane
        from PathScripts import PathUtils
        from math import sqrt

        output = '(helix cut operation'
        if obj.Comment:
            output += ', ' + str(obj.Comment) + ')\n'
        else:
            output += ')\n'

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

            if not obj.ToolController:
                obj.ToolController = PathUtils.findToolController(obj)

            toolLoad = obj.ToolController

            if toolLoad is None or toolLoad.ToolNumber == 0:
                FreeCAD.Console.PrintError(
                    "PathHelix: No tool selected for helix cut operation, insert a tool change operation first\n"
                )
                obj.Path = Path.Path(
                    "(ERROR: no tool selected for helix cut operation)")
                return

            tool = toolLoad.Proxy.getTool(toolLoad)

            zsafe = max(
                baseobj.Shape.BoundBox.ZMax
                for baseobj, features in obj.Features) + obj.Clearance.Value
            output += "G0 Z" + fmt(zsafe)

            drill_jobs = []

            for base, features in obj.Features:
                for center, by_radius in features_by_centers(base,
                                                             features).items():
                    radii = sorted(by_radius.keys(), reverse=True)
                    cylinders = map(
                        lambda radius: getattr(base.Shape, by_radius[radius]),
                        radii)
                    zsafe = max(cyl.BoundBox.ZMax
                                for cyl in cylinders) + obj.Clearance.Value
                    cur_z = cylinders[0].BoundBox.ZMax
                    jobs = []

                    for cylinder in cylinders:
                        # Find other edge of current cylinder
                        other_edge = None
                        for edge in cylinder.Edges:
                            if isinstance(
                                    edge.Curve,
                                    Circle) and edge.Curve.Center.z != cur_z:
                                other_edge = edge
                                break

                        next_z = other_edge.Curve.Center.z
                        dz = next_z - cur_z
                        r = cylinder.Surface.Radius

                        if dz < 0:
                            # This is a closed hole if the face connected to
                            # the current cylinder at next_z has the cylinder's
                            # edge as its OuterWire
                            closed = None
                            for face in base.Shape.Faces:
                                if connected(other_edge,
                                             face) and not face.isSame(
                                                 cylinder.Faces[0]):
                                    wire = face.OuterWire
                                    if len(wire.Edges) == 1 and wire.Edges[
                                            0].isSame(other_edge):
                                        closed = True
                                    else:
                                        closed = False

                            if closed is None:
                                raise Exception(
                                    "Cannot determine if this cylinder is closed on the z = {0} side"
                                    .format(next_z))

                            xc, yc, _ = cylinder.Surface.Center
                            jobs.append(
                                dict(xc=xc,
                                     yc=yc,
                                     zmin=next_z,
                                     zmax=cur_z,
                                     zsafe=zsafe,
                                     r_out=r,
                                     r_in=0.0,
                                     closed=closed))

                        elif dz > 0:
                            new_jobs = []
                            for job in jobs:
                                if job["zmin"] < next_z < job["zmax"]:
                                    # split this job
                                    job1 = dict(job)
                                    job2 = dict(job)
                                    job1["zmin"] = next_z
                                    job2["zmax"] = next_z
                                    job2["r_in"] = r
                                    new_jobs.append(job1)
                                    new_jobs.append(job2)
                                else:
                                    new_jobs.append(job)
                            jobs = new_jobs
                        else:
                            FreeCAD.Console.PrintError(
                                "PathHelix: Encountered cylinder with zero height\n"
                            )
                            break

                        cur_z = next_z

                    if obj.UseStartDepth:
                        jobs = [
                            job for job in jobs
                            if job["zmin"] < obj.StartDepth.Value
                        ]
                        if jobs:
                            jobs[0]["zmax"] = obj.StartDepth.Value
                    if obj.UseFinalDepth:
                        jobs = [
                            job for job in jobs
                            if job["zmax"] > obj.FinalDepth.Value
                        ]
                        if jobs:
                            jobs[-1]["zmin"] = obj.FinalDepth.Value
                    else:
                        if not jobs[-1]["closed"]:
                            jobs[-1]["zmin"] -= obj.ThroughDepth.Value

                    drill_jobs.extend(jobs)
            if len(drill_jobs) > 0:
                drill_jobs = PathUtils.sort_jobs(drill_jobs, ['xc', 'yc'])

            for job in drill_jobs:
                output += helix_cut(
                    (job["xc"], job["yc"]), job["r_out"], job["r_in"],
                    obj.DeltaR.Value, job["zmax"], job["zmin"],
                    obj.StepDown.Value, job["zsafe"], tool.Diameter,
                    toolLoad.VertFeed.Value, toolLoad.HorizFeed.Value,
                    obj.Direction, obj.StartSide)
                output += '\n'

        obj.Path = Path.Path(output)
        if obj.ViewObject:
            obj.ViewObject.Visibility = True
Exemplo n.º 11
0
    def execute(self, obj):
        PathLog.track()
        output = ""
        if obj.Comment != "":
            output += '(' + str(obj.Comment)+')\n'

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

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

            # Arch PanelSheet
            if hasattr(baseobject, "Proxy"):
                holes = []
                if isinstance(baseobject.Proxy, ArchPanel.PanelSheet):
                    baseobject.Proxy.execute(baseobject)
                    i = 0
                    holeshapes = baseobject.Proxy.getHoles(baseobject, transform=True)
                    tooldiameter = obj.ToolController.Proxy.getTool(obj.ToolController).Diameter
                    for holeshape in holeshapes:
                        PathLog.debug('Entering new HoleShape')
                        for wire in holeshape.Wires:
                            PathLog.debug('Entering new Wire')
                            for edge in wire.Edges:
                                if PathUtils.isDrillable(baseobject, edge, tooldiameter):
                                    PathLog.debug('Found drillable hole edges: {}'.format(edge))
                                    x = edge.Curve.Center.x
                                    y = edge.Curve.Center.y
                                    diameter = edge.BoundBox.XLength
                                    holes.append({'x': x, 'y': y, 'featureName': baseobject.Name+'.'+'Drill'+str(i), 'd': diameter})
                                    i = i + 1
            else:
                holes = self.findHoles(obj, baseobject.Shape)
                for i in range(len(holes)):
                    holes[i]['featureName'] = baseobject.Name + '.' + holes[i]['featureName']
            names = []
            positions = []
            enabled = []
            diameters = []
            for h in holes:
                if len(names) == 0:
                    self.findHeights(obj, baseobject, h)
                names.append(h['featureName'])
                positions.append(FreeCAD.Vector(h['x'], h['y'], 0))
                enabled.append(1)
                diameters.append(h['d'])
            obj.Names = names
            obj.Positions = positions
            obj.Enabled = enabled
            obj.Diameters = diameters

        locations = []
        output = "(Begin Drilling)\n"

        for i in range(len(obj.Names)):
            if obj.Enabled[i] > 0:
                locations.append({'x': obj.Positions[i].x, 'y': obj.Positions[i].y})
        if len(locations) > 0:
            locations = PathUtils.sort_jobs(locations, ['x', 'y'])
            output += "G90 G98\n"
            # rapid to clearance height
            output += "G0 Z" + str(obj.ClearanceHeight.Value) + "F " + PathUtils.fmt(self.vertRapid) + "\n"
            # rapid to first hole location, with spindle still retracted:

            p0 = locations[0]
            output += "G0 X" + fmt(p0['x']) + " Y" + fmt(p0['y']) + "F " + PathUtils.fmt(self.horizRapid) + "\n"
            # move tool to clearance plane
            output += "G0 Z" + fmt(obj.ClearanceHeight.Value) + "F " + PathUtils.fmt(self.vertRapid) + "\n"
            pword = ""
            qword = ""
            if obj.PeckDepth.Value > 0 and obj.PeckEnabled:
                cmd = "G83"
                qword = " Q" + fmt(obj.PeckDepth.Value)
            elif obj.DwellTime > 0 and obj.DwellEnabled:
                cmd = "G82"
                pword = " P" + fmt(obj.DwellTime)
            else:
                cmd = "G81"
            for p in locations:
                output += cmd + \
                    " X" + fmt(p['x']) + \
                    " Y" + fmt(p['y']) + \
                    " Z" + fmt(obj.FinalDepth.Value) + qword + pword + \
                    " R" + str(obj.RetractHeight.Value) + \
                    " F" + str(self.vertFeed) + "\n" \

            output += "G80\n"

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

        else:
            path = Path.Path("(inactive operation)")
            obj.Path = path
            obj.ViewObject.Visibility = False
Exemplo n.º 12
0
    def execute(self, obj):
        from Part import Circle, Cylinder, Plane
        from PathScripts import PathUtils
        from math import sqrt

        output = '(helix cut operation'
        if obj.Comment:
            output += ', ' + str(obj.Comment) + ')\n'
        else:
            output += ')\n'

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

            if not obj.ToolController:
                obj.ToolController = PathUtils.findToolController(obj)

            toolLoad = obj.ToolController

            if toolLoad is None or toolLoad.ToolNumber == 0:
                FreeCAD.Console.PrintError("PathHelix: No tool selected for helix cut operation, insert a tool change operation first\n")
                obj.Path = Path.Path("(ERROR: no tool selected for helix cut operation)")
                return

            tool = toolLoad.Proxy.getTool(toolLoad)

            zsafe = max(baseobj.Shape.BoundBox.ZMax for baseobj, features in obj.Features) + obj.Clearance.Value
            output += "G0 Z" + fmt(zsafe)

            drill_jobs = []

            for base, features in obj.Features:
                for center, by_radius in features_by_centers(base, features).items():
                    radii = sorted(by_radius.keys(), reverse=True)
                    cylinders = map(lambda radius: getattr(base.Shape, by_radius[radius]), radii)
                    zsafe = max(cyl.BoundBox.ZMax for cyl in cylinders) + obj.Clearance.Value
                    cur_z = cylinders[0].BoundBox.ZMax
                    jobs = []

                    for cylinder in cylinders:
                        # Find other edge of current cylinder
                        other_edge = None
                        for edge in cylinder.Edges:
                            if isinstance(edge.Curve, Circle) and edge.Curve.Center.z != cur_z:
                                other_edge = edge
                                break

                        next_z = other_edge.Curve.Center.z
                        dz = next_z - cur_z
                        r = cylinder.Surface.Radius

                        if dz < 0:
                            # This is a closed hole if the face connected to
                            # the current cylinder at next_z has the cylinder's
                            # edge as its OuterWire
                            closed = None
                            for face in base.Shape.Faces:
                                if connected(other_edge, face) and not face.isSame(cylinder.Faces[0]):
                                    wire = face.OuterWire
                                    if len(wire.Edges) == 1 and wire.Edges[0].isSame(other_edge):
                                        closed = True
                                    else:
                                        closed = False

                            if closed is None:
                                raise Exception("Cannot determine if this cylinder is closed on the z = {0} side".format(next_z))

                            xc, yc, _ = cylinder.Surface.Center
                            jobs.append(dict(xc=xc, yc=yc,
                                             zmin=next_z, zmax=cur_z, zsafe=zsafe,
                                             r_out=r, r_in=0.0, closed=closed))

                        elif dz > 0:
                            new_jobs = []
                            for job in jobs:
                                if job["zmin"] < next_z < job["zmax"]:
                                    # split this job
                                    job1 = dict(job)
                                    job2 = dict(job)
                                    job1["zmin"] = next_z
                                    job2["zmax"] = next_z
                                    job2["r_in"] = r
                                    new_jobs.append(job1)
                                    new_jobs.append(job2)
                                else:
                                    new_jobs.append(job)
                            jobs = new_jobs
                        else:
                            FreeCAD.Console.PrintError("PathHelix: Encountered cylinder with zero height\n")
                            break

                        cur_z = next_z

                    if obj.UseStartDepth:
                        jobs = [job for job in jobs if job["zmin"] < obj.StartDepth.Value]
                        if jobs:
                            jobs[0]["zmax"] = obj.StartDepth.Value
                    if obj.UseFinalDepth:
                        jobs = [job for job in jobs if job["zmax"] > obj.FinalDepth.Value]
                        if jobs:
                            jobs[-1]["zmin"] = obj.FinalDepth.Value
                    else:
                        if not jobs[-1]["closed"]:
                            jobs[-1]["zmin"] -= obj.ThroughDepth.Value

                    drill_jobs.extend(jobs)

            if len(drill_jobs) > 0:
                drill_jobs = PathUtils.sort_jobs(drill_jobs, ['xc', 'yc'], ['xc', 'zmax'])

            for job in drill_jobs:
                output += helix_cut((job["xc"], job["yc"]), job["r_out"], job["r_in"], obj.DeltaR.Value,
                                    job["zmax"], job["zmin"], obj.StepDown.Value,
                                    job["zsafe"], tool.Diameter,
                                    toolLoad.VertFeed.Value, toolLoad.HorizFeed.Value,
                                    obj.Direction, obj.StartSide)
                output += '\n'

        obj.Path = Path.Path(output)
        if obj.ViewObject:
            obj.ViewObject.Visibility = True
Exemplo n.º 13
0
    def opExecute(self, obj, getsim=False):
        '''opExecute(obj, getsim=False) ... implementation of Path.Area ops.
        determines the parameters for _buildPathArea().
        Do not overwrite, implement
            areaOpAreaParams(obj, isHole) ... op specific area param dictionary
            areaOpPathParams(obj, isHole) ... op specific path param dictionary
            areaOpShapes(obj)             ... the shape for path area to process
            areaOpUseProjection(obj)      ... return true if operation can use projection
        instead.'''
        PathLog.track()
        self.endVector = None
        PathLog.debug("opExecute() in PathAreaOp.py")

        # Instantiate class variables for operation reference
        self.rotateFlag = False
        self.modelName = None
        self.leadIn = 2.0  # safOfset / 2.0

        # Initialize depthparams
        finish_step = obj.FinishDepth.Value if hasattr(obj,
                                                       "FinishDepth") else 0.0
        self.depthparams = PathUtils.depth_params(
            clearance_height=obj.ClearanceHeight.Value,
            safe_height=obj.SafeHeight.Value,
            start_depth=obj.StartDepth.Value,
            step_down=obj.StepDown.Value,
            z_finish_step=finish_step,
            final_depth=obj.FinalDepth.Value,
            user_depths=None)

        # Recalculate operation heights for rotational operation
        if obj.UseRotation != 'Off':
            # Calculate operation heights based upon rotation radii
            opHeights = self.opDetermineRotationRadii(
                obj
            )  # return is [(xRotRad, yRotRad, zRotRad), (clrOfst, safOfst)]
            (xRotRad, yRotRad, zRotRad) = opHeights[0]
            (clrOfst, safOfset) = opHeights[1]
            # self.leadIn = 0.0 #safOfset / 2.0

            # Set clearnance and safe heights based upon rotation radii
            obj.ClearanceHeight.Value = xRotRad + clrOfst
            obj.SafeHeight.Value = xRotRad + safOfset
            if yRotRad > xRotRad:
                obj.ClearanceHeight.Value = yRotRad + clrOfst
                obj.SafeHeight.Value = yRotRad + safOfset

            # Set axial feed rates based upon horizontal feed rates
            safeCircum = 2 * math.pi * obj.SafeHeight.Value
            self.axialFeed = 360 / safeCircum * self.horizFeed
            self.axialRapid = 360 / safeCircum * self.horizRapid

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

        aOS = self.areaOpShapes(
            obj)  # list of tuples (shape, isHole, sub, angle, axis, tag)

        # Adjust tuples length received from other PathWB tools/operations beside PathPocketShape
        shapes = []
        for shp in aOS:
            if len(shp) == 2:
                (fc, iH) = shp
                tup = fc, iH, 'notPocket', 0.0, 'X'
                shapes.append(tup)
            else:
                shapes.append(shp)

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

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

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

        sims = []
        for (shape, isHole, sub, angle, axis) in shapes:
            startDep = obj.StartDepth.Value  # + safOfset
            safeDep = obj.SafeHeight.Value
            clearDep = obj.ClearanceHeight.Value
            finalDep = obj.FinalDepth.Value  # finDep

            finish_step = obj.FinishDepth.Value if hasattr(
                obj, "FinishDepth") else 0.0
            self.depthparams = PathUtils.depth_params(
                clearance_height=clearDep,  # obj.ClearanceHeight.Value
                safe_height=safeDep,  # obj.SafeHeight.Value
                start_depth=startDep,
                step_down=obj.StepDown.Value,
                z_finish_step=finish_step,  # obj.FinalDepth.Value
                final_depth=finalDep,
                user_depths=None)

            try:
                (pp, sim) = self._buildPathArea(obj, shape, isHole, start,
                                                getsim)
                ppCmds = pp.Commands
                if obj.UseRotation != 'Off' and self.rotateFlag is True:
                    # Rotate model to index for cut
                    axisOfRot = 'A'
                    if 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
                    # Rotate Model to correct angle
                    ppCmds.insert(
                        0,
                        Path.Command('G0', {
                            axisOfRot: angle,
                            'F': self.axialFeed
                        }))
                    # Raise cutter to safe depth and return index to starting position
                    ppCmds.append(
                        Path.Command('G0', {
                            'Z': safeDep,
                            'F': self.vertRapid
                        }))
                    ppCmds.append(
                        Path.Command('G0', {
                            axisOfRot: 0.0,
                            'F': self.axialFeed
                        }))
                # Save gcode commands to object command list
                self.commandlist.extend(ppCmds)
                sims.append(sim)
            except Exception as e:
                FreeCAD.Console.PrintError(e)
                FreeCAD.Console.PrintError(
                    "Something unexpected happened. Check project and tool config."
                )

            if self.areaOpRetractTool(obj):
                self.endVector = None

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

        PathLog.debug("obj.Name: " + str(obj.Name))
        return sims