From 0fabe5c079da5e332a04f16c8b672d3a4ed33748 Mon Sep 17 00:00:00 2001 From: Markus Lampert Date: Mon, 5 Dec 2016 14:37:36 -0800 Subject: [PATCH] Fixed helix construction. --- src/Mod/Path/PathScripts/PathGeom.py | 107 +++++++++++++++++++++---- src/Mod/Path/PathTests/TestPathGeom.py | 66 +++++++++++++++ 2 files changed, 158 insertions(+), 15 deletions(-) diff --git a/src/Mod/Path/PathScripts/PathGeom.py b/src/Mod/Path/PathScripts/PathGeom.py index 7e06829d8..9280a7f51 100644 --- a/src/Mod/Path/PathScripts/PathGeom.py +++ b/src/Mod/Path/PathScripts/PathGeom.py @@ -128,6 +128,33 @@ class PathGeom: Convenience function to return the projection of the Vector in the XY-plane.""" return Vector(point.x, point.y, 0) + @classmethod + def cmdForEdge(cls, edge): + pt = edge.valueAt(edge.LastParameter) + params = {'X': pt.x, 'Y': pt.y, 'Z': pt.z} + if type(edge.Curve) == Part.Line or type(edge.Curve) == Part.LineSegment: + command = Path.Command('G1', params) + else: + p1 = edge.valueAt(edge.FirstParameter) + p2 = edge.valueAt((edge.FirstParameter + edge.LastParameter)/2) + p3 = pt + if Side.Left == Side.of(p2 - p1, p3 - p2): + cmd = 'G3' + else: + cmd = 'G2' + pa = PathGeom.xy(p1) + pb = PathGeom.xy(p2) + pc = PathGeom.xy(p3) + pd = Part.Circle(PathGeom.xy(p1), PathGeom.xy(p2), PathGeom.xy(p3)).Center + #print("**** (%.2f, %.2f, %.2f) - (%.2f, %.2f, %.2f)" % (pa.x, pa.y, pa.z, pc.x, pc.y, pc.z)) + #print("**** (%.2f, %.2f, %.2f) - (%.2f, %.2f, %.2f)" % (pb.x, pb.y, pb.z, pd.x, pd.y, pd.z)) + offset = Part.Circle(PathGeom.xy(p1), PathGeom.xy(p2), PathGeom.xy(p3)).Center - p1 + #print("**** (%.2f, %.2f, %.2f)" % (offset.x, offset.y, offset.z)) + params.update({'I': offset.x, 'J': offset.y, 'K': (p3.z - p1.z)/2}) + command = Path.Command(cmd, params) + #print command + return command + @classmethod def edgeForCmd(cls, cmd, startPoint): """(cmd, startPoint). @@ -215,26 +242,76 @@ class PathGeom: @classmethod def arcToHelix(cls, edge, z0, z1): - m = FreeCAD.Matrix() - m.unity() + """(edge, z0, z1) + Assuming edge is an arc it'll return a helix matching the arc starting at z0 and rising/falling to z1.""" + p1 = edge.valueAt(edge.FirstParameter) p2 = edge.valueAt(edge.LastParameter) - z = p1.z - pd = p2 - p1 - dz = z1 - z0 + cmd = cls.cmdForEdge(edge) + params = cmd.Parameters + params.update({'Z': z1, 'K': (z1 - z0)/2}) + command = Path.Command(cmd.Name, params) - #print("arcToHelix(%.2f, %.2f): dz=%.2f, dy=%.2f, z=%.2f" % (z0 ,z1, dz, pd.y, z)) + return cls.edgeForCmd(command, FreeCAD.Vector(p1.x, p1.y, z0)) - m.A32 = dz / pd.y - m.A34 = - m.A32 - if dz < 0: - m.A34 *= p2.y - m.A34 += z1 - z + + @classmethod + def helixToArc(cls, edge, z = 0): + """(edge, z=0) + Returns the projection of the helix onto the XY-plane with a given offset.""" + p1 = edge.valueAt(edge.FirstParameter) + p2 = edge.valueAt((edge.FirstParameter + edge.LastParameter)/2) + p3 = edge.valueAt(edge.LastParameter) + p01 = FreeCAD.Vector(p1.x, p1.y, z) + p02 = FreeCAD.Vector(p2.x, p2.y, z) + p03 = FreeCAD.Vector(p3.x, p3.y, z) + return Part.Edge(Part.Arc(p01, p02, p03)) + + @classmethod + def splitArcAt(cls, edge, pt): + """(edge, pt) + Returns a list of 2 edges which together form the original arc split at the given point. + The Vector pt has to represnt a point on the given arc.""" + p1 = edge.valueAt(edge.FirstParameter) + p2 = pt + p3 = edge.valueAt(edge.LastParameter) + edges = [] + + p = edge.Curve.parameter(p2) + #print("splitArcAt(%.2f, %.2f, %.2f): %.2f - %.2f - %.2f" % (pt.x, pt.y, pt.z, edge.FirstParameter, p, edge.LastParameter)) + + p12 = edge.Curve.value((edge.FirstParameter + p)/2) + p23 = edge.Curve.value((p + edge.LastParameter)/2) + #print("splitArcAt: p12=(%.2f, %.2f, %.2f) p23=(%.2f, %.2f, %.2f)" % (p12.x, p12.y, p12.z, p23.x, p23.y, p23.z)) + + edges.append(Part.Edge(Part.Arc(p1, p12, p2))) + edges.append(Part.Edge(Part.Arc(p2, p23, p3))) + + return edges + + @classmethod + def splitEdgeAt(cls, edge, pt): + """(edge, pt) + Returns a list of 2 edges, forming the original edge split at the given point. + The results are undefined if the Vector representing the point is not part of the edge.""" + # I could not get the OCC parameterAt and split to work ... + # pt HAS to be on the edge, otherwise the results are undefined + p1 = edge.valueAt(edge.FirstParameter) + p2 = pt + p3 = edge.valueAt(edge.LastParameter) + edges = [] + + if type(edge.Curve) == Part.Line or type(edge.Curve) == Part.LineSegment: + # it's a line + return [Part.Edge(Part.LineSegment(p1, p2)), Part.Edge(Part.LineSegment(p2, p3))] + elif type(edge.Curve) == Part.Circle: + # it's an arc + return cls.splitArcAt(edge, pt) else: - m.A34 *= p1.y - m.A34 += z0 - z + # it's a helix + arc = cls.helixToArc(edge, 0) + aes = cls.splitArcAt(arc, FreeCAD.Vector(pt.x, pt.y, 0)) + return [cls.arcToHelix(aes[0], p1.z, p2.z), cls.arcToHelix(aes[1], p2.z, p3.z)] - e = edge.transformGeometry(m).Edges[0] - return e diff --git a/src/Mod/Path/PathTests/TestPathGeom.py b/src/Mod/Path/PathTests/TestPathGeom.py index 779767ccb..7ab7a5396 100644 --- a/src/Mod/Path/PathTests/TestPathGeom.py +++ b/src/Mod/Path/PathTests/TestPathGeom.py @@ -172,3 +172,69 @@ class TestPathGeom(PathTestBase): 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)) + + + def test62(self): + """Verify splitArcAt returns proper subarcs.""" + p1 = Vector(10,-10,0) + p2 = Vector(0,0,0) + p3 = Vector(10,10,0) + + arc = Part.Edge(Part.Arc(p1, p2, p3)) + + o = 10*math.sin(math.pi/4) + p12 = Vector(10 - o, -o, 0) + p23 = Vector(10 - o, +o, 0) + + e = PathGeom.splitArcAt(arc, p2) + self.assertCurve(e[0], p1, p12, p2) + self.assertCurve(e[1], p2, p23, p3) + + p34 = Vector(10 - 10*math.sin(1*math.pi/8), -10*math.cos(1*math.pi/8), 0) + p45 = Vector(10 - 10*math.sin(5*math.pi/8), -10*math.cos(5*math.pi/8), 0) + + e = PathGeom.splitArcAt(arc, p12) + self.assertCurve(e[0], p1, p34, p12) + self.assertCurve(e[1], p12, p45, p3) + + + 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)