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))
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
Esempio n. 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
Esempio n. 5
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)
Esempio n. 6
0
import FreeCAD
import FreeCADGui
import PathScripts.PathCircularHoleBaseGui as PathCircularHoleBaseGui
import PathScripts.PathDrilling as PathDrilling
import PathScripts.PathLog as PathLog
import PathScripts.PathOpGui as PathOpGui

from PySide import QtCore, QtGui

__title__ = "Path Drilling Operation UI."
__author__ = "sliptonic (Brad Collette)"
__url__ = "http://www.freecadweb.org"
__doc__ = "UI and Command for Path Drilling Operation."

if True:
    PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
    PathLog.trackModule(PathLog.thisModule())
else:
    PathLog.setLevel(PathLog.Level.NOTICE, PathLog.thisModule())


class TaskPanelOpPage(PathCircularHoleBaseGui.TaskPanelOpPage):
    '''Controller for the drilling operation's page'''
    def getForm(self):
        '''getForm() ... return UI'''
        return FreeCADGui.PySideUic.loadUi(":/panels/PageOpDrillingEdit.ui")

    def getFields(self, obj):
        '''setFields(obj) ... update obj's properties with values from the UI'''
        PathLog.track()
        self.updateInputField(obj, 'PeckDepth', self.form.peckDepth)
Esempio n. 7
0
import Path
import PathScripts.PathGeom as PathGeom
import PathScripts.PathLog as PathLog
import PathScripts.PathUtils as PathUtils

from PySide import QtCore, QtGui

# lazily loaded modules
from lazy_loader.lazy_loader import LazyLoader
Part = LazyLoader('Part', globals(), 'Part')
"""Z Depth Correction Dressup.  This dressup takes a probe file as input and does bilinear interpolation of the Zdepths to correct for a surface which is not parallel to the milling table/bed.  The probe file should conform to the format specified by the linuxcnc G38 probe logging: 9-number coordinate consisting of XYZABCUVW http://linuxcnc.org/docs/html/gcode/g-code.html#gcode:g38
"""

LOGLEVEL = False

LOG_MODULE = PathLog.thisModule()

if LOGLEVEL:
    PathLog.setLevel(PathLog.Level.DEBUG, LOG_MODULE)
    PathLog.trackModule(PathLog.thisModule())
else:
    PathLog.setLevel(PathLog.Level.NOTICE, LOG_MODULE)


# Qt translation handling
def translate(context, text, disambig=None):
    return QtCore.QCoreApplication.translate(context, text, disambig)


movecommands = ['G1', 'G01', 'G2', 'G02', 'G3', 'G03']
rapidcommands = ['G0', 'G00']
Esempio n. 8
0
def addDebugDisplay():
    return PathLog.getLevel(PathLog.thisModule()) == PathLog.Level.DEBUG
Esempio n. 9
0
import Part
import Path
import PathScripts
import PathScripts.PathLog as PathLog
import PathScripts.PathToolEdit as PathToolEdit
import PathScripts.PathUtil as PathUtil

from FreeCAD import Units
from PySide import QtCore

if FreeCAD.GuiUp:
    import FreeCADGui
    import PathScripts.PathGui as PathGui

if False:
    PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
    PathLog.trackModule(PathLog.thisModule())
else:
    PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())

# Qt tanslation handling
def translate(context, text, disambig=None):
    return QtCore.QCoreApplication.translate(context, text, disambig)

class ToolControllerTemplate:
    '''Attribute and sub element strings for template export/import.'''
    Expressions  = 'xengine'
    ExprExpr     = 'expr'
    ExprProp     = 'prop'
    HorizFeed    = 'hfeed'
    HorizRapid   = 'hrapid'
Esempio n. 10
0
import FreeCAD
import Path
import PathScripts.PathLog as PathLog
import PathScripts.PathOp as PathOp
import PathScripts.PathUtils as PathUtils

from PathScripts.PathUtils import waiting_effects
from PySide import QtCore

__title__ = "Base class for PathArea based operations."
__author__ = "sliptonic (Brad Collette)"
__url__ = "http://www.freecadweb.org"
__doc__ = "Base class and properties for Path.Area based operations."

if False:
    PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
    PathLog.trackModule()
else:
    PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())

# Qt tanslation handling
def translate(context, text, disambig=None):
    return QtCore.QCoreApplication.translate(context, text, disambig)

class ObjectOp(PathOp.ObjectOp):
    '''Base class for all Path.Area based operations.
    Provides standard features including debugging properties AreaParams,
    PathParams and removalshape, all hidden.
    The main reason for existence is to implement the standard interface
    to Path.Area so subclasses only have to provide the shapes for the
    operations.'''
Esempio n. 11
0
import PathScripts.PathLog as PathLog
import traceback

from MKObserverable import *
from MKService import *

from machinetalk.protobuf.status_pb2 import *
from machinetalk.protobuf.types_pb2 import MT_EMCSTAT_FULL_UPDATE

PathLog.setLevel(PathLog.Level.NOTICE, PathLog.thisModule())
#PathLog.trackModule(PathLog.thisModule())


class MKServiceContainer(MKObserverable):
    def __init__(self, valid):
        super().__init__()
        self.valid = valid

    def __getitem__(self, path):
        if self.valid:
            try:
                attr = self.__getattribute__(path[0])
                return returnAttribute(attr, path[1:])
            except:
                PathLog.error("unknown container path = %s (%s)" %
                              (path, self))
                traceback.print_stack()
                raise
        return None

Esempio n. 12
0
def addDebugDisplay():
    return PathLog.getLevel(PathLog.thisModule()) == PathLog.Level.DEBUG
Esempio n. 13
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
Esempio n. 14
0
# *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
# *   GNU Library General Public License for more details.                  *
# *                                                                         *
# *   You should have received a copy of the GNU Library General Public     *
# *   License along with this program; if not, write to the Free Software   *
# *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
# *   USA                                                                   *
# *                                                                         *
# ***************************************************************************

import FreeCAD
import PathScripts.PathSetupSheet as PathSetupSheet
import PathScripts.PathLog as PathLog
import sys

PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())

from PathTests.PathTestUtils import PathTestBase


def refstring(string):
    if sys.version_info.major < 3:
        return string
    return string.replace(" u'", " '")


class TestPathSetupSheet(PathTestBase):
    def setUp(self):
        self.doc = FreeCAD.newDocument("TestPathSetupSheet")

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        self.useTempJobClones('Delete')  # Delete temp job clone group and contents
        self.guiMessage('title', None, show=True)  # Process GUI messages to user
        for ton in self.tempObjectNames:  # remove temporary objects by name
            FreeCAD.ActiveDocument.removeObject(ton)
        PathLog.debug("obj.Name: " + str(obj.Name) + "\n\n")
        return sims
Esempio n. 17
0
open a gcode file, parse its contents, and create the appropriate objects
in FreeCAD. This preprocessor will not add imported gcode to an existing
job. For a more useful preprocessor, look at the gcode_pre.py file

Read the Path Workbench documentation to know how to create Path objects
from GCode.
'''

import os
import Path
import FreeCAD
import PathScripts.PathLog as PathLog

# LEVEL = PathLog.Level.DEBUG
LEVEL = PathLog.Level.INFO
PathLog.setLevel(LEVEL, PathLog.thisModule())

if LEVEL == PathLog.Level.DEBUG:
    PathLog.trackModule(PathLog.thisModule())


# to distinguish python built-in open function from the one declared below
if open.__module__ in ['__builtin__', 'io']:
    pythonopen = open


def open(filename):
    "called when freecad opens a file."
    PathLog.track(filename)
    docname = os.path.splitext(os.path.basename(filename))[0]
    doc = FreeCAD.newDocument(docname)
Esempio n. 18
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
Esempio n. 19
0
# *   USA                                                                   *
# *                                                                         *
# ***************************************************************************

import FreeCAD
import Part
import Path
import PathScripts.PathLog as PathLog
import PathScripts.PathOp as PathOp
import PathScripts.PathProfileBase as PathProfileBase
import PathScripts.PathUtils as PathUtils

from DraftGeomUtils import findWires
from PySide import QtCore

PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())

#PathLog.trackModule(PathLog.thisModule())


# Qt translation handling
def translate(context, text, disambig=None):
    return QtCore.QCoreApplication.translate(context, text, disambig)


__title__ = "Path Profile Edges Operation"
__author__ = "sliptonic (Brad Collette)"
__url__ = "http://www.freecadweb.org"
__doc__ = "Path Profile operation based on edges."

Esempio n. 20
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])
Esempio n. 21
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
Esempio n. 22
0
    def areaOpShapes(self, obj):
        '''areaOpShapes(obj) ... returns envelope for all wires formed by the base edges.'''
        PathLog.track()

        inaccessible = translate(
            'PathProfileEdges',
            'The selected edge(s) are inaccessible. If multiple, re-ordering selection might work.'
        )
        if PathLog.getLevel(PathLog.thisModule()) == 4:
            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.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(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
                            flattened = self._flattenWire(
                                obj, wire, obj.FinalDepth.Value)
                            if flattened:
                                (origWire, flatWire) = flattened
                                if PathLog.getLevel(PathLog.thisModule()) == 4:
                                    os = FreeCAD.ActiveDocument.addObject(
                                        'Part::Feature', 'tmpFlatWire')
                                    os.Shape = flatWire
                                    os.purgeTouched()
                                    self.tmpGrp.addObject(os)
                                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(inaccessible)
                            else:
                                PathLog.error(inaccessible)

            # Delete the temporary objects
            if PathLog.getLevel(PathLog.thisModule()) == 4:
                if FreeCAD.GuiUp:
                    import FreeCADGui
                    FreeCADGui.ActiveDocument.getObject(
                        tmpGrpNm).Visibility = False
                self.tmpGrp.purgeTouched()

        return shapes
Esempio n. 23
0
import Draft
import math
import Part

# from PathScripts.PathUtils import waiting_effects
from PySide import QtCore
if FreeCAD.GuiUp:
    import FreeCADGui

__title__ = "Base class for PathArea based operations."
__author__ = "sliptonic (Brad Collette)"
__url__ = "http://www.freecadweb.org"
__doc__ = "Base class and properties for Path.Area based operations."

LOGLEVEL = PathLog.Level.INFO
PathLog.setLevel(LOGLEVEL, PathLog.thisModule())

if LOGLEVEL is PathLog.Level.DEBUG:
    PathLog.trackModule()

# Qt translation handling
def translate(context, text, disambig=None):
    return QtCore.QCoreApplication.translate(context, text, disambig)


class ObjectOp(PathOp.ObjectOp):
    '''Base class for all Path.Area based operations.
    Provides standard features including debugging properties AreaParams,
    PathParams and removalshape, all hidden.
    The main reason for existence is to implement the standard interface
    to Path.Area so subclasses only have to provide the shapes for the
Esempio n. 24
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
Esempio n. 25
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.profileEdgesIsOpen = 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 clearance and safe heights based upon rotation radii
            if obj.EnableRotation == 'A(x)':
                strDep = self.xRotRad
            elif obj.EnableRotation == 'B(y)':
                strDep = self.yRotRad
            else:
                strDep = max(self.xRotRad, self.yRotRad)
            finDep = -1 * strDep

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

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

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

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

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

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

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

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

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

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

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

        if self.profileEdgesIsOpen is 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}))

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

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

            try:
                if self.profileEdgesIsOpen is True:
                    (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 self.profileEdgesIsOpen is True:
                    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'
                        # Reverse angle temporarily to match model. Error in FreeCAD render of B axis rotations
                        if obj.B_AxisErrorOverride is True:
                            angle = -1 * angle
                    elif axis == 'Z':
                        axisOfRot = 'C'
                    else:
                        axisOfRot = 'A'
                    # Rotate Model to correct angle
                    ppCmds.insert(0, Path.Command('G1', {axisOfRot: angle, 'F': self.axialFeed}))
                    ppCmds.insert(0, Path.Command('N100', {}))

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

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

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

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

        self.useTempJobClones('Delete')  # Delete temp job clone group and contents
        self.guiMessage('title', None, show=True)  # Process GUI messages to user
        for ton in self.tempObjectNames:  # remove temporary objects by name
            FreeCAD.ActiveDocument.removeObject(ton)
        PathLog.debug("obj.Name: " + str(obj.Name) + "\n\n")
        return sims
Esempio n. 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
Esempio n. 27
0
import FreeCAD
import FreeCADGui
import math
import Part
import Path
import PathScripts.PathDressup as PathDressup
import PathScripts.PathLog as PathLog
import PathScripts.PathUtil as PathUtil
import PathScripts.PathUtils as PathUtils

from PathScripts.PathGeom import PathGeom
from PySide import QtCore, QtGui

"""Dogbone Dressup object and FreeCAD command"""

LOG_MODULE = PathLog.thisModule()

if False:
    PathLog.setLevel(PathLog.Level.DEBUG, LOG_MODULE)


# Qt tanslation handling
def translate(context, text, disambig=None):
    return QtCore.QCoreApplication.translate(context, text, disambig)

movecommands = ['G0', 'G00', 'G1', 'G01', 'G2', 'G02', 'G3', 'G03']
movestraight = ['G1', 'G01']
movecw = ['G2', 'G02']
moveccw = ['G3', 'G03']
movearc = movecw + moveccw
Esempio n. 28
0
import FreeCAD
import FreeCADGui
import Path
import PathScripts.PathJob as PathJob
import PathScripts.PathLog as PathLog
import PathScripts.PathPreferences as PathPreferences
import PathScripts.PathUtil as PathUtil
import PathScripts.PathUtils as PathUtils
import os

from PathScripts.PathPostProcessor import PostProcessor
from PySide import QtCore, QtGui
from datetime import datetime

LOG_MODULE = PathLog.thisModule()

PathLog.setLevel(PathLog.Level.INFO, LOG_MODULE)


# Qt translation handling
def translate(context, text, disambig=None):
    return QtCore.QCoreApplication.translate(context, text, disambig)


class _TempObject:
    # pylint: disable=no-init
    Path = None
    Name = "Fixture"
    InList = []
    Label = "Fixture"
Esempio n. 29
0
# *                                                                         *
# ***************************************************************************

import FreeCAD
import FreeCADGui
import PathScripts.PathCircularHoleBaseGui as PathCircularHoleBaseGui
import PathScripts.PathHelix as PathHelix
import PathScripts.PathLog as PathLog
import PathScripts.PathOpGui as PathOpGui

from PySide import QtCore, QtGui

__doc__ = "Helix operation page controller and command implementation."

if True:
    PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
    PathLog.trackModule(PathLog.thisModule())
else:
    PathLog.setLevel(PathLog.Level.NOTICE, PathLog.thisModule())


class TaskPanelOpPage(PathCircularHoleBaseGui.TaskPanelOpPage):
    '''Page controller class for Helix operations.'''

    def getForm(self):
        '''getForm() ... return UI'''
        return FreeCADGui.PySideUic.loadUi(":/panels/PageOpHelixEdit.ui")

    def getFields(self, obj):
        '''getFields(obj) ... transfers values from UI to obj's proprties'''
        PathLog.track()
Esempio n. 30
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))