def test30(self):
        """Verify proper geometry for arcs with rising and fall ing Z-axis are created."""
        #print("------ rising helix -------")
        p1 = Vector(0, 1, 0)
        p2 = Vector(1, 0, 2)
        self.assertCurve(
                PathGeom.edgeForCmd(
                    Path.Command('G2', {'X': p2.x, 'Y': p2.y, 'Z': p2.z, 'I': 0, 'J': -1, 'K': 1}), p1),
                p1, Vector(1/math.sqrt(2), 1/math.sqrt(2), 1), p2)
        p1 = Vector(-1, 0, 0)
        p2 = Vector(0, -1, 2)
        self.assertCurve(
                PathGeom.edgeForCmd(
                    Path.Command('G3', {'X': p2.x, 'Y': p2.y, 'Z': p2.z, 'I': 1, 'J': 0, 'K': 1}), p1),
                p1, Vector(-1/math.sqrt(2), -1/math.sqrt(2), 1), p2)

        #print("------ falling helix -------")
        p1 = Vector(0, -1, 2)
        p2 = Vector(-1, 0, 0)
        self.assertCurve(
                PathGeom.edgeForCmd(
                    Path.Command('G2', {'X': p2.x, 'Y': p2.y, 'Z': p2.z, 'I': 0, 'J': 1, 'K': -1}), p1),
                p1, Vector(-1/math.sqrt(2), -1/math.sqrt(2), 1), p2)
        p1 = Vector(-1, 0, 2)
        p2 = Vector(0, -1, 0)
        self.assertCurve(
                PathGeom.edgeForCmd(
                    Path.Command('G3', {'X': p2.x, 'Y': p2.y, 'Z': p2.z, 'I': 1, 'J': 0, 'K': -1}), p1),
                p1, Vector(-1/math.sqrt(2), -1/math.sqrt(2), 1), p2)
 def commandsForEdges(self):
     if self.edges:
         try:
             shape = self.shell().common(self.tag.solid)
             commands = []
             rapid = None
             for e, flip in self.orderAndFlipEdges(self.cleanupEdges(shape.Edges)):
                 debugEdge(e, '++++++++ %s' % ('<' if flip else '>'), False)
                 p1 = e.valueAt(e.FirstParameter)
                 p2 = e.valueAt(e.LastParameter)
                 if self.tag.isSquare and (PathGeom.isRoughly(p1.z, self.maxZ) or p1.z > self.maxZ) and (PathGeom.isRoughly(p2.z, self.maxZ) or p2.z > self.maxZ):
                     rapid = p1 if flip else p2
                 else:
                     if rapid:
                         commands.append(Path.Command('G0', {'X': rapid.x, 'Y': rapid.y, 'Z': rapid.z}))
                         rapid = None
                     commands.extend(PathGeom.cmdsForEdge(e, flip, False, self.segm))
             if rapid:
                 commands.append(Path.Command('G0', {'X': rapid.x, 'Y': rapid.y, 'Z': rapid.z}))
                 rapid = None
             return commands
         except Exception as e:
             PathLog.error("Exception during processing tag @(%.2f, %.2f) (%s) - disabling the tag" % (self.tag.x, self.tag.y, e.args[0]))
             self.tag.enabled = False
             commands = []
             for e in self.edges:
                 commands.extend(PathGeom.cmdsForEdge(e))
             return commands
     return []
    def test65(self):
        """Verify splitEdgeAt."""
        e = PathGeom.splitEdgeAt(Part.Edge(Part.LineSegment(Vector(), Vector(2, 4, 6))), Vector(1, 2, 3))
        self.assertLine(e[0], Vector(), Vector(1,2,3))
        self.assertLine(e[1], Vector(1,2,3), Vector(2,4,6))

        # split an arc
        p1 = Vector(10,-10,1)
        p2 = Vector(0,0,1)
        p3 = Vector(10,10,1)
        arc = Part.Edge(Part.Arc(p1, p2, p3))
        e = PathGeom.splitEdgeAt(arc, p2)
        o = 10*math.sin(math.pi/4)
        p12 = Vector(10 - o, -o, 1)
        p23 = Vector(10 - o, +o, 1)
        self.assertCurve(e[0], p1, p12, p2)
        self.assertCurve(e[1], p2, p23, p3)


        # split a helix
        p1 = Vector(10,-10,0)
        p2 = Vector(0,0,5)
        p3 = Vector(10,10,10)
        h = PathGeom.arcToHelix(arc, 0, 10)
        self.assertCurve(h, p1, p2, p3)

        e = PathGeom.splitEdgeAt(h, p2)
        o = 10*math.sin(math.pi/4)
        p12 = Vector(10 - o, -o, 2.5)
        p23 = Vector(10 - o, +o, 7.5)
        pf = e[0].valueAt((e[0].FirstParameter + e[0].LastParameter)/2)
        pl = e[1].valueAt((e[1].FirstParameter + e[1].LastParameter)/2)
        self.assertCurve(e[0], p1, p12, p2)
        self.assertCurve(e[1], p2, p23, p3)
 def __init__(self, edge, tag, i, segm, maxZ):
     debugEdge(edge, 'MapWireToTag(%.2f, %.2f, %.2f)' % (i.x, i.y, i.z))
     self.tag = tag
     self.segm = segm
     self.maxZ = maxZ
     if PathGeom.pointsCoincide(edge.valueAt(edge.FirstParameter), i):
         tail = edge
         self.commands = []
         debugEdge(tail, '.........=')
     elif PathGeom.pointsCoincide(edge.valueAt(edge.LastParameter), i):
         debugEdge(edge, '++++++++ .')
         self.commands = PathGeom.cmdsForEdge(edge, segm=segm)
         tail = None
     else:
         e, tail = PathGeom.splitEdgeAt(edge, i)
         debugEdge(e, '++++++++ .')
         self.commands = PathGeom.cmdsForEdge(e, segm=segm)
         debugEdge(tail, '.........-')
         self.initialEdge = edge
     self.tail = tail
     self.edges = []
     self.entry = i
     if tail:
         PathLog.debug("MapWireToTag(%s - %s)" % (i, tail.valueAt(tail.FirstParameter)))
     else:
         PathLog.debug("MapWireToTag(%s - )" % i)
     self.complete = False
     self.haveProblem = False
Exemple #5
0
def horizontalEdgeLoop(obj, edge):
    '''horizontalEdgeLoop(obj, edge) ... returns a wire in the horizontal plane, if that is the only horizontal wire the given edge is a part of.'''
    h = edge.hashCode()
    wires = [w for w in obj.Shape.Wires if any(e.hashCode() == h for e in w.Edges)]
    loops = [w for w in wires if all(PathGeom.isHorizontal(e) for e in w.Edges) and PathGeom.isHorizontal(Part.Face(w))]
    if len(loops) == 1:
        return loops[0]
    return None
 def isValidTagStartIntersection(self, edge, i):
     if PathGeom.pointsCoincide(i, edge.valueAt(edge.LastParameter)):
         return False
     p1 = edge.valueAt(edge.FirstParameter)
     p2 = edge.valueAt(edge.LastParameter)
     if PathGeom.pointsCoincide(PathGeom.xy(p1), PathGeom.xy(p2)):
         # if this vertical goes up, it can't be the start of a tag intersection
         if p1.z < p2.z:
             return False
     return True
 def isRapid(self, edge):
     if type(edge.Curve) == Part.Line or type(edge.Curve) == Part.LineSegment:
         v0 = edge.Vertexes[0]
         v1 = edge.Vertexes[1]
         for r in self.rapid:
             r0 = r.Vertexes[0]
             r1 = r.Vertexes[1]
             if PathGeom.isRoughly(r0.X, v0.X) and PathGeom.isRoughly(r0.Y, v0.Y) and PathGeom.isRoughly(r0.Z, v0.Z) and PathGeom.isRoughly(r1.X, v1.X) and PathGeom.isRoughly(r1.Y, v1.Y) and PathGeom.isRoughly(r1.Z, v1.Z):
                 return True
     return False
    def generateHelix(self):
        edges = self.wire.Edges
        minZ = self.findMinZ(edges)
        outedges = []
        i = 0
        while i < len(edges):
            edge = edges[i]
            israpid = False
            for redge in self.rapids:
                if PathGeom.edgesMatch(edge, redge):
                    israpid = True
            if not israpid:
                bb = edge.BoundBox
                p0 = edge.Vertexes[0].Point
                p1 = edge.Vertexes[1].Point
                if bb.XLength < 1e-6 and bb.YLength < 1e-6 and bb.ZLength > 0 and p0.z > p1.z:
                    # plungelen = abs(p0.z-p1.z)
                    PathLog.debug("Found plunge move at X:{} Y:{} From Z:{} to Z{}, Searching for closed loop".format(p0.x, p0.y, p0.z, p1.z))
                    # next need to determine how many edges in the path after plunge are needed to cover the length:
                    loopFound = False
                    rampedges = []
                    j = i + 1
                    while not loopFound:
                        candidate = edges[j]
                        cp0 = candidate.Vertexes[0].Point
                        cp1 = candidate.Vertexes[1].Point
                        if PathGeom.pointsCoincide(p1, cp1):
                            # found closed loop
                            loopFound = True
                            rampedges.append(candidate)
                            break
                        if abs(cp0.z - cp1.z) > 1e-6:
                            # this edge is not parallel to XY plane, not qualified for ramping.
                            break
                        # PathLog.debug("Next edge length {}".format(candidate.Length))
                        rampedges.append(candidate)
                        j = j + 1
                        if j >= len(edges):
                            break
                    if len(rampedges) == 0 or not loopFound:
                        PathLog.debug("No suitable helix found")
                        outedges.append(edge)
                    else:
                        outedges.extend(self.createHelix(rampedges, p0, p1))
                        if not PathGeom.isRoughly(p1.z, minZ):
                            # the edges covered by the helix not handled again,
                            # unless reached the bottom height
                            i = j

                else:
                    outedges.append(edge)
            else:
                outedges.append(edge)
            i = i + 1
        return outedges
Exemple #9
0
    def updateDepths(self, obj, ignoreErrors=False):
        '''updateDepths(obj) ... base implementation calculating depths depending on base geometry.
        Should not be overwritten.'''

        def faceZmin(bb, fbb):
            if fbb.ZMax == fbb.ZMin and fbb.ZMax == bb.ZMax:  # top face
                return fbb.ZMin
            elif fbb.ZMax > fbb.ZMin and fbb.ZMax == bb.ZMax: # vertical face, full cut
                return fbb.ZMin
            elif fbb.ZMax > fbb.ZMin and fbb.ZMin > bb.ZMin:  # internal vertical wall
                return fbb.ZMin
            elif fbb.ZMax == fbb.ZMin and fbb.ZMax > bb.ZMin: # face/shelf
                return fbb.ZMin
            return bb.ZMin

        if not self._setBaseAndStock(obj, ignoreErrors):
            return False

        stockBB = self.stock.Shape.BoundBox
        zmin = stockBB.ZMin
        zmax = stockBB.ZMax

        if hasattr(obj, 'Base') and obj.Base:
            for base, sublist in obj.Base:
                bb = base.Shape.BoundBox
                zmax = max(zmax, bb.ZMax)
                for sub in sublist:
                    fbb = base.Shape.getElement(sub).BoundBox
                    zmin = max(zmin, faceZmin(bb, fbb))
                    zmax = max(zmax, fbb.ZMax)
        else:
            # clearing with stock boundaries
            job = PathUtils.findParentJob(obj)
            zmax = stockBB.ZMax
            zmin = job.Base.Shape.BoundBox.ZMax

        if FeatureDepths & self.opFeatures(obj):
            # first set update final depth, it's value is not negotiable
            if not PathGeom.isRoughly(obj.OpFinalDepth.Value, zmin):
                obj.OpFinalDepth = zmin
            zmin = obj.OpFinalDepth.Value

            def minZmax(z):
                if hasattr(obj, 'StepDown') and not PathGeom.isRoughly(obj.StepDown.Value, 0):
                    return z + obj.StepDown.Value
                else:
                    return z + 1

            # ensure zmax is higher than zmin
            if (zmax - 0.0001) <= zmin:
                zmax = minZmax(zmin)

            # update start depth if requested and required
            if not PathGeom.isRoughly(obj.OpStartDepth.Value, zmax):
                obj.OpStartDepth = zmax
 def isEntryOrExitStrut(self, e):
     p1 = e.valueAt(e.FirstParameter)
     p2 = e.valueAt(e.LastParameter)
     if PathGeom.pointsCoincide(p1, self.entry) and p2.z >= self.entry.z:
         return 1
     if PathGeom.pointsCoincide(p2, self.entry) and p1.z >= self.entry.z:
         return 1
     if PathGeom.pointsCoincide(p1, self.exit) and p2.z >= self.exit.z:
         return 2
     if PathGeom.pointsCoincide(p2, self.exit) and p1.z >= self.exit.z:
         return 2
     return 0
Exemple #11
0
 def test20(self):
     """Verify proper geometry for arcs in the XY-plane are created."""
     p1 = Vector(0, -1, 2)
     p2 = Vector(-1, 0, 2)
     self.assertArc(
             PathGeom.edgeForCmd(
                 Path.Command('G2', {'X': p2.x, 'Y': p2.y, 'Z': p2.z, 'I': 0, 'J': 1, 'K': 0}), p1),
             p1, p2, 'CW')
     self.assertArc(
             PathGeom.edgeForCmd(
                 Path.Command('G3', {'X': p1.x, 'Y': p1.y, 'z': p1.z, 'I': -1, 'J': 0, 'K': 0}), p2),
             p2, p1, 'CCW')
 def isPointOnEdge(self, pt, edge):
     param = edge.Curve.parameter(pt)
     if edge.FirstParameter <= param <= edge.LastParameter:
         return True
     if edge.LastParameter <= param <= edge.FirstParameter:
         return True
     if PathGeom.isRoughly(edge.FirstParameter, param) or PathGeom.isRoughly(edge.LastParameter, param):
         return True
     # print("-------- X %.2f <= %.2f <=%.2f   (%.2f, %.2f, %.2f)   %.2f:%.2f" % (edge.FirstParameter, param, edge.LastParameter, pt.x, pt.y, pt.z, edge.Curve.parameter(edge.valueAt(edge.FirstParameter)), edge.Curve.parameter(edge.valueAt(edge.LastParameter))))
     # p1 = edge.Vertexes[0]
     # f1 = edge.Curve.parameter(FreeCAD.Vector(p1.X, p1.Y, p1.Z))
     # p2 = edge.Vertexes[1]
     # f2 = edge.Curve.parameter(FreeCAD.Vector(p2.X, p2.Y, p2.Z))
     return False
Exemple #13
0
    def orientSelected(self, axis):
        def flipSel(sel):
            PathLog.debug("flip")
            p = sel.Object.Placement
            loc = sel.Object.Placement.Base
            rot = FreeCAD.Rotation(FreeCAD.Vector(1-axis.x, 1-axis.y, 1-axis.z), 180)
            sel.Object.Placement = FreeCAD.Placement(loc, p.Rotation.multiply(rot))

        def rotateSel(sel, n):
            p = sel.Object.Placement
            loc = sel.Object.Placement.Base
            r = axis.cross(n) # rotation axis
            a = DraftVecUtils.angle(n, axis, r) * 180 / math.pi
            PathLog.debug("oh boy: (%.2f, %.2f, %.2f) -> %.2f" % (r.x, r.y, r.z, a))
            Draft.rotate(sel.Object, a, axis=r)

        selObject = None
        selFeature = None
        for sel in FreeCADGui.Selection.getSelectionEx():
            selObject = sel.Object
            for feature in sel.SubElementNames:
                selFeature = feature
                sub = sel.Object.Shape.getElement(feature)
                if 'Face' == sub.ShapeType:
                    n = sub.Surface.Axis
                    if sub.Orientation == 'Reversed':
                        n = FreeCAD.Vector() - n
                        PathLog.debug("(%.2f, %.2f, %.2f) -> reversed (%s)" % (n.x, n.y, n.z, sub.Orientation))
                    else:
                        PathLog.debug("(%.2f, %.2f, %.2f) -> forward  (%s)" % (n.x, n.y, n.z, sub.Orientation))

                    if PathGeom.pointsCoincide(axis, n):
                        PathLog.debug("face properly oriented (%.2f, %.2f, %.2f)" % (n.x, n.y, n.z))
                    else:
                        if PathGeom.pointsCoincide(axis, FreeCAD.Vector() - n):
                            flipSel(sel)
                        else:
                            rotateSel(sel, n)
                if 'Edge' == sub.ShapeType:
                    n = (sub.Vertexes[1].Point - sub.Vertexes[0].Point).normalize()
                    if PathGeom.pointsCoincide(axis, n) or PathGeom.pointsCoincide(axis, FreeCAD.Vector() - n):
                        # Don't really know the orientation of an edge, so let's just flip the object
                        # and if the user doesn't like it they can flip again
                        flipSel(sel)
                    else:
                        rotateSel(sel, n)
        if selObject and selFeature:
            FreeCADGui.Selection.clearSelection()
            FreeCADGui.Selection.addSelection(selObject, selFeature)
 def checkIgnoreAbove(self, edge):
     if self.ignoreAboveEnabled:
         p0 = edge.Vertexes[0].Point
         p1 = edge.Vertexes[1].Point
         if p0.z > self.ignoreAbove and (p1.z > self.ignoreAbove or PathGeom.isRoughly(p1.z, self.ignoreAbove.Value)):
             PathLog.debug("Whole plunge move above 'ignoreAbove', ignoring")
             return (edge, True)
         elif p0.z > self.ignoreAbove and not PathGeom.isRoughly(p0.z, self.ignoreAbove.Value):
             PathLog.debug("Plunge move partially above 'ignoreAbove', splitting into two")
             newPoint = FreeCAD.Base.Vector(p0.x, p0.y, self.ignoreAbove)
             return (Part.makeLine(p0, newPoint), False)
         else:
             return None, False
     else:
         return None, False
 def __init__(self, obj):
     PathLog.track(obj.Base.Name)
     self.obj = obj
     self.wire, rapid = PathGeom.wireForPath(obj.Base.Path)
     self.rapid = _RapidEdges(rapid)
     self.edges = self.wire.Edges
     self.baseWire = self.findBottomWire(self.edges)
    def execute(self, obj):
        if not obj.Base:
            return
        if not obj.Base.isDerivedFrom("Path::Feature"):
            return
        if not obj.Base.Path:
            return
        if obj.Angle >= 90:
            obj.Angle = 89.9
        elif obj.Angle <= 0:
            obj.Angle = 0.1

        if hasattr(obj, 'UseStartDepth'):
            self.ignoreAboveEnabled = obj.UseStartDepth
            self.ignoreAbove = obj.DressupStartDepth
        else:
            self.ignoreAboveEnabled = False
            self.ignoreAbove = 0

        self.angle = obj.Angle
        self.method = obj.Method
        self.wire, self.rapids = PathGeom.wireForPath(obj.Base.Path)
        if self.method in ['RampMethod1', 'RampMethod2', 'RampMethod3']:
            self.outedges = self.generateRamps()
        else:
            self.outedges = self.generateHelix()
        obj.Path = self.createCommands(obj, self.outedges)
Exemple #17
0
    def onDocumentRestored(self, obj):
        features = self.opFeatures(obj)
        if FeatureBaseGeometry & features and 'App::PropertyLinkSubList' == obj.getTypeIdOfProperty('Base'):
            PathLog.info("Replacing link property with global link (%s)." % obj.State)
            base = obj.Base
            obj.removeProperty('Base')
            self.addBaseProperty(obj)
            obj.Base = base
            obj.touch()
            obj.Document.recompute()

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

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

        if FeatureDepths & features and not hasattr(obj, 'OpStartDepth'):
            self.addOpValues(obj, ['start', 'final'])
            if not hasattr(obj, 'StartDepthLock') or not obj.StartDepthLock:
                obj.setExpression('StartDepth', 'OpStartDepth')
            if FeatureNoFinalDepth & features:
                obj.setEditorMode('OpFinalDepth', 2)
            elif not hasattr(obj, 'FinalDepthLock') or not obj.FinalDepthLock:
                obj.setExpression('FinalDepth', 'OpFinalDepth')
Exemple #18
0
    def createCommands(self, obj, edges):
        commands = []
        for edge in edges:
            israpid = False
            for redge in self.rapids:
                if PathGeom.edgesMatch(edge, redge):
                    israpid = True
            if israpid:
                v = edge.valueAt(edge.LastParameter)
                commands.append(Path.Command('G0', {'X': v.x, 'Y': v.y, 'Z': v.z}))
            else:
                commands.extend(PathGeom.cmdsForEdge(edge))

        lastCmd = Path.Command('G0', {'X': 0.0, 'Y': 0.0, 'Z': 0.0})

        outCommands = []

        horizFeed = obj.ToolController.HorizFeed.Value
        vertFeed = obj.ToolController.VertFeed.Value
        horizRapid = obj.ToolController.HorizRapid.Value
        vertRapid = obj.ToolController.VertRapid.Value

        for cmd in commands:
            params = cmd.Parameters
            zVal = params.get('Z', None)
            zVal2 = lastCmd.Parameters.get('Z', None)

            zVal = zVal and round(zVal, 8)
            zVal2 = zVal2 and round(zVal2, 8)

            if cmd.Name in ['G1', 'G2', 'G3', 'G01', 'G02', 'G03']:
                if zVal is not None and zVal2 != zVal:
                    params['F'] = vertFeed
                else:
                    params['F'] = horizFeed
                lastCmd = cmd

            elif cmd.Name in ['G0', 'G00']:
                if zVal is not None and zVal2 != zVal:
                    params['F'] = vertRapid
                else:
                    params['F'] = horizRapid
                lastCmd = cmd

            outCommands.append(Path.Command(cmd.Name, params))

        return Path.Path(outCommands)
Exemple #19
0
    def buildpathocc(self, obj, wires, zValues):
        '''buildpathocc(obj, wires, zValues) ... internal helper function to generate engraving commands.'''
        PathLog.track(obj.Label, len(wires), zValues)

        for wire in wires:
            offset = wire

            # reorder the wire
            offset = DraftGeomUtils.rebaseWire(offset, obj.StartVertex)

            last = None
            for z in zValues:
                if last:
                    self.commandlist.append(Path.Command('G1', {'X': last.x, 'Y': last.y, 'Z': z, 'F': self.vertFeed}))

                for edge in offset.Edges:
                    if not last:
                        # we set the first move to our first point
                        last = edge.Vertexes[0].Point
                        if len(offset.Edges) > 1:
                            e2 = offset.Edges[1]
                            if not PathGeom.pointsCoincide(edge.Vertexes[-1].Point, e2.Vertexes[0].Point) and not PathGeom.pointsCoincide(edge.Vertexes[-1].Point, e2.Vertexes[-1].Point):
                                PathLog.debug("flip first edge")
                                last = edge.Vertexes[-1].Point
                            else:
                                PathLog.debug("original first edge")
                        else:
                            PathLog.debug("not enough edges to flip")
                        self.commandlist.append(Path.Command('G0', {'X': last.x, 'Y': last.y, 'Z': obj.ClearanceHeight.Value, 'F': self.horizRapid}))
                        self.commandlist.append(Path.Command('G0', {'X': last.x, 'Y': last.y, 'Z': obj.SafeHeight.Value, 'F': self.vertRapid}))
                        self.commandlist.append(Path.Command('G0', {'X': last.x, 'Y': last.y, 'Z': z, 'F': self.vertFeed}))

                    if PathGeom.pointsCoincide(last, edge.Vertexes[0].Point):
                        for cmd in PathGeom.cmdsForEdge(edge):
                            params = cmd.Parameters
                            params.update({'Z': z, 'F': self.horizFeed})
                            self.commandlist.append(Path.Command(cmd.Name, params))
                        last = edge.Vertexes[-1].Point
                    else:
                        for cmd in PathGeom.cmdsForEdge(edge, True):
                            params = cmd.Parameters
                            params.update({'Z': z, 'F': self.horizFeed})
                            self.commandlist.append(Path.Command(cmd.Name, params))
                        last = edge.Vertexes[0].Point
            self.commandlist.append(Path.Command('G0', {'Z': obj.ClearanceHeight.Value, 'F': self.vertRapid}))
        if self.commandlist:
            self.commandlist.pop()
 def cloneAt(self, pos):
     clone = self.solid.copy()
     pos.z = 0
     angle = -PathGeom.getAngle(pos - self.baseCenter) * 180 / math.pi
     clone.rotate(FreeCAD.Vector(0, 0, 0), FreeCAD.Vector(0, 0, 1), angle)
     pos.z = self.z - self.actualHeight * 0.01
     clone.translate(pos)
     return clone
Exemple #21
0
 def test00(self):
     """Verify getAngle functionality."""
     self.assertRoughly(PathGeom.getAngle(Vector(1, 0, 0)), 0)
     self.assertRoughly(PathGeom.getAngle(Vector(1, 1, 0)), math.pi/4)
     self.assertRoughly(PathGeom.getAngle(Vector(0, 1, 0)), math.pi/2)
     self.assertRoughly(PathGeom.getAngle(Vector(-1, 1, 0)), 3*math.pi/4)
     self.assertRoughly(PathGeom.getAngle(Vector(-1, 0, 0)), math.pi)
     self.assertRoughly(PathGeom.getAngle(Vector(-1, -1, 0)), -3*math.pi/4)
     self.assertRoughly(PathGeom.getAngle(Vector(0, -1, 0)), -math.pi/2)
     self.assertRoughly(PathGeom.getAngle(Vector(1, -1, 0)), -math.pi/4)
 def pointIsOnPath(self, p):
     v = Part.Vertex(self.pointAtBottom(p))
     PathLog.debug("pt = (%f, %f, %f)" % (v.X, v.Y, v.Z))
     for e in self.bottomEdges:
         indent = "{} ".format(e.distToShape(v)[0])
         debugEdge(e, indent, True)
         if PathGeom.isRoughly(0.0, v.distToShape(e)[0], 0.1):
             return True
     return False
 def orderAndFlipEdges(self, edges):
     PathLog.track("entry(%.2f, %.2f, %.2f), exit(%.2f, %.2f, %.2f)" % (self.entry.x, self.entry.y, self.entry.z, self.exit.x, self.exit.y, self.exit.z))
     self.edgesOrder = []
     outputEdges = []
     p0 = self.entry
     lastP = p0
     while edges:
         # print("(%.2f, %.2f, %.2f) %d %d" % (p0.x, p0.y, p0.z))
         for e in edges:
             p1 = e.valueAt(e.FirstParameter)
             p2 = e.valueAt(e.LastParameter)
             if PathGeom.pointsCoincide(p1, p0):
                 outputEdges.append((e, False))
                 edges.remove(e)
                 lastP = None
                 p0 = p2
                 debugEdge(e, ">>>>> no flip")
                 break
             elif PathGeom.pointsCoincide(p2, p0):
                 outputEdges.append((e, True))
                 edges.remove(e)
                 lastP = None
                 p0 = p1
                 debugEdge(e, ">>>>> flip")
                 break
             else:
                 debugEdge(e, "<<<<< (%.2f, %.2f, %.2f)" % (p0.x, p0.y, p0.z))
         if lastP == p0:
             self.edgesOrder.append(outputEdges)
             self.edgesOrder.append(edges)
             print('ordered edges:')
             for e, flip in outputEdges:
                 debugEdge(e, '  %c ' % ('<' if flip else '>'), False)
             print('remaining edges:')
             for e in edges:
                 debugEdge(e, '    ', False)
             raise ValueError("No connection to %s" % (p0))
         elif lastP:
             PathLog.debug("xxxxxx (%.2f, %.2f, %.2f) (%.2f, %.2f, %.2f)" % (p0.x, p0.y, p0.z, lastP.x, lastP.y, lastP.z))
         else:
             PathLog.debug("xxxxxx (%.2f, %.2f, %.2f) -" % (p0.x, p0.y, p0.z))
         lastP = p0
     PathLog.track("-")
     return outputEdges
 def filterIntersections(self, pts, face):
     if type(face.Surface) == Part.Cone or type(face.Surface) == Part.Cylinder or type(face.Surface) == Part.Toroid:
         PathLog.track("it's a cone/cylinder, checking z")
         return filter(lambda pt: pt.z >= self.bottom() and pt.z <= self.top(), pts)
     if type(face.Surface) == Part.Plane:
         PathLog.track("it's a plane, checking R")
         c = face.Edges[0].Curve
         if (type(c) == Part.Circle):
             return filter(lambda pt: (pt - c.Center).Length <= c.Radius or PathGeom.isRoughly((pt - c.Center).Length, c.Radius), pts)
     print("==== we got a %s" % face.Surface)
Exemple #25
0
 def selectionZLevel(self, sel):
     if len(sel) == 1 and len(sel[0].SubObjects) == 1:
         sub = sel[0].SubObjects[0]
         if PathGeom.isHorizontal(sub):
             if 'Vertex' == sub.ShapeType:
                 return sub.Z
             if 'Edge' == sub.ShapeType:
                 return sub.Vertexes[0].Z
             if 'Face' == sub.ShapeType:
                 return sub.BoundBox.ZMax
     return None
 def tagAtPoint(self, point, matchZ):
     x = point[0]
     y = point[1]
     z = point[2]
     if self.tags and not matchZ:
         z = self.tags[0].point.z
     p = FreeCAD.Vector(x, y, z)
     for i, tag in enumerate(self.tags):
         if PathGeom.pointsCoincide(p, tag.point, tag.sphere.radius.getValue() * 1.3):
             return i
     return -1
    def shell(self):
        if len(self.edges) > 1:
            wire = Part.Wire(self.initialEdge)
        else:
            edge = self.edges[0]
            if PathGeom.pointsCoincide(edge.valueAt(edge.FirstParameter), self.finalEdge.valueAt(self.finalEdge.FirstParameter)):
                wire = Part.Wire(self.finalEdge)
            elif hasattr(self, 'initialEdge') and PathGeom.pointsCoincide(edge.valueAt(edge.FirstParameter), self.initialEdge.valueAt(self.initialEdge.FirstParameter)):
                wire = Part.Wire(self.initialEdge)
            else:
                wire = Part.Wire(edge)

        for edge in self.edges[1:]:
            if PathGeom.pointsCoincide(edge.valueAt(edge.FirstParameter), self.finalEdge.valueAt(self.finalEdge.FirstParameter)):
                wire.add(self.finalEdge)
            else:
                wire.add(edge)

        shell = wire.extrude(FreeCAD.Vector(0, 0, self.tag.height + 1))
        return shell.removeShape(filter(lambda f: PathGeom.isRoughly(f.Area, 0), shell.Faces))
 def execute(self, obj):
     if not obj.Base:
         return
     if not obj.Base.isDerivedFrom("Path::Feature"):
         return
     if not obj.Base.Path:
         return
     if obj.Length < 0:
         PathLog.error(translate("Length/Radius positiv not Null\n"))
         obj.Length = 0.1
     self.wire, self.rapids = PathGeom.wireForPath(obj.Base.Path)
     obj.Path = self.generateLeadInOutCurve(obj)
 def findBottomWire(self, edges):
     (minZ, maxZ) = self.findZLimits(edges)
     self.minZ = minZ
     self.maxZ = maxZ
     bottom = [e for e in edges if PathGeom.isRoughly(e.Vertexes[0].Point.z, minZ) and PathGeom.isRoughly(e.Vertexes[1].Point.z, minZ)]
     self.bottomEdges = bottom
     try:
         wire = Part.Wire(bottom)
         if wire.isClosed():
             return wire
     except:
         return None
Exemple #30
0
    def test60(self):
        """Verify arcToHelix returns proper helix."""
        p1 = Vector(10,-10,0)
        p2 = Vector(0,0,0)
        p3 = Vector(10,10,0)

        e = PathGeom.arcToHelix(Part.Edge(Part.Arc(p1, p2, p3)), 0, 2)
        self.assertCurve(e, p1, p2 + Vector(0,0,1), p3 + Vector(0,0,2))

        e = PathGeom.arcToHelix(Part.Edge(Part.Arc(p1, p2, p3)), 3, 7)
        self.assertCurve(e, p1 + Vector(0,0,3), p2 + Vector(0,0,5), p3 + Vector(0,0,7))

        e = PathGeom.arcToHelix(Part.Edge(Part.Arc(p1, p2, p3)), 9, 1)
        self.assertCurve(e, p1 + Vector(0,0,9), p2 + Vector(0,0,5), p3 + Vector(0,0,1))

        dz = Vector(0,0,3)
        p11 = p1 + dz
        p12 = p2 + dz
        p13 = p3 + dz

        e = PathGeom.arcToHelix(Part.Edge(Part.Arc(p11, p12, p13)), 0, 8)
        self.assertCurve(e, p1, p2 + Vector(0,0,4), p3 + Vector(0,0,8))

        e = PathGeom.arcToHelix(Part.Edge(Part.Arc(p11, p12, p13)), 2, -2)
        self.assertCurve(e, p1 + Vector(0,0,2), p2, p3 + Vector(0,0,-2))

        o = 10*math.sin(math.pi/4)
        p1 = Vector(10, -10, 1)
        p2 = Vector(10 - 10*math.sin(math.pi/4), -10*math.cos(math.pi/4), 1)
        p3 = Vector(0, 0, 1)
        e = PathGeom.arcToHelix(Part.Edge(Part.Arc(p1, p2, p3)), 0, 5)
        self.assertCurve(e, Vector(10,-10,0), Vector(p2.x,p2.y,2.5), Vector(0, 0, 5))
Exemple #31
0
def makeAreaCurve(edges, direction, startpt=None, endpt=None):
    curveobj = area.Curve()

    cleanededges = Part.__sortEdges__(PathUtils.cleanedges(edges, 0.01))

    # for e in cleanededges:
    # print str(e.valueAt(e.FirstParameter)) + "," +
    # str(e.valueAt(e.LastParameter))
    edgelist = []

    if len(cleanededges) == 1:  # user selected a single edge.
        edgelist = cleanededges
    else:
        # edgelist = [] #Multiple edges.  Need to sequence the vetexes.
        # First get the first segment oriented correctly.

        # We first compare the last parameter of the first segment to see if it
        # matches either end of the second segment. If not, it must need
        # flipping.
        p0L = cleanededges[0].valueAt(cleanededges[0].LastParameter)
        if PathGeom.pointsCoincide(
                p0L, cleanededges[1].valueAt(cleanededges[1].FirstParameter)
        ) or PathGeom.pointsCoincide(
                p0L, cleanededges[1].valueAt(cleanededges[1].LastParameter)):
            edge0 = cleanededges[0]
        else:
            edge0 = PathUtils.reverseEdge(cleanededges[0])

        edgelist.append(edge0)

        # Now iterate the rest of the edges matching the last parameter of the
        # previous segment.
        for edge in cleanededges[1:]:
            if PathGeom.pointsCoincide(
                    edge.valueAt(edge.FirstParameter),
                    edgelist[-1].valueAt(edgelist[-1].LastParameter)):
                nextedge = edge
            else:
                nextedge = PathUtils.reverseEdge(edge)
            edgelist.append(nextedge)
    # print "makeareacurve 87: " + "area.Point(" +
    # str(edgelist[0].Vertexes[0].X) + ", " +
    # str(edgelist[0].Vertexes[0].Y)+")"
    curveobj.append(
        area.Point(edgelist[0].Vertexes[0].X, edgelist[0].Vertexes[0].Y))
    #     seglist =[]
    #     if direction=='CW':
    #         edgelist.reverse()
    #         for e in edgelist:
    #             seglist.append(PathUtils.reverseEdge(e)) #swap end points on every segment
    #     else:
    #         for e in edgelist:
    #             seglist.append(e)

    for s in edgelist:
        curveobj.append(makeAreaVertex(s))

    if startpt:
        # future nearest point code yet to be worked out -fixme
        #         v1 = Vector(startpt.X,startpt.Y,startpt.Z)
        #         perppoint1 = DraftGeomUtils.findPerpendicular(v1,firstedge)
        #         perppoint1 = DraftGeomUtils.findDistance(v1,firstedge)
        #         if  perppoint1:
        #             curveobj.ChangeStart(area.Point(perppoint1[0].x,perppoint1[0].y))
        #         else:
        #             curveobj.ChangeStart(area.Point(startpt.X,startpt.Y))
        curveobj.ChangeStart(area.Point(startpt.x, startpt.y))
    if endpt:
        # future nearest point code yet to be worked out -fixme
        #         v2 = Vector(endpt.X,endpt.Y,endpt.Z)
        #         perppoint2 = DraftGeomUtils.findPerpendicular(v2,lastedge)
        #         if perppoint2:
        #             curveobj.ChangeEnd(area.Point(perppoint2[0].x,perppoint2[0].y))
        #         else:
        #             curveobj.ChangeEnd(area.Point(endpt.X,endpt.Y))
        curveobj.ChangeEnd(area.Point(endpt.x, endpt.y))

    if curveobj.IsClockwise() and direction == 'CCW':
        curveobj.Reverse()
    elif not curveobj.IsClockwise() and direction == 'CW':
        curveobj.Reverse()
    return curveobj
Exemple #32
0
 def isStrut(self, edge):
     p1 = PathGeom.xy(edge.valueAt(edge.FirstParameter))
     p2 = PathGeom.xy(edge.valueAt(edge.LastParameter))
     return PathGeom.pointsCoincide(p1, p2)
 def isAPlungeMove(self):
     return not PathGeom.isRoughly(self.End.z, self.Start.z)
Exemple #34
0
    def test02(self):
        """Verify isVertical/isHorizontal for Vector"""
        self.assertTrue(PathGeom.isVertical(Vector(0, 0, 1)))
        self.assertTrue(PathGeom.isVertical(Vector(0, 0, -1)))
        self.assertFalse(PathGeom.isVertical(Vector(1, 0, 1)))
        self.assertFalse(PathGeom.isVertical(Vector(1, 0, -1)))

        self.assertTrue(PathGeom.isHorizontal(Vector(1, 0, 0)))
        self.assertTrue(PathGeom.isHorizontal(Vector(-1, 0, 0)))
        self.assertTrue(PathGeom.isHorizontal(Vector(0, 1, 0)))
        self.assertTrue(PathGeom.isHorizontal(Vector(0, -1, 0)))
        self.assertTrue(PathGeom.isHorizontal(Vector(1, 1, 0)))
        self.assertTrue(PathGeom.isHorizontal(Vector(-1, 1, 0)))
        self.assertTrue(PathGeom.isHorizontal(Vector(1, -1, 0)))
        self.assertTrue(PathGeom.isHorizontal(Vector(-1, -1, 0)))

        self.assertFalse(PathGeom.isHorizontal(Vector(0, 1, 1)))
        self.assertFalse(PathGeom.isHorizontal(Vector(0, -1, 1)))
        self.assertFalse(PathGeom.isHorizontal(Vector(0, 1, -1)))
        self.assertFalse(PathGeom.isHorizontal(Vector(0, -1, -1)))
    def createPath(self, obj, pathData, tags):
        PathLog.track()
        commands = []
        lastEdge = 0
        lastTag = 0
        # sameTag = None
        t = 0
        # inters = None
        edge = None

        segm = 50
        if hasattr(obj, 'SegmentationFactor'):
            segm = obj.SegmentationFactor
            if segm <= 0:
                segm = 50
                obj.SegmentationFactor = 50

        self.mappers = []
        mapper = None

        while edge or lastEdge < len(pathData.edges):
            PathLog.debug("------- lastEdge = %d/%d.%d/%d" %
                          (lastEdge, lastTag, t, len(tags)))
            if not edge:
                edge = pathData.edges[lastEdge]
                debugEdge(
                    edge, "=======  new edge: %d/%d" %
                    (lastEdge, len(pathData.edges)))
                lastEdge += 1
                # sameTag = None

            if mapper:
                mapper.add(edge)
                if mapper.mappingComplete():
                    commands.extend(mapper.commands)
                    edge = mapper.tail
                    mapper = None
                else:
                    edge = None

            if edge:
                tIndex = (t + lastTag) % len(tags)
                t += 1
                i = tags[tIndex].intersects(edge, edge.FirstParameter)
                if i and self.isValidTagStartIntersection(edge, i):
                    mapper = MapWireToTag(edge, tags[tIndex], i, segm,
                                          pathData.maxZ)
                    self.mappers.append(mapper)
                    edge = mapper.tail

            if not mapper and t >= len(tags):
                # gone through all tags, consume edge and move on
                if edge:
                    debugEdge(edge, '++++++++')
                    if pathData.rapid.isRapid(edge):
                        v = edge.Vertexes[1]
                        if not commands and PathGeom.isRoughly(
                                0, v.X) and PathGeom.isRoughly(
                                    0, v.Y) and not PathGeom.isRoughly(0, v.Z):
                            # The very first move is just to move to ClearanceHeight
                            commands.append(Path.Command('G0', {'Z': v.Z}))
                        else:
                            commands.append(
                                Path.Command('G0', {
                                    'X': v.X,
                                    'Y': v.Y,
                                    'Z': v.Z
                                }))
                    else:
                        commands.extend(PathGeom.cmdsForEdge(edge, segm=segm))
                edge = None
                t = 0

        lastCmd = Path.Command('G0', {'X': 0.0, 'Y': 0.0, 'Z': 0.0})
        outCommands = []

        tc = PathDressup.toolController(obj.Base)
        horizFeed = tc.HorizFeed.Value
        vertFeed = tc.VertFeed.Value
        horizRapid = tc.HorizRapid.Value
        vertRapid = tc.VertRapid.Value

        for cmd in commands:
            params = cmd.Parameters
            zVal = params.get('Z', None)
            zVal2 = lastCmd.Parameters.get('Z', None)

            zVal = zVal and round(zVal, 8)
            zVal2 = zVal2 and round(zVal2, 8)

            if cmd.Name in ['G1', 'G2', 'G3', 'G01', 'G02', 'G03']:
                if False and zVal is not None and zVal2 != zVal:
                    params['F'] = vertFeed
                else:
                    params['F'] = horizFeed
                lastCmd = cmd

            elif cmd.Name in ['G0', 'G00']:
                if zVal is not None and zVal2 != zVal:
                    params['F'] = vertRapid
                else:
                    params['F'] = horizRapid
                lastCmd = cmd

            outCommands.append(Path.Command(cmd.Name, params))

        return Path.Path(outCommands)
 def tboneHorizontal(self, bone):
     angle = bone.angle()
     boneAngle = 0
     if PathGeom.isRoughly(angle, math.pi) or math.fabs(angle) > math.pi/2:
         boneAngle = -math.pi
     return self.inOutBoneCommands(bone, boneAngle, self.toolRadius)
Exemple #37
0
    def test04(self):
        """Verify isVertical/isHorizontal for faces"""

        # planes
        xPlane = Part.makePlane(100, 100, FreeCAD.Vector(),
                                FreeCAD.Vector(1, 0, 0))
        yPlane = Part.makePlane(100, 100, FreeCAD.Vector(),
                                FreeCAD.Vector(0, 1, 0))
        zPlane = Part.makePlane(100, 100, FreeCAD.Vector(),
                                FreeCAD.Vector(0, 0, 1))
        xyPlane = Part.makePlane(100, 100, FreeCAD.Vector(),
                                 FreeCAD.Vector(1, 1, 0))
        xzPlane = Part.makePlane(100, 100, FreeCAD.Vector(),
                                 FreeCAD.Vector(1, 0, 1))
        yzPlane = Part.makePlane(100, 100, FreeCAD.Vector(),
                                 FreeCAD.Vector(0, 1, 1))

        self.assertTrue(PathGeom.isVertical(xPlane))
        self.assertTrue(PathGeom.isVertical(yPlane))
        self.assertFalse(PathGeom.isVertical(zPlane))
        self.assertTrue(PathGeom.isVertical(xyPlane))
        self.assertFalse(PathGeom.isVertical(xzPlane))
        self.assertFalse(PathGeom.isVertical(yzPlane))

        self.assertFalse(PathGeom.isHorizontal(xPlane))
        self.assertFalse(PathGeom.isHorizontal(yPlane))
        self.assertTrue(PathGeom.isHorizontal(zPlane))
        self.assertFalse(PathGeom.isHorizontal(xyPlane))
        self.assertFalse(PathGeom.isHorizontal(xzPlane))
        self.assertFalse(PathGeom.isHorizontal(yzPlane))

        # cylinders
        xCylinder = [
            f for f in Part.makeCylinder(1, 1, FreeCAD.Vector(),
                                         FreeCAD.Vector(1, 0, 0)).Faces
            if type(f.Surface) == Part.Cylinder
        ][0]
        yCylinder = [
            f for f in Part.makeCylinder(1, 1, FreeCAD.Vector(),
                                         FreeCAD.Vector(0, 1, 0)).Faces
            if type(f.Surface) == Part.Cylinder
        ][0]
        zCylinder = [
            f for f in Part.makeCylinder(1, 1, FreeCAD.Vector(),
                                         FreeCAD.Vector(0, 0, 1)).Faces
            if type(f.Surface) == Part.Cylinder
        ][0]
        xyCylinder = [
            f for f in Part.makeCylinder(1, 1, FreeCAD.Vector(),
                                         FreeCAD.Vector(1, 1, 0)).Faces
            if type(f.Surface) == Part.Cylinder
        ][0]
        xzCylinder = [
            f for f in Part.makeCylinder(1, 1, FreeCAD.Vector(),
                                         FreeCAD.Vector(1, 0, 1)).Faces
            if type(f.Surface) == Part.Cylinder
        ][0]
        yzCylinder = [
            f for f in Part.makeCylinder(1, 1, FreeCAD.Vector(),
                                         FreeCAD.Vector(0, 1, 1)).Faces
            if type(f.Surface) == Part.Cylinder
        ][0]

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

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

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

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

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

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

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

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

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

        return outedges
Exemple #40
0
    def test01(self):
        """Verify diffAngle functionality."""
        self.assertRoughly(PathGeom.diffAngle(0, +0*math.pi/4, 'CW') / math.pi, 0/4.)
        self.assertRoughly(PathGeom.diffAngle(0, +3*math.pi/4, 'CW') / math.pi, 5/4.)
        self.assertRoughly(PathGeom.diffAngle(0, -3*math.pi/4, 'CW') / math.pi, 3/4.)
        self.assertRoughly(PathGeom.diffAngle(0, +4*math.pi/4, 'CW') / math.pi, 4/4.)
        self.assertRoughly(PathGeom.diffAngle(0, +0*math.pi/4, 'CCW')/ math.pi, 0/4.)
        self.assertRoughly(PathGeom.diffAngle(0, +3*math.pi/4, 'CCW')/ math.pi, 3/4.)
        self.assertRoughly(PathGeom.diffAngle(0, -3*math.pi/4, 'CCW')/ math.pi, 5/4.)
        self.assertRoughly(PathGeom.diffAngle(0, +4*math.pi/4, 'CCW')/ math.pi, 4/4.)

        self.assertRoughly(PathGeom.diffAngle(+math.pi/4, +0*math.pi/4,  'CW') / math.pi, 1/4.)
        self.assertRoughly(PathGeom.diffAngle(+math.pi/4, +3*math.pi/4,  'CW') / math.pi, 6/4.)
        self.assertRoughly(PathGeom.diffAngle(+math.pi/4, -1*math.pi/4,  'CW') / math.pi, 2/4.)
        self.assertRoughly(PathGeom.diffAngle(-math.pi/4, +0*math.pi/4,  'CW') / math.pi, 7/4.)
        self.assertRoughly(PathGeom.diffAngle(-math.pi/4, +3*math.pi/4,  'CW') / math.pi, 4/4.)
        self.assertRoughly(PathGeom.diffAngle(-math.pi/4, -1*math.pi/4,  'CW') / math.pi, 0/4.)

        self.assertRoughly(PathGeom.diffAngle(+math.pi/4, +0*math.pi/4, 'CCW') / math.pi, 7/4.)
        self.assertRoughly(PathGeom.diffAngle(+math.pi/4, +3*math.pi/4, 'CCW') / math.pi, 2/4.)
        self.assertRoughly(PathGeom.diffAngle(+math.pi/4, -1*math.pi/4, 'CCW') / math.pi, 6/4.)
        self.assertRoughly(PathGeom.diffAngle(-math.pi/4, +0*math.pi/4, 'CCW') / math.pi, 1/4.)
        self.assertRoughly(PathGeom.diffAngle(-math.pi/4, +3*math.pi/4, 'CCW') / math.pi, 4/4.)
        self.assertRoughly(PathGeom.diffAngle(-math.pi/4, -1*math.pi/4, 'CCW') / math.pi, 0/4.)
    def createCommands(self, obj, edges):
        commands = []
        for edge in edges:
            israpid = False
            for redge in self.rapids:
                if PathGeom.edgesMatch(edge, redge):
                    israpid = True
            if israpid:
                v = edge.valueAt(edge.LastParameter)
                commands.append(Path.Command('G0', {'X': v.x, 'Y': v.y, 'Z': v.z}))
            else:
                commands.extend(PathGeom.cmdsForEdge(edge))

        lastCmd = Path.Command('G0', {'X': 0.0, 'Y': 0.0, 'Z': 0.0})

        outCommands = []

        horizFeed = obj.ToolController.HorizFeed.Value
        vertFeed = obj.ToolController.VertFeed.Value
        if obj.RampFeedRate == "Horizontal Feed Rate":
            rampFeed = obj.ToolController.HorizFeed.Value
        elif obj.RampFeedRate == "Vertical Feed Rate":
            rampFeed = obj.ToolController.VertFeed.Value
        else:
            rampFeed = obj.CustomFeedRate.Value
        horizRapid = obj.ToolController.HorizRapid.Value
        vertRapid = obj.ToolController.VertRapid.Value


        for cmd in commands:
            params = cmd.Parameters
            zVal = params.get('Z', None)
            zVal2 = lastCmd.Parameters.get('Z', None)

            xVal = params.get('X', None)
            xVal2 = lastCmd.Parameters.get('X', None)

            yVal2 = lastCmd.Parameters.get('Y', None)
            yVal = params.get('Y', None)

            zVal = zVal and round(zVal, 8)
            zVal2 = zVal2 and round(zVal2, 8)

            if cmd.Name in ['G1', 'G2', 'G3', 'G01', 'G02', 'G03']:
                if zVal is not None and zVal2 != zVal:
                    if PathGeom.isRoughly(xVal, xVal2) and PathGeom.isRoughly(yVal, yVal2):
                        # this is a straight plunge
                        params['F'] = vertFeed
                    else:
                        # this is a ramp
                        params['F'] = rampFeed
                else:
                    params['F'] = horizFeed
                lastCmd = cmd

            elif cmd.Name in ['G0', 'G00']:
                if zVal is not None and zVal2 != zVal:
                    params['F'] = vertRapid
                else:
                    params['F'] = horizRapid
                lastCmd = cmd

            outCommands.append(Path.Command(cmd.Name, params))

        return Path.Path(outCommands)
Exemple #42
0
 def test10(self):
     """Verify proper geometry objects for G1 and G01 commands are created."""
     spt = Vector(1,2,3)
     self.assertLine(PathGeom.edgeForCmd(Path.Command('G1',  {'X': 7, 'Y': 2, 'Z': 3}), spt), spt, Vector(7, 2, 3))
     self.assertLine(PathGeom.edgeForCmd(Path.Command('G01', {'X': 1, 'Y': 3, 'Z': 5}), spt), spt, Vector(1, 3, 5))
 def RapidMove(self, cmd, curpos):
     path = PathGeom.edgeForCmd(cmd, curpos)  # hack to overcome occ bug
     if path is None:
         return curpos
     return path.valueAt(path.LastParameter)
Exemple #44
0
    def updateDepths(self, obj, ignoreErrors=False):
        '''updateDepths(obj) ... base implementation calculating depths depending on base geometry.
        Can safely be overwritten.'''
        def faceZmin(bb, fbb):
            if fbb.ZMax == fbb.ZMin and fbb.ZMax == bb.ZMax:  # top face
                return bb.ZMin
            elif fbb.ZMax > fbb.ZMin and fbb.ZMax == bb.ZMax:  # vertical face, full cut
                return fbb.ZMin
            elif fbb.ZMax > fbb.ZMin and fbb.ZMin > bb.ZMin:  # internal vertical wall
                return fbb.ZMin
            elif fbb.ZMax == fbb.ZMin and fbb.ZMax > bb.ZMin:  # face/shelf
                return fbb.ZMin
            return bb.ZMin

        if not self._setBaseAndStock(obj, ignoreErrors):
            return False

        stockBB = self.stock.Shape.BoundBox
        zmin = stockBB.ZMin
        zmax = stockBB.ZMax

        if hasattr(obj, 'Base') and obj.Base:
            for base, sublist in obj.Base:
                bb = base.Shape.BoundBox
                zmax = max(zmax, bb.ZMax)
                for sub in sublist:
                    fbb = base.Shape.getElement(sub).BoundBox
                    zmin = max(zmin, faceZmin(bb, fbb))
                    zmax = max(zmax, fbb.ZMax)
        else:
            # clearing with stock boundaries
            pass

        safeDepths = True
        if FeatureDepths & self.opFeatures(obj):
            # first set update final depth, it's value is not negotiable
            if not PathGeom.isRoughly(obj.FinalDepth.Value, zmin):
                if not hasattr(obj,
                               'FinalDepthLock') or not obj.FinalDepthLock:
                    obj.FinalDepth = zmin
                else:
                    if obj.FinalDepth.Value < zmin:
                        safeDepths = False
            zmin = obj.FinalDepth.Value

            def minZmax(z):
                if hasattr(obj, 'StepDown') and not PathGeom.isRoughly(
                        obj.StepDown.Value, 0):
                    return z + obj.StepDown.Value
                else:
                    return z + 1

            # ensure zmax is higher than zmin
            if (zmax - 0.0001) <= zmin:
                zmax = minZmax(zmin)

            # update start depth if requested and required
            if not PathGeom.isRoughly(obj.StartDepth.Value, zmax):
                if not hasattr(obj,
                               'StartDepthLock') or not obj.StartDepthLock:
                    obj.StartDepth = zmax
                elif (obj.StartDepth.Value - 0.0001) <= obj.FinalDepth.Value:
                    obj.StartDepth = minZmax(obj.FinalDepth.Value)
                else:
                    if obj.StartDepth.Value < zmax:
                        safeDepths = False

        clearance = obj.StartDepth.Value + 5.0
        safe = obj.StartDepth.Value + 3
        if hasattr(obj, 'ClearanceHeight') and not PathGeom.isRoughly(
                clearance, obj.ClearanceHeight.Value):
            obj.ClearanceHeight = clearance
        if hasattr(obj, 'SafeHeight') and not PathGeom.isRoughly(
                safe, obj.SafeHeight.Value):
            obj.SafeHeight = safe

        return safeDepths
Exemple #45
0
def isDrillable(obj, candidate, tooldiameter=None, includePartials=False):
    """
    Checks candidates to see if they can be drilled.
    Candidates can be either faces - circular or cylindrical or circular edges.
    The tooldiameter can be optionally passed.  if passed, the check will return
    False for any holes smaller than the tooldiameter.
    obj=Shape
    candidate = Face or Edge
    tooldiameter=float
    """
    PathLog.track('obj: {} candidate: {} tooldiameter {}'.format(obj, candidate, tooldiameter))
    drillable = False
    try:
        if candidate.ShapeType == 'Face':
            face = candidate
            # eliminate flat faces
            if (round(face.ParameterRange[0], 8) == 0.0) and (round(face.ParameterRange[1], 8) == round(math.pi * 2, 8)):
                for edge in face.Edges:  # Find seam edge and check if aligned to Z axis.
                    if (isinstance(edge.Curve, Part.Line)):
                        PathLog.debug("candidate is a circle")
                        v0 = edge.Vertexes[0].Point
                        v1 = edge.Vertexes[1].Point
                        #check if the cylinder seam is vertically aligned.  Eliminate tilted holes
                        if (numpy.isclose(v1.sub(v0).x, 0, rtol=1e-05, atol=1e-06)) and \
                                (numpy.isclose(v1.sub(v0).y, 0, rtol=1e-05, atol=1e-06)):
                            drillable = True
                            # vector of top center
                            lsp = Vector(face.BoundBox.Center.x, face.BoundBox.Center.y, face.BoundBox.ZMax)
                            # vector of bottom center
                            lep = Vector(face.BoundBox.Center.x, face.BoundBox.Center.y, face.BoundBox.ZMin)
                            # check if the cylindrical 'lids' are inside the base
                            # object.  This eliminates extruded circles but allows
                            # actual holes.
                            if obj.isInside(lsp, 1e-6, False) or obj.isInside(lep, 1e-6, False):
                                PathLog.track("inside check failed. lsp: {}  lep: {}".format(lsp,lep))
                                drillable = False
                            # eliminate elliptical holes
                            elif not hasattr(face.Surface, "Radius"):
                                PathLog.debug("candidate face has no radius attribute")
                                drillable = False
                            else:
                                if tooldiameter is not None:
                                    drillable = face.Surface.Radius >= tooldiameter/2
                                else:
                                    drillable = True
            elif type(face.Surface) == Part.Plane and PathGeom.pointsCoincide(face.Surface.Axis, FreeCAD.Vector(0,0,1)):
                if len(face.Edges) == 1 and type(face.Edges[0].Curve) == Part.Circle:
                    center = face.Edges[0].Curve.Center
                    if obj.isInside(center, 1e-6, False):
                        if tooldiameter is not None:
                            drillable = face.Edges[0].Curve.Radius >= tooldiameter/2
                        else:
                            drillable = True
        else:
            for edge in candidate.Edges:
                if isinstance(edge.Curve, Part.Circle) and (includePartials or edge.isClosed()):
                    PathLog.debug("candidate is a circle or ellipse")
                    if not hasattr(edge.Curve, "Radius"):
                        PathLog.debug("No radius.  Ellipse.")
                        drillable = False
                    else:
                        PathLog.debug("Has Radius, Circle")
                        if tooldiameter is not None:
                            drillable = edge.Curve.Radius >= tooldiameter/2
                            if not drillable:
                                FreeCAD.Console.PrintMessage(
                                        "Found a drillable hole with diameter: {}: "
                                        "too small for the current tool with "
                                        "diameter: {}".format(edge.Curve.Radius*2, tooldiameter))
                        else:
                            drillable = True
        PathLog.debug("candidate is drillable: {}".format(drillable))
    except Exception as ex:
        PathLog.warning(translate("PathUtils", "Issue determine drillability: {}").format(ex))
    return drillable
    def createRampMethod1(self, rampedges, p0, projectionlen, rampangle):
        """
        This method generates ramp with following pattern:
        1. Start from the original startpoint of the plunge
        2. Ramp down along the path that comes after the plunge
        3. When reaching the Z level of the original plunge, return back to the beginning
           by going the path backwards until the original plunge end point is reached
        4. Continue with the original path

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

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

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

        return outedges
Exemple #47
0
    def areaOpShapes(self, obj):
        '''areaOpShapes(obj) ... return shapes representing the solids to be removed.'''
        PathLog.track()

        if obj.Base:
            PathLog.debug("base items exist.  Processing...")
            self.removalshapes = []
            self.horiz = []
            vertical = []
            for o in obj.Base:
                PathLog.debug("Base item: {}".format(o))
                base = o[0]
                for sub in o[1]:
                    if "Face" in sub:
                        face = base.Shape.getElement(sub)
                        if type(face.Surface
                                ) == Part.Plane and PathGeom.isVertical(
                                    face.Surface.Axis):
                            # it's a flat horizontal face
                            self.horiz.append(face)
                        elif type(face.Surface
                                  ) == Part.Cylinder and PathGeom.isVertical(
                                      face.Surface.Axis):
                            # vertical cylinder wall
                            if any(e.isClosed() for e in face.Edges):
                                # complete cylinder
                                circle = Part.makeCircle(
                                    face.Surface.Radius, face.Surface.Center)
                                disk = Part.Face(Part.Wire(circle))
                                self.horiz.append(disk)
                            else:
                                # partial cylinder wall
                                vertical.append(face)
                        elif type(face.Surface
                                  ) == Part.Plane and PathGeom.isHorizontal(
                                      face.Surface.Axis):
                            vertical.append(face)
                        else:
                            PathLog.error(
                                translate(
                                    'PathPocket',
                                    "Pocket does not support shape %s.%s") %
                                (base.Label, sub))

            self.vertical = PathGeom.combineConnectedShapes(vertical)
            self.vWires = [
                TechDraw.findShapeOutline(shape, 1, FreeCAD.Vector(0, 0, 1))
                for shape in self.vertical
            ]
            for wire in self.vWires:
                w = PathGeom.removeDuplicateEdges(wire)
                face = Part.Face(w)
                face.tessellate(0.1)
                if PathGeom.isRoughly(face.Area, 0):
                    PathLog.error(
                        translate(
                            'PathPocket',
                            'Vertical faces do not form a loop - ignoring'))
                else:
                    self.horiz.append(face)

            # move all horizontal faces to FinalDepth
            for f in self.horiz:
                f.translate(
                    FreeCAD.Vector(0, 0,
                                   obj.FinalDepth.Value - f.BoundBox.ZMin))

            # check all faces and see if they are touching/overlapping and combine those into a compound
            self.horizontal = PathGeom.combineConnectedShapes(self.horiz)
            for shape in self.horizontal:
                shape.sewShape()
                shape.tessellate(0.1)

            # extrude all faces up to StartDepth and those are the removal shapes
            extent = FreeCAD.Vector(
                0, 0, obj.StartDepth.Value - obj.FinalDepth.Value)
            self.removalshapes = [(face.extrude(extent), False)
                                  for face in self.horizontal]

        else:  # process the job base object as a whole
            PathLog.debug("processing the whole job base object")
            self.outline = Part.Face(
                TechDraw.findShapeOutline(self.baseobject.Shape, 1,
                                          FreeCAD.Vector(0, 0, 1)))
            stockBB = self.stock.Shape.BoundBox

            self.outline.translate(FreeCAD.Vector(0, 0, stockBB.ZMin - 1))
            self.body = self.outline.extrude(
                FreeCAD.Vector(0, 0, stockBB.ZLength + 2))
            self.removalshapes = [(self.stock.Shape.cut(self.body), False)]

        for (shape, hole) in self.removalshapes:
            shape.tessellate(0.1)

        if self.removalshapes:
            obj.removalshape = self.removalshapes[0][0]
        return self.removalshapes
    def generateRamps(self, allowBounce=True):
        edges = self.wire.Edges
        outedges = []
        for edge in edges:
            israpid = False
            for redge in self.rapids:
                if PathGeom.edgesMatch(edge, redge):
                    israpid = True
            if not israpid:
                bb = edge.BoundBox
                p0 = edge.Vertexes[0].Point
                p1 = edge.Vertexes[1].Point
                rampangle = self.angle
                if bb.XLength < 1e-6 and bb.YLength < 1e-6 and bb.ZLength > 0 and p0.z > p1.z:
                    plungelen = abs(p0.z - p1.z)
                    projectionlen = plungelen * math.tan(math.radians(rampangle))  # length of the forthcoming ramp projected to XY plane
                    if self.method == 'RampMethod3':
                        projectionlen = projectionlen / 2
                    PathLog.debug("Found plunge move at X:{} Y:{} From Z:{} to Z{}, length of ramp: {}".format(p0.x, p0.y, p0.z, p1.z, projectionlen))
                    # next need to determine how many edges in the path after
                    # plunge are needed to cover the length:
                    covered = False
                    coveredlen = 0
                    rampedges = []
                    i = edges.index(edge) + 1
                    while not covered:
                        candidate = edges[i]
                        cp0 = candidate.Vertexes[0].Point
                        cp1 = candidate.Vertexes[1].Point
                        if abs(cp0.z - cp1.z) > 1e-6:
                            # this edge is not parallel to XY plane, not qualified for ramping.
                            break
                        # PathLog.debug("Next edge length {}".format(candidate.Length))
                        rampedges.append(candidate)
                        coveredlen = coveredlen + candidate.Length

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

                        # PathLog.debug("Doing ramp to edges: {}".format(rampedges))
                        if self.method == 'RampMethod1':
                            outedges.extend(self.createRampMethod1(rampedges, p0, projectionlen, rampangle))
                        elif self.method == 'RampMethod2':
                            outedges.extend(self.createRampMethod2(rampedges, p0, projectionlen, rampangle))
                        else:
                            # if the ramp cannot be covered with Method3, revert to Method1
                            # because Method1 support going back-and-forth and thus results in same path as Method3 when
                            # length of the ramp is smaller than needed for single ramp.
                            if (not covered) and allowBounce:
                                projectionlen = projectionlen * 2
                                outedges.extend(self.createRampMethod1(rampedges, p0, projectionlen, rampangle))
                            else:
                                outedges.extend(self.createRampMethod3(rampedges, p0, projectionlen, rampangle))
                else:
                    outedges.append(edge)
            else:
                outedges.append(edge)
        return outedges
Exemple #49
0
 def cmds(pa, pb, pc, flip):
     return PathGeom.cmdsForEdge(Part.Edge(Part.Arc(pa, pb, pc)),
                                 flip)[0]
 def tboneVertical(self, bone):
     angle = bone.angle()
     boneAngle = math.pi / 2
     if PathGeom.isRoughly(angle, math.pi) or angle < 0:
         boneAngle = -boneAngle
     return self.inOutBoneCommands(bone, boneAngle, self.toolRadius)
Exemple #51
0
    def test03(self):
        """Verify isVertical/isHorizontal for Edges"""

        # lines
        self.assertTrue(
            PathGeom.isVertical(
                Part.Edge(
                    Part.LineSegment(Vector(-1, -1, -1), Vector(-1, -1, 8)))))
        self.assertFalse(
            PathGeom.isVertical(
                Part.Edge(
                    Part.LineSegment(Vector(-1, -1, -1), Vector(1, -1, 8)))))
        self.assertFalse(
            PathGeom.isVertical(
                Part.Edge(
                    Part.LineSegment(Vector(-1, -1, -1), Vector(-1, 1, 8)))))

        self.assertTrue(
            PathGeom.isHorizontal(
                Part.Edge(
                    Part.LineSegment(Vector(1, -1, -1), Vector(-1, -1, -1)))))
        self.assertTrue(
            PathGeom.isHorizontal(
                Part.Edge(
                    Part.LineSegment(Vector(-1, 1, -1), Vector(-1, -1, -1)))))
        self.assertTrue(
            PathGeom.isHorizontal(
                Part.Edge(
                    Part.LineSegment(Vector(1, 1, -1), Vector(-1, -1, -1)))))
        self.assertFalse(
            PathGeom.isHorizontal(
                Part.Edge(Part.LineSegment(Vector(1, -1, -1), Vector(1, -1,
                                                                     8)))))
        self.assertFalse(
            PathGeom.isHorizontal(
                Part.Edge(Part.LineSegment(Vector(-1, 1, -1), Vector(-1, 1,
                                                                     8)))))

        # circles
        self.assertTrue(
            PathGeom.isVertical(
                Part.Edge(Part.makeCircle(4, Vector(), Vector(0, 1, 0)))))
        self.assertTrue(
            PathGeom.isVertical(
                Part.Edge(Part.makeCircle(4, Vector(), Vector(1, 0, 0)))))
        self.assertTrue(
            PathGeom.isVertical(
                Part.Edge(Part.makeCircle(4, Vector(), Vector(1, 1, 0)))))
        self.assertFalse(
            PathGeom.isVertical(
                Part.Edge(Part.makeCircle(4, Vector(), Vector(1, 1, 1)))))

        self.assertTrue(
            PathGeom.isHorizontal(
                Part.Edge(Part.makeCircle(4, Vector(), Vector(0, 0, 1)))))
        self.assertFalse(
            PathGeom.isHorizontal(
                Part.Edge(Part.makeCircle(4, Vector(), Vector(0, 1, 1)))))
        self.assertFalse(
            PathGeom.isHorizontal(
                Part.Edge(Part.makeCircle(4, Vector(), Vector(1, 0, 1)))))
        self.assertFalse(
            PathGeom.isHorizontal(
                Part.Edge(Part.makeCircle(4, Vector(), Vector(1, 1, 1)))))
    def smoothChordCommands(self,
                            bone,
                            inChord,
                            outChord,
                            edge,
                            wire,
                            corner,
                            smooth,
                            color=None):
        if smooth == 0:
            PathLog.info(" No smoothing requested")
            return [bone.lastCommand, outChord.g1Command(bone.F)]

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

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

        pivot = None
        pivotDistance = 0

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

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

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

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

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

            return commands

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

                else:
                    outedges.append(edge)
            else:
                outedges.append(edge)
            i = i + 1
        return outedges
 def connectsTo(self, chord):
     return PathGeom.pointsCoincide(self.End, chord.Start)
Exemple #55
0
 def minZmax(z):
     if hasattr(obj, 'StepDown') and not PathGeom.isRoughly(
             obj.StepDown.Value, 0):
         return z + obj.StepDown.Value
     else:
         return z + 1
Exemple #56
0
    def cleanupEdges(self, edges):
        # want to remove all edges from the wire itself, and all internal struts
        PathLog.track("+cleanupEdges")
        PathLog.debug(" edges:")
        if not edges:
            return edges
        for e in edges:
            debugEdge(e, '   ')
        PathLog.debug(":")
        self.edgesCleanup = [copy.copy(edges)]

        # remove any edge that has a point inside the tag solid
        # and collect all edges that are connected to the entry and/or exit
        self.entryEdges = []
        self.exitEdges = []
        self.edgePoints = []
        for e in copy.copy(edges):
            p1 = e.valueAt(e.FirstParameter)
            p2 = e.valueAt(e.LastParameter)
            self.edgePoints.append(p1)
            self.edgePoints.append(p2)
            if self.tag.solid.isInside(p1, PathGeom.Tolerance,
                                       False) or self.tag.solid.isInside(
                                           p2, PathGeom.Tolerance, False):
                edges.remove(e)
                debugEdge(e, '......... X0', False)
            else:
                if PathGeom.pointsCoincide(
                        p1, self.entry) or PathGeom.pointsCoincide(
                            p2, self.entry):
                    self.entryEdges.append(e)
                if PathGeom.pointsCoincide(
                        p1, self.exit) or PathGeom.pointsCoincide(
                            p2, self.exit):
                    self.exitEdges.append(e)
        self.edgesCleanup.append(copy.copy(edges))

        # if there are no edges connected to entry/exit, it means the plunge in/out is vertical
        # we need to add in the missing segment and collect the new entry/exit edges.
        if not self.entryEdges:
            print("fill entryEdges ...")
            self.realEntry = sorted(self.edgePoints,
                                    key=lambda p: (p - self.entry).Length)[0]
            self.entryEdges = filter(
                lambda e: PathGeom.edgeConnectsTo(e, self.realEntry), edges)
            edges.append(
                Part.Edge(Part.LineSegment(self.entry, self.realEntry)))
        else:
            self.realEntry = None
        if not self.exitEdges:
            print("fill exitEdges ...")
            self.realExit = sorted(self.edgePoints,
                                   key=lambda p: (p - self.exit).Length)[0]
            self.exitEdges = filter(
                lambda e: PathGeom.edgeConnectsTo(e, self.realExit), edges)
            edges.append(Part.Edge(Part.LineSegment(self.realExit, self.exit)))
        else:
            self.realExit = None
        self.edgesCleanup.append(copy.copy(edges))

        # if there are 2 edges attached to entry/exit, throw away the one that is "lower"
        if len(self.entryEdges) > 1:
            debugEdge(self.entryEdges[0], ' entry[0]', False)
            debugEdge(self.entryEdges[1], ' entry[1]', False)
            if self.entryEdges[0].BoundBox.ZMax < self.entryEdges[
                    1].BoundBox.ZMax:
                edges.remove(self.entryEdges[0])
                debugEdge(e, '......... X1', False)
            else:
                edges.remove(self.entryEdges[1])
                debugEdge(e, '......... X2', False)
        if len(self.exitEdges) > 1:
            debugEdge(self.exitEdges[0], ' exit[0]', False)
            debugEdge(self.exitEdges[1], ' exit[1]', False)
            if self.exitEdges[0].BoundBox.ZMax < self.exitEdges[
                    1].BoundBox.ZMax:
                if self.exitEdges[0] in edges:
                    edges.remove(self.exitEdges[0])
                debugEdge(e, '......... X3', False)
            else:
                if self.exitEdges[1] in edges:
                    edges.remove(self.exitEdges[1])
                debugEdge(e, '......... X4', False)

        self.edgesCleanup.append(copy.copy(edges))
        return edges
Exemple #57
0
 def needToFlipEdge(self, edge, p):
     if PathGeom.pointsCoincide(edge.valueAt(edge.LastParameter), p):
         return True, edge.valueAt(edge.FirstParameter)
     return False, edge.valueAt(edge.LastParameter)
Exemple #58
0
    def execute(self, obj):
        PathLog.track()
        if not obj.Base:
            PathLog.error(translate('Path_DressupTag',
                                    'No Base object found.'))
            return
        if not obj.Base.isDerivedFrom('Path::Feature'):
            PathLog.error(
                translate('Path_DressupTag',
                          'Base is not a Path::Feature object.'))
            return
        if not obj.Base.Path:
            PathLog.error(
                translate('Path_DressupTag',
                          'Base doesn\'t have a Path to dress-up.'))
            return
        if not obj.Base.Path.Commands:
            PathLog.error(translate('Path_DressupTag', 'Base Path is empty.'))
            return

        self.obj = obj

        minZ = +sys.maxint
        minX = minZ
        minY = minZ

        maxZ = -sys.maxint
        maxX = maxZ
        maxY = maxZ

        # the assumption is that all helixes are in the xy-plane - in other words there is no
        # intermittent point of a command that has a lower/higher Z-position than the start and
        # and end positions of a command.
        lastPt = FreeCAD.Vector(0, 0, 0)
        for cmd in obj.Base.Path.Commands:
            pt = PathGeom.commandEndPoint(cmd, lastPt)
            if lastPt.x != pt.x:
                maxX = max(pt.x, maxX)
                minX = min(pt.x, minX)
            if lastPt.y != pt.y:
                maxY = max(pt.y, maxY)
                minY = min(pt.y, minY)
            if lastPt.z != pt.z:
                maxZ = max(pt.z, maxZ)
                minZ = min(pt.z, minZ)
            lastPt = pt
        PathLog.debug("bb = (%.2f, %.2f, %.2f) ... (%.2f, %.2f, %.2f)" %
                      (minX, minY, minZ, maxX, maxY, maxZ))
        self.ptMin = FreeCAD.Vector(minX, minY, minZ)
        self.ptMax = FreeCAD.Vector(maxX, maxY, maxZ)
        self.masterSolid = TagSolid(self, minZ, self.toolRadius())
        self.solids = [
            self.masterSolid.cloneAt(pos) for pos in self.obj.Positions
        ]
        self.tagSolid = Part.Compound(self.solids)

        self.wire, rapid = PathGeom.wireForPath(obj.Base.Path)
        self.edges = self.wire.Edges

        maxTagZ = minZ + obj.Height.Value

        # lastX = 0
        # lastY = 0
        lastZ = 0

        commands = []

        for cmd in obj.Base.Path.Commands:
            if cmd in PathGeom.CmdMove:
                if lastZ <= maxTagZ or cmd.Parameters.get('Z',
                                                          lastZ) <= maxTagZ:
                    pass
                else:
                    commands.append(cmd)
            else:
                commands.append(cmd)

        obj.Path = obj.Base.Path

        PathLog.track()