Smooth path with fillets.

This commit is contained in:
ml 2016-10-16 23:46:00 -07:00 committed by Markus Lampert
parent 0ba1030163
commit 23713a2c51
3 changed files with 244 additions and 181 deletions

View File

@ -1,7 +1,7 @@
#*************************************************************************** #***************************************************************************
#* * #* *
#* Copyright (c) 2009, 2010 * #* Copyright (c) 2009, 2010 *
#* Yorik van Havre <yorik@uncreated.net>, Ken Cline <cline@frii.com> * #* Yorik van Havre <yorik@uncreated.net>, Ken Cline <cline@frii.com> *
#* * #* *
#* This program is free software; you can redistribute it and/or modify * #* This program is free software; you can redistribute it and/or modify *
#* it under the terms of the GNU Lesser General Public License (LGPL) * #* it under the terms of the GNU Lesser General Public License (LGPL) *
@ -133,7 +133,7 @@ def isAligned(edge,axis="x"):
if edge.StartPoint.z == edge.EndPoint.z: if edge.StartPoint.z == edge.EndPoint.z:
return True return True
return False return False
def getQuad(face): def getQuad(face):
"""getQuad(face): returns a list of 3 vectors (basepoint, Xdir, Ydir) if the face """getQuad(face): returns a list of 3 vectors (basepoint, Xdir, Ydir) if the face
is a quad, or None if not.""" is a quad, or None if not."""
@ -162,7 +162,7 @@ def areColinear(e1,e2):
return False return False
v1 = vec(e1) v1 = vec(e1)
v2 = vec(e2) v2 = vec(e2)
a = round(v1.getAngle(v2),precision()) a = round(v1.getAngle(v2),precision())
if (a == 0) or (a == round(math.pi,precision())): if (a == 0) or (a == round(math.pi,precision())):
v3 = e2.Vertexes[0].Point.sub(e1.Vertexes[0].Point) v3 = e2.Vertexes[0].Point.sub(e1.Vertexes[0].Point)
if DraftVecUtils.isNull(v3): if DraftVecUtils.isNull(v3):
@ -225,7 +225,7 @@ def findEdge(anEdge,aList):
if DraftVecUtils.equals(anEdge.Vertexes[-1].Point,aList[e].Vertexes[-1].Point): if DraftVecUtils.equals(anEdge.Vertexes[-1].Point,aList[e].Vertexes[-1].Point):
return(e) return(e)
return None return None
def findIntersection(edge1,edge2,infinite1=False,infinite2=False,ex1=False,ex2=False,dts=True) : def findIntersection(edge1,edge2,infinite1=False,infinite2=False,ex1=False,ex2=False,dts=True) :
'''findIntersection(edge1,edge2,infinite1=False,infinite2=False,dts=True): '''findIntersection(edge1,edge2,infinite1=False,infinite2=False,dts=True):
@ -268,13 +268,13 @@ def findIntersection(edge1,edge2,infinite1=False,infinite2=False,ex1=False,ex2=F
(pt3.x-pt1.x)*(vec2.y-vec2.z))/(norm3.x+norm3.y+norm3.z) (pt3.x-pt1.x)*(vec2.y-vec2.z))/(norm3.x+norm3.y+norm3.z)
vec1.scale(k,k,k) vec1.scale(k,k,k)
intp = pt1.add(vec1) intp = pt1.add(vec1)
if infinite1 == False and not isPtOnEdge(intp,edge1) : if infinite1 == False and not isPtOnEdge(intp,edge1) :
return [] return []
if infinite2 == False and not isPtOnEdge(intp,edge2) : if infinite2 == False and not isPtOnEdge(intp,edge2) :
return [] return []
return [intp] return [intp]
else : else :
return [] # Lines have same direction return [] # Lines have same direction
@ -303,26 +303,26 @@ def findIntersection(edge1,edge2,infinite1=False,infinite2=False,ex1=False,ex2=F
infinite2 = ex2 infinite2 = ex2
return getLineIntersections(pt1,pt2,pt3,pt4,infinite1,infinite2) return getLineIntersections(pt1,pt2,pt3,pt4,infinite1,infinite2)
elif (geomType(edge1) == "Line") and (geomType(edge2) == "Line") : elif (geomType(edge1) == "Line") and (geomType(edge2) == "Line") :
# we have 2 straight lines # we have 2 straight lines
pt1, pt2, pt3, pt4 = [edge1.Vertexes[0].Point, pt1, pt2, pt3, pt4 = [edge1.Vertexes[0].Point,
edge1.Vertexes[1].Point, edge1.Vertexes[1].Point,
edge2.Vertexes[0].Point, edge2.Vertexes[0].Point,
edge2.Vertexes[1].Point] edge2.Vertexes[1].Point]
return getLineIntersections(pt1,pt2,pt3,pt4,infinite1,infinite2) return getLineIntersections(pt1,pt2,pt3,pt4,infinite1,infinite2)
elif (geomType(edge1) == "Circle") and (geomType(edge2) == "Line") \ elif (geomType(edge1) == "Circle") and (geomType(edge2) == "Line") \
or (geomType(edge1) == "Line") and (geomType(edge2) == "Circle") : or (geomType(edge1) == "Line") and (geomType(edge2) == "Circle") :
# deals with an arc or circle and a line # deals with an arc or circle and a line
edges = [edge1,edge2] edges = [edge1,edge2]
for edge in edges : for edge in edges :
if geomType(edge) == "Line": if geomType(edge) == "Line":
line = edge line = edge
else : else :
arc = edge arc = edge
dirVec = vec(line) ; dirVec.normalize() dirVec = vec(line) ; dirVec.normalize()
pt1 = line.Vertexes[0].Point pt1 = line.Vertexes[0].Point
pt2 = line.Vertexes[1].Point pt2 = line.Vertexes[1].Point
@ -335,15 +335,15 @@ def findIntersection(edge1,edge2,infinite1=False,infinite2=False,ex1=False,ex2=F
return [pt1] return [pt1]
elif (pt2 in [pt3,pt4]): elif (pt2 in [pt3,pt4]):
return [pt2] return [pt2]
if DraftVecUtils.isNull(pt1.sub(center).cross(pt2.sub(center)).cross(arc.Curve.Axis)) : if DraftVecUtils.isNull(pt1.sub(center).cross(pt2.sub(center)).cross(arc.Curve.Axis)) :
# Line and Arc are on same plane # Line and Arc are on same plane
dOnLine = center.sub(pt1).dot(dirVec) dOnLine = center.sub(pt1).dot(dirVec)
onLine = Vector(dirVec) onLine = Vector(dirVec)
onLine.scale(dOnLine,dOnLine,dOnLine) onLine.scale(dOnLine,dOnLine,dOnLine)
toLine = pt1.sub(center).add(onLine) toLine = pt1.sub(center).add(onLine)
if toLine.Length < arc.Curve.Radius : if toLine.Length < arc.Curve.Radius :
dOnLine = (arc.Curve.Radius**2 - toLine.Length**2)**(0.5) dOnLine = (arc.Curve.Radius**2 - toLine.Length**2)**(0.5)
onLine = Vector(dirVec) onLine = Vector(dirVec)
@ -356,8 +356,8 @@ def findIntersection(edge1,edge2,infinite1=False,infinite2=False,ex1=False,ex2=F
int = [center.add(toLine)] int = [center.add(toLine)]
else : else :
return [] return []
else : else :
# Line isn't on Arc's plane # Line isn't on Arc's plane
if dirVec.dot(arc.Curve.Axis) != 0 : if dirVec.dot(arc.Curve.Axis) != 0 :
toPlane = Vector(arc.Curve.Axis) ; toPlane.normalize() toPlane = Vector(arc.Curve.Axis) ; toPlane.normalize()
@ -374,7 +374,7 @@ def findIntersection(edge1,edge2,infinite1=False,infinite2=False,ex1=False,ex2=F
return [] return []
else : else :
return [] return []
if infinite1 == False : if infinite1 == False :
for i in range(len(int)-1,-1,-1) : for i in range(len(int)-1,-1,-1) :
if not isPtOnEdge(int[i],edge1) : if not isPtOnEdge(int[i],edge1) :
@ -384,16 +384,16 @@ def findIntersection(edge1,edge2,infinite1=False,infinite2=False,ex1=False,ex2=F
if not isPtOnEdge(int[i],edge2) : if not isPtOnEdge(int[i],edge2) :
del int[i] del int[i]
return int return int
elif (geomType(edge1) == "Circle") and (geomType(edge2) == "Circle") : elif (geomType(edge1) == "Circle") and (geomType(edge2) == "Circle") :
# deals with 2 arcs or circles # deals with 2 arcs or circles
cent1, cent2 = edge1.Curve.Center, edge2.Curve.Center cent1, cent2 = edge1.Curve.Center, edge2.Curve.Center
rad1 , rad2 = edge1.Curve.Radius, edge2.Curve.Radius rad1 , rad2 = edge1.Curve.Radius, edge2.Curve.Radius
axis1, axis2 = edge1.Curve.Axis , edge2.Curve.Axis axis1, axis2 = edge1.Curve.Axis , edge2.Curve.Axis
c2c = cent2.sub(cent1) c2c = cent2.sub(cent1)
if DraftVecUtils.isNull(axis1.cross(axis2)) : if DraftVecUtils.isNull(axis1.cross(axis2)) :
if round(c2c.dot(axis1),precision()) == 0 : if round(c2c.dot(axis1),precision()) == 0 :
# circles are on same plane # circles are on same plane
@ -432,7 +432,7 @@ def findIntersection(edge1,edge2,infinite1=False,infinite2=False,ex1=False,ex2=F
for pt in intTemp : for pt in intTemp :
if round(pt.sub(cent2).Length-rad2,precision()) == 0 : if round(pt.sub(cent2).Length-rad2,precision()) == 0 :
int += [pt] int += [pt]
if infinite1 == False : if infinite1 == False :
for i in range(len(int)-1,-1,-1) : for i in range(len(int)-1,-1,-1) :
if not isPtOnEdge(int[i],edge1) : if not isPtOnEdge(int[i],edge1) :
@ -441,12 +441,12 @@ def findIntersection(edge1,edge2,infinite1=False,infinite2=False,ex1=False,ex2=F
for i in range(len(int)-1,-1,-1) : for i in range(len(int)-1,-1,-1) :
if not isPtOnEdge(int[i],edge2) : if not isPtOnEdge(int[i],edge2) :
del int[i] del int[i]
return int return int
else: else:
# print("DraftGeomUtils: Unsupported curve type: (" + str(edge1.Curve) + ", " + str(edge2.Curve) + ")") # print("DraftGeomUtils: Unsupported curve type: (" + str(edge1.Curve) + ", " + str(edge2.Curve) + ")")
return [] return []
def wiresIntersect(wire1,wire2): def wiresIntersect(wire1,wire2):
"wiresIntersect(wire1,wire2): returns True if some of the edges of the wires are intersecting otherwise False" "wiresIntersect(wire1,wire2): returns True if some of the edges of the wires are intersecting otherwise False"
for e1 in wire1.Edges: for e1 in wire1.Edges:
@ -454,7 +454,7 @@ def wiresIntersect(wire1,wire2):
if findIntersection(e1,e2,dts=False): if findIntersection(e1,e2,dts=False):
return True return True
return False return False
def pocket2d(shape,offset): def pocket2d(shape,offset):
"""pocket2d(shape,offset): return a list of wires obtained from offsetting the wires from the given shape """pocket2d(shape,offset): return a list of wires obtained from offsetting the wires from the given shape
by the given offset, and intersection if needed.""" by the given offset, and intersection if needed."""
@ -551,7 +551,7 @@ def mirror (point, edge):
return refl return refl
else: else:
return None return None
def isClockwise(edge,ref=None): def isClockwise(edge,ref=None):
"""Returns True if a circle-based edge has a clockwise direction""" """Returns True if a circle-based edge has a clockwise direction"""
if not geomType(edge) == "Circle": if not geomType(edge) == "Circle":
@ -572,7 +572,7 @@ def isClockwise(edge,ref=None):
if n.z < 0: if n.z < 0:
return False return False
return True return True
def isSameLine(e1,e2): def isSameLine(e1,e2):
"""isSameLine(e1,e2): return True if the 2 edges are lines and have the same """isSameLine(e1,e2): return True if the 2 edges are lines and have the same
points""" points"""
@ -587,7 +587,7 @@ def isSameLine(e1,e2):
(DraftVecUtils.equals(e1.Vertexes[0].Point,e2.Vertexes[-1].Point)): (DraftVecUtils.equals(e1.Vertexes[0].Point,e2.Vertexes[-1].Point)):
return True return True
return False return False
def isWideAngle(edge): def isWideAngle(edge):
"""returns True if the given edge is an arc with angle > 180 degrees""" """returns True if the given edge is an arc with angle > 180 degrees"""
if geomType(edge) != "Circle": if geomType(edge) != "Circle":
@ -657,9 +657,9 @@ def isLine(bsp):
def sortEdges(edges): def sortEdges(edges):
"Deprecated. Use Part.__sortEdges__ instead" "Deprecated. Use Part.__sortEdges__ instead"
raise DeprecationWarning("Deprecated. Use Part.__sortEdges__ instead") raise DeprecationWarning("Deprecated. Use Part.__sortEdges__ instead")
# Build a dictionary of edges according to their end points. # Build a dictionary of edges according to their end points.
# Each entry is a set of edges that starts, or ends, at the # Each entry is a set of edges that starts, or ends, at the
# given vertex hash. # given vertex hash.
@ -685,7 +685,7 @@ def sortEdges(edges):
if v not in edict and len(se) == 1: if v not in edict and len(se) == 1:
startedge = se startedge = se
break break
# The above may not find a start vertex; if the start edge is reversed, # The above may not find a start vertex; if the start edge is reversed,
# the start vertex will appear in edict (and not sdict). # the start vertex will appear in edict (and not sdict).
if not startedge: if not startedge:
for v, se in edict.items(): for v, se in edict.items():
@ -735,9 +735,9 @@ def sortEdges(edges):
def sortEdgesOld(lEdges, aVertex=None): def sortEdgesOld(lEdges, aVertex=None):
"Deprecated. Use Part.__sortEdges__ instead" "Deprecated. Use Part.__sortEdges__ instead"
raise DeprecationWarning("Deprecated. Use Part.__sortEdges__ instead") raise DeprecationWarning("Deprecated. Use Part.__sortEdges__ instead")
#There is no reason to limit this to lines only because every non-closed edge always #There is no reason to limit this to lines only because every non-closed edge always
#has exactly two vertices (wmayer) #has exactly two vertices (wmayer)
#for e in lEdges: #for e in lEdges:
@ -781,7 +781,7 @@ def sortEdgesOld(lEdges, aVertex=None):
else: else:
return lEdges return lEdges
olEdges = [] # ol stands for ordered list olEdges = [] # ol stands for ordered list
if aVertex == None: if aVertex == None:
for i in range(len(lEdges)*2) : for i in range(len(lEdges)*2) :
if len(lEdges[i/2].Vertexes) > 1: if len(lEdges[i/2].Vertexes) > 1:
@ -791,7 +791,7 @@ def sortEdgesOld(lEdges, aVertex=None):
return olEdges return olEdges
# if the wire is closed there is no end so choose 1st Vertex # if the wire is closed there is no end so choose 1st Vertex
# print("closed wire, starting from ",lEdges[0].Vertexes[0].Point) # print("closed wire, starting from ",lEdges[0].Vertexes[0].Point)
return sortEdgesOld(lEdges, lEdges[0].Vertexes[0]) return sortEdgesOld(lEdges, lEdges[0].Vertexes[0])
else : else :
#print("looking ",aVertex.Point) #print("looking ",aVertex.Point)
result = lookfor(aVertex,lEdges) result = lookfor(aVertex,lEdges)
@ -876,7 +876,7 @@ def findWires(edgeslist):
if DraftVecUtils.equals(e1.Vertexes[-1].Point,e2.Vertexes[-1].Point): if DraftVecUtils.equals(e1.Vertexes[-1].Point,e2.Vertexes[-1].Point):
return True return True
return False return False
edges = edgeslist[:] edges = edgeslist[:]
wires = [] wires = []
lost = [] lost = []
@ -916,7 +916,7 @@ def findWires(edgeslist):
else: else:
nwires.append(wi) nwires.append(wi)
return nwires return nwires
def superWire(edgeslist,closed=False): def superWire(edgeslist,closed=False):
'''superWire(edges,[closed]): forces a wire between edges that don't necessarily '''superWire(edges,[closed]): forces a wire between edges that don't necessarily
have coincident endpoints. If closed=True, wire will always be closed''' have coincident endpoints. If closed=True, wire will always be closed'''
@ -1089,7 +1089,7 @@ def getNormal(shape):
if FreeCAD.GuiUp: if FreeCAD.GuiUp:
import Draft import Draft
vdir = Draft.get3DView().getViewDirection() vdir = Draft.get3DView().getViewDirection()
if n.getAngle(vdir) < 0.78: if n.getAngle(vdir) < 0.78:
n = n.negative() n = n.negative()
return n return n
@ -1193,7 +1193,7 @@ def connect(edges,closed=False):
i = findIntersection(curr,prev,True,True) i = findIntersection(curr,prev,True,True)
if i: if i:
v1 = i[DraftVecUtils.closest(curr.Vertexes[0].Point,i)] v1 = i[DraftVecUtils.closest(curr.Vertexes[0].Point,i)]
else: else:
v1 = curr.Vertexes[0].Point v1 = curr.Vertexes[0].Point
else: else:
v1 = curr.Vertexes[0].Point v1 = curr.Vertexes[0].Point
@ -1203,7 +1203,7 @@ def connect(edges,closed=False):
if i: if i:
v2 = i[DraftVecUtils.closest(curr.Vertexes[-1].Point,i)] v2 = i[DraftVecUtils.closest(curr.Vertexes[-1].Point,i)]
else: else:
v2 = curr.Vertexes[-1].Point v2 = curr.Vertexes[-1].Point
else: else:
v2 = curr.Vertexes[-1].Point v2 = curr.Vertexes[-1].Point
if geomType(curr) == "Line": if geomType(curr) == "Line":
@ -1444,7 +1444,7 @@ def cleanFaces(shape):
if ee.hashCode() in eset: if ee.hashCode() in eset:
return i return i
return None return None
# build lookup table # build lookup table
lut = {} lut = {}
for face in faceset: for face in faceset:
@ -1655,7 +1655,7 @@ def fillet(lEdges,r,chamfer=False):
Returns a list of sorted edges describing a round corner''' Returns a list of sorted edges describing a round corner'''
def getCurveType(edge,existingCurveType = None): def getCurveType(edge,existingCurveType = None):
'''Builds or completes a dictionnary containing edges with keys "Arc" and "Line"''' '''Builds or completes a dictionnary containing edges with keys "Arc" and "Line"'''
if not existingCurveType : if not existingCurveType :
existingCurveType = { 'Line' : [], 'Arc' : [] } existingCurveType = { 'Line' : [], 'Arc' : [] }
if issubclass(type(edge.Curve),Part.Line) : if issubclass(type(edge.Curve),Part.Line) :
@ -1665,51 +1665,51 @@ def fillet(lEdges,r,chamfer=False):
else : else :
raise ValueError("Edge's curve must be either Line or Arc") raise ValueError("Edge's curve must be either Line or Arc")
return existingCurveType return existingCurveType
rndEdges = lEdges[0:2] rndEdges = lEdges[0:2]
rndEdges = Part.__sortEdges__(rndEdges) rndEdges = Part.__sortEdges__(rndEdges)
if len(rndEdges) < 2 : if len(rndEdges) < 2 :
return rndEdges return rndEdges
if r <= 0 : if r <= 0 :
print("DraftGeomUtils.fillet : Error : radius is negative.") print("DraftGeomUtils.fillet : Error : radius is negative.")
return rndEdges return rndEdges
curveType = getCurveType(rndEdges[0]) curveType = getCurveType(rndEdges[0])
curveType = getCurveType(rndEdges[1],curveType) curveType = getCurveType(rndEdges[1],curveType)
lVertexes = rndEdges[0].Vertexes + [rndEdges[1].Vertexes[-1]] lVertexes = rndEdges[0].Vertexes + [rndEdges[1].Vertexes[-1]]
if len(curveType['Line']) == 2: if len(curveType['Line']) == 2:
# Deals with 2-line-edges lists -------------------------------------- # Deals with 2-line-edges lists --------------------------------------
U1 = lVertexes[0].Point.sub(lVertexes[1].Point) ; U1.normalize() U1 = lVertexes[0].Point.sub(lVertexes[1].Point) ; U1.normalize()
U2 = lVertexes[2].Point.sub(lVertexes[1].Point) ; U2.normalize() U2 = lVertexes[2].Point.sub(lVertexes[1].Point) ; U2.normalize()
alpha = U1.getAngle(U2) alpha = U1.getAngle(U2)
if chamfer: if chamfer:
# correcting r value so the size of the chamfer = r # correcting r value so the size of the chamfer = r
beta = math.pi - alpha/2 beta = math.pi - alpha/2
r = (r/2)/math.cos(beta) r = (r/2)/math.cos(beta)
if round(alpha,precision()) == 0 or round(alpha - math.pi,precision()) == 0: # Edges have same direction if round(alpha,precision()) == 0 or round(alpha - math.pi,precision()) == 0: # Edges have same direction
print("DraftGeomUtils.fillet : Warning : edges have same direction. Did nothing") print("DraftGeomUtils.fillet : Warning : edges have same direction. Did nothing")
return rndEdges return rndEdges
dToCenter = r / math.sin(alpha/2.) dToCenter = r / math.sin(alpha/2.)
dToTangent = (dToCenter**2-r**2)**(0.5) dToTangent = (dToCenter**2-r**2)**(0.5)
dirVect = Vector(U1) ; dirVect.scale(dToTangent,dToTangent,dToTangent) dirVect = Vector(U1) ; dirVect.scale(dToTangent,dToTangent,dToTangent)
arcPt1 = lVertexes[1].Point.add(dirVect) arcPt1 = lVertexes[1].Point.add(dirVect)
dirVect = U2.add(U1) ; dirVect.normalize() dirVect = U2.add(U1) ; dirVect.normalize()
dirVect.scale(dToCenter-r,dToCenter-r,dToCenter-r) dirVect.scale(dToCenter-r,dToCenter-r,dToCenter-r)
arcPt2 = lVertexes[1].Point.add(dirVect) arcPt2 = lVertexes[1].Point.add(dirVect)
dirVect = Vector(U2) ; dirVect.scale(dToTangent,dToTangent,dToTangent) dirVect = Vector(U2) ; dirVect.scale(dToTangent,dToTangent,dToTangent)
arcPt3 = lVertexes[1].Point.add(dirVect) arcPt3 = lVertexes[1].Point.add(dirVect)
if (dToTangent>lEdges[0].Length) or (dToTangent>lEdges[1].Length) : if (dToTangent>lEdges[0].Length) or (dToTangent>lEdges[1].Length) :
print("DraftGeomUtils.fillet : Error : radius value ", r," is too high") print("DraftGeomUtils.fillet : Error : radius value ", r," is too high")
return rndEdges return rndEdges
@ -1717,15 +1717,23 @@ def fillet(lEdges,r,chamfer=False):
rndEdges[1] = Part.Edge(Part.Line(arcPt1,arcPt3)) rndEdges[1] = Part.Edge(Part.Line(arcPt1,arcPt3))
else: else:
rndEdges[1] = Part.Edge(Part.Arc(arcPt1,arcPt2,arcPt3)) rndEdges[1] = Part.Edge(Part.Arc(arcPt1,arcPt2,arcPt3))
rndEdges[0] = Part.Edge(Part.Line(lVertexes[0].Point,arcPt1))
rndEdges += [Part.Edge(Part.Line(arcPt3,lVertexes[2].Point))] if lVertexes[0].Point == arcPt1:
# fillet consumes entire first edge
rndEdges.pop(0)
else:
rndEdges[0] = Part.Edge(Part.Line(lVertexes[0].Point,arcPt1))
if lVertexes[2].Point != arcPt3:
# fillet does not consume entire second edge
rndEdges += [Part.Edge(Part.Line(arcPt3,lVertexes[2].Point))]
return rndEdges return rndEdges
elif len(curveType['Arc']) == 1 : elif len(curveType['Arc']) == 1 :
# Deals with lists containing an arc and a line ---------------------------------- # Deals with lists containing an arc and a line ----------------------------------
if lEdges[0] in curveType['Arc'] : if lEdges[0] in curveType['Arc'] :
lineEnd = lVertexes[2] ; arcEnd = lVertexes[0] ; arcFirst = True lineEnd = lVertexes[2] ; arcEnd = lVertexes[0] ; arcFirst = True
else : else :
@ -1734,23 +1742,23 @@ def fillet(lEdges,r,chamfer=False):
arcRadius = curveType['Arc'][0].Curve.Radius arcRadius = curveType['Arc'][0].Curve.Radius
arcAxis = curveType['Arc'][0].Curve.Axis arcAxis = curveType['Arc'][0].Curve.Axis
arcLength = curveType['Arc'][0].Length arcLength = curveType['Arc'][0].Length
U1 = lineEnd.Point.sub(lVertexes[1].Point) ; U1.normalize() U1 = lineEnd.Point.sub(lVertexes[1].Point) ; U1.normalize()
toCenter = arcCenter.sub(lVertexes[1].Point) toCenter = arcCenter.sub(lVertexes[1].Point)
if arcFirst : # make sure the tangent points towards the arc if arcFirst : # make sure the tangent points towards the arc
T = arcAxis.cross(toCenter) T = arcAxis.cross(toCenter)
else : else :
T = toCenter.cross(arcAxis) T = toCenter.cross(arcAxis)
projCenter = toCenter.dot(U1) projCenter = toCenter.dot(U1)
if round(abs(projCenter),precision()) > 0 : if round(abs(projCenter),precision()) > 0 :
normToLine = U1.cross(T).cross(U1) normToLine = U1.cross(T).cross(U1)
else : else :
normToLine = Vector(toCenter) normToLine = Vector(toCenter)
normToLine.normalize() normToLine.normalize()
dCenterToLine = toCenter.dot(normToLine) - r dCenterToLine = toCenter.dot(normToLine) - r
if round(projCenter,precision()) > 0 : if round(projCenter,precision()) > 0 :
newRadius = arcRadius - r newRadius = arcRadius - r
elif round(projCenter,precision()) < 0 or (round(projCenter,precision()) == 0 and U1.dot(T) > 0): elif round(projCenter,precision()) < 0 or (round(projCenter,precision()) == 0 and U1.dot(T) > 0):
@ -1758,65 +1766,65 @@ def fillet(lEdges,r,chamfer=False):
else : else :
print("DraftGeomUtils.fillet : Warning : edges are already tangent. Did nothing") print("DraftGeomUtils.fillet : Warning : edges are already tangent. Did nothing")
return rndEdges return rndEdges
toNewCent = newRadius**2-dCenterToLine**2 toNewCent = newRadius**2-dCenterToLine**2
if toNewCent > 0 : if toNewCent > 0 :
toNewCent = abs(abs(projCenter) - toNewCent**(0.5)) toNewCent = abs(abs(projCenter) - toNewCent**(0.5))
else : else :
print("DraftGeomUtils.fillet : Error : radius value ", r," is too high") print("DraftGeomUtils.fillet : Error : radius value ", r," is too high")
return rndEdges return rndEdges
U1.scale(toNewCent,toNewCent,toNewCent) U1.scale(toNewCent,toNewCent,toNewCent)
normToLine.scale(r,r,r) normToLine.scale(r,r,r)
newCent = lVertexes[1].Point.add(U1).add(normToLine) newCent = lVertexes[1].Point.add(U1).add(normToLine)
arcPt1= lVertexes[1].Point.add(U1) arcPt1= lVertexes[1].Point.add(U1)
arcPt2= lVertexes[1].Point.sub(newCent); arcPt2.normalize() arcPt2= lVertexes[1].Point.sub(newCent); arcPt2.normalize()
arcPt2.scale(r,r,r) ; arcPt2 = arcPt2.add(newCent) arcPt2.scale(r,r,r) ; arcPt2 = arcPt2.add(newCent)
if newRadius == arcRadius - r : if newRadius == arcRadius - r :
arcPt3= newCent.sub(arcCenter) arcPt3= newCent.sub(arcCenter)
else : else :
arcPt3= arcCenter.sub(newCent) arcPt3= arcCenter.sub(newCent)
arcPt3.normalize() arcPt3.normalize()
arcPt3.scale(r,r,r) ; arcPt3 = arcPt3.add(newCent) arcPt3.scale(r,r,r) ; arcPt3 = arcPt3.add(newCent)
arcPt = [arcPt1,arcPt2,arcPt3] arcPt = [arcPt1,arcPt2,arcPt3]
# Warning : In the following I used a trick for calling the right element # Warning : In the following I used a trick for calling the right element
# in arcPt or V : arcFirst is a boolean so - not arcFirst is -0 or -1 # in arcPt or V : arcFirst is a boolean so - not arcFirst is -0 or -1
# list[-1] is the last element of a list and list[0] the first # list[-1] is the last element of a list and list[0] the first
# this way I don't have to proceed tests to know the position of the arc # this way I don't have to proceed tests to know the position of the arc
myTrick = not arcFirst myTrick = not arcFirst
V = [arcPt3] V = [arcPt3]
V += [arcEnd.Point] V += [arcEnd.Point]
toCenter.scale(-1,-1,-1) toCenter.scale(-1,-1,-1)
delLength = arcRadius * V[0].sub(arcCenter).getAngle(toCenter) delLength = arcRadius * V[0].sub(arcCenter).getAngle(toCenter)
if delLength > arcLength or toNewCent > curveType['Line'][0].Length: if delLength > arcLength or toNewCent > curveType['Line'][0].Length:
print("DraftGeomUtils.fillet : Error : radius value ", r," is too high") print("DraftGeomUtils.fillet : Error : radius value ", r," is too high")
return rndEdges return rndEdges
arcAsEdge = arcFrom2Pts(V[-arcFirst],V[-myTrick],arcCenter,arcAxis) arcAsEdge = arcFrom2Pts(V[-arcFirst],V[-myTrick],arcCenter,arcAxis)
V = [lineEnd.Point,arcPt1] V = [lineEnd.Point,arcPt1]
lineAsEdge = Part.Edge(Part.Line(V[-arcFirst],V[myTrick])) lineAsEdge = Part.Edge(Part.Line(V[-arcFirst],V[myTrick]))
rndEdges[not arcFirst] = arcAsEdge rndEdges[not arcFirst] = arcAsEdge
rndEdges[arcFirst] = lineAsEdge rndEdges[arcFirst] = lineAsEdge
if chamfer: if chamfer:
rndEdges[1:1] = [Part.Edge(Part.Line(arcPt[- arcFirst],arcPt[- myTrick]))] rndEdges[1:1] = [Part.Edge(Part.Line(arcPt[- arcFirst],arcPt[- myTrick]))]
else: else:
rndEdges[1:1] = [Part.Edge(Part.Arc(arcPt[- arcFirst],arcPt[1],arcPt[- myTrick]))] rndEdges[1:1] = [Part.Edge(Part.Arc(arcPt[- arcFirst],arcPt[1],arcPt[- myTrick]))]
return rndEdges return rndEdges
elif len(curveType['Arc']) == 2 : elif len(curveType['Arc']) == 2 :
# Deals with lists of 2 arc-edges -------------------------------------------- # Deals with lists of 2 arc-edges --------------------------------------------
arcCenter, arcRadius, arcAxis, arcLength, toCenter, T, newRadius = [], [], [], [], [], [], [] arcCenter, arcRadius, arcAxis, arcLength, toCenter, T, newRadius = [], [], [], [], [], [], []
for i in range(2) : for i in range(2) :
arcCenter += [curveType['Arc'][i].Curve.Center] arcCenter += [curveType['Arc'][i].Curve.Center]
@ -1828,7 +1836,7 @@ def fillet(lEdges,r,chamfer=False):
T += [toCenter[1].cross(arcAxis[1])] T += [toCenter[1].cross(arcAxis[1])]
CentToCent = toCenter[1].sub(toCenter[0]) CentToCent = toCenter[1].sub(toCenter[0])
dCentToCent = CentToCent.Length dCentToCent = CentToCent.Length
sameDirection = (arcAxis[0].dot(arcAxis[1]) > 0) sameDirection = (arcAxis[0].dot(arcAxis[1]) > 0)
TcrossT = T[0].cross(T[1]) TcrossT = T[0].cross(T[1])
if sameDirection : if sameDirection :
@ -1844,7 +1852,7 @@ def fillet(lEdges,r,chamfer=False):
else : else :
print("DraftGeomUtils.fillet : Warning : edges are already tangent. Did nothing") print("DraftGeomUtils.fillet : Warning : edges are already tangent. Did nothing")
return rndEdges return rndEdges
elif not sameDirection : elif not sameDirection :
if round(TcrossT.dot(arcAxis[0]),precision()) > 0 : if round(TcrossT.dot(arcAxis[0]),precision()) > 0 :
newRadius += [arcRadius[0]+r] newRadius += [arcRadius[0]+r]
newRadius += [arcRadius[1]-r] newRadius += [arcRadius[1]-r]
@ -1864,16 +1872,16 @@ def fillet(lEdges,r,chamfer=False):
else : else :
print("DraftGeomUtils.fillet : Warning : edges are already tangent. Did nothing") print("DraftGeomUtils.fillet : Warning : edges are already tangent. Did nothing")
return rndEdges return rndEdges
if newRadius[0]+newRadius[1] < dCentToCent or \ if newRadius[0]+newRadius[1] < dCentToCent or \
newRadius[0]-newRadius[1] > dCentToCent or \ newRadius[0]-newRadius[1] > dCentToCent or \
newRadius[1]-newRadius[0] > dCentToCent : newRadius[1]-newRadius[0] > dCentToCent :
print("DraftGeomUtils.fillet : Error : radius value ", r," is too high") print("DraftGeomUtils.fillet : Error : radius value ", r," is too high")
return rndEdges return rndEdges
x = (dCentToCent**2+newRadius[0]**2-newRadius[1]**2)/(2*dCentToCent) x = (dCentToCent**2+newRadius[0]**2-newRadius[1]**2)/(2*dCentToCent)
y = (newRadius[0]**2-x**2)**(0.5) y = (newRadius[0]**2-x**2)**(0.5)
CentToCent.normalize() ; toCenter[0].normalize() ; toCenter[1].normalize() CentToCent.normalize() ; toCenter[0].normalize() ; toCenter[1].normalize()
if abs(toCenter[0].dot(toCenter[1])) != 1 : if abs(toCenter[0].dot(toCenter[1])) != 1 :
normVect = CentToCent.cross(CentToCent.cross(toCenter[0])) normVect = CentToCent.cross(CentToCent.cross(toCenter[0]))
@ -1895,7 +1903,7 @@ def fillet(lEdges,r,chamfer=False):
arcPt2 = newCent.add(toThirdPt) arcPt2 = newCent.add(toThirdPt)
arcPt3 = newCent.add(CentToNewCent[1]) arcPt3 = newCent.add(CentToNewCent[1])
arcPt = [arcPt1,arcPt2,arcPt3] arcPt = [arcPt1,arcPt2,arcPt3]
arcAsEdge = [] arcAsEdge = []
for i in range(2) : for i in range(2) :
toCenter[i].scale(-1,-1,-1) toCenter[i].scale(-1,-1,-1)
@ -1905,21 +1913,21 @@ def fillet(lEdges,r,chamfer=False):
return rndEdges return rndEdges
V = [arcPt[-i],lVertexes[-i].Point] V = [arcPt[-i],lVertexes[-i].Point]
arcAsEdge += [arcFrom2Pts(V[i-1],V[-i],arcCenter[i],arcAxis[i])] arcAsEdge += [arcFrom2Pts(V[i-1],V[-i],arcCenter[i],arcAxis[i])]
rndEdges[0] = arcAsEdge[0] rndEdges[0] = arcAsEdge[0]
rndEdges[1] = arcAsEdge[1] rndEdges[1] = arcAsEdge[1]
if chamfer: if chamfer:
rndEdges[1:1] = [Part.Edge(Part.Line(arcPt[0],arcPt[2]))] rndEdges[1:1] = [Part.Edge(Part.Line(arcPt[0],arcPt[2]))]
else: else:
rndEdges[1:1] = [Part.Edge(Part.Arc(arcPt[0],arcPt[1],arcPt[2]))] rndEdges[1:1] = [Part.Edge(Part.Arc(arcPt[0],arcPt[1],arcPt[2]))]
return rndEdges return rndEdges
def filletWire(aWire,r,chamfer=False): def filletWire(aWire,r,chamfer=False):
''' Fillets each angle of a wire with r as radius value ''' Fillets each angle of a wire with r as radius value
if chamfer is true, a chamfer is made instead and r is the if chamfer is true, a chamfer is made instead and r is the
size of the chamfer''' size of the chamfer'''
edges = aWire.Edges edges = aWire.Edges
edges = Part.__sortEdges__(edges) edges = Part.__sortEdges__(edges)
filEdges = [edges[0]] filEdges = [edges[0]]
@ -1935,7 +1943,7 @@ def filletWire(aWire,r,chamfer=False):
filEdges[-1:] = result[0:2] filEdges[-1:] = result[0:2]
filEdges[0] = result[2] filEdges[0] = result[2]
return Part.Wire(filEdges) return Part.Wire(filEdges)
def getCircleFromSpline(edge): def getCircleFromSpline(edge):
"returns a circle-based edge from a bspline-based edge" "returns a circle-based edge from a bspline-based edge"
if geomType(edge) != "BSplineCurve": if geomType(edge) != "BSplineCurve":
@ -2018,7 +2026,7 @@ def cleanProjection(shape,tessellate=True,seglength=.05):
except: except:
print("Debug: error cleaning edge ",e) print("Debug: error cleaning edge ",e)
return Part.makeCompound(newedges) return Part.makeCompound(newedges)
def curvetosegment(curve,seglen): def curvetosegment(curve,seglen):
points = curve.discretize(seglen) points = curve.discretize(seglen)
p0 = points[0] p0 = points[0]
@ -2049,10 +2057,10 @@ def tessellateProjection(shape,seglen):
except: except:
print("Debug: error cleaning edge ",e) print("Debug: error cleaning edge ",e)
return Part.makeCompound(newedges) return Part.makeCompound(newedges)
def rebaseWire(wire,vidx): def rebaseWire(wire,vidx):
"""rebaseWire(wire,vidx): returns a new wire which is a copy of the """rebaseWire(wire,vidx): returns a new wire which is a copy of the
current wire, but where the first vertex is the vertex indicated by the given current wire, but where the first vertex is the vertex indicated by the given
index vidx, starting from 1. 0 will return an exact copy of the wire.""" index vidx, starting from 1. 0 will return an exact copy of the wire."""
@ -2109,7 +2117,7 @@ def getBoundaryAngles(angle,alist):
if a < higher: if a < higher:
higher = a higher = a
return (lower,higher) return (lower,higher)
def circleFrom2tan1pt(tan1, tan2, point): def circleFrom2tan1pt(tan1, tan2, point):
"circleFrom2tan1pt(edge, edge, Vector)" "circleFrom2tan1pt(edge, edge, Vector)"
@ -2237,7 +2245,7 @@ def circleFrom3LineTangents (edge1, edge2, edge3):
bis23 = angleBisection(edge2,edge3) bis23 = angleBisection(edge2,edge3)
bis31 = angleBisection(edge3,edge1) bis31 = angleBisection(edge3,edge1)
intersections = [] intersections = []
int = findIntersection(bis12, bis23, True, True) int = findIntersection(bis12, bis23, True, True)
if int: if int:
radius = findDistance(int[0],edge1).Length radius = findDistance(int[0],edge1).Length
intersections.append(Part.Circle(int[0],NORM,radius)) intersections.append(Part.Circle(int[0],NORM,radius))
@ -2457,7 +2465,7 @@ def innerSoddyCircle(circle1, circle2, circle3):
print("debug: innerSoddyCircle bad parameters!\n") print("debug: innerSoddyCircle bad parameters!\n")
# FreeCAD.Console.PrintMessage("debug: innerSoddyCircle bad parameters!\n") # FreeCAD.Console.PrintMessage("debug: innerSoddyCircle bad parameters!\n")
return None return None
def circleFrom3CircleTangents(circle1, circle2, circle3): def circleFrom3CircleTangents(circle1, circle2, circle3):
''' '''
http://en.wikipedia.org/wiki/Problem_of_Apollonius#Inversive_methods http://en.wikipedia.org/wiki/Problem_of_Apollonius#Inversive_methods
@ -2560,7 +2568,7 @@ def determinant (mat,n):
else: else:
return 0 return 0
def findHomotheticCenterOfCircles(circle1, circle2): def findHomotheticCenterOfCircles(circle1, circle2):
''' '''
findHomotheticCenterOfCircles(circle1, circle2) findHomotheticCenterOfCircles(circle1, circle2)

View File

@ -27,6 +27,8 @@ import Path
from PathScripts import PathUtils from PathScripts import PathUtils
from PySide import QtCore, QtGui from PySide import QtCore, QtGui
import math import math
import Part
import DraftGeomUtils
"""Dogbone Dressup object and FreeCAD command""" """Dogbone Dressup object and FreeCAD command"""
@ -45,6 +47,20 @@ except AttributeError:
movecommands = ['G0', 'G00', 'G1', 'G01', 'G2', 'G02', 'G3', 'G03'] movecommands = ['G0', 'G00', 'G1', 'G01', 'G2', 'G02', 'G3', 'G03']
movestraight = ['G1', 'G01'] movestraight = ['G1', 'G01']
def debugMarker(vector, label):
obj = FreeCAD.ActiveDocument.addObject("Part::Sphere", label)
obj.Label = label
obj.Radius = 0.5
obj.Placement = FreeCAD.Placement(vector, FreeCAD.Rotation(FreeCAD.Vector(0,0,1), 0))
def debugCircle(vector, r, label):
obj = FreeCAD.ActiveDocument.addObject("Part::Cylinder", label)
obj.Label = label
obj.Radius = r
obj.Height = 1
obj.Placement = FreeCAD.Placement(vector, FreeCAD.Rotation(FreeCAD.Vector(0,0,1), 0))
obj.ViewObject.Transparency = 95
class Style: class Style:
Dogbone = 'Dogbone' Dogbone = 'Dogbone'
Tbone_H = 'T-bone horizontal' Tbone_H = 'T-bone horizontal'
@ -155,6 +171,9 @@ class Chord (object):
def g1Command(self): def g1Command(self):
return Path.Command("G1", {"X": self.End.x, "Y": self.End.y}) return Path.Command("G1", {"X": self.End.x, "Y": self.End.y})
def arcCommand(self, orientation):
return self.g1Command()
def isAPlungeMove(self): def isAPlungeMove(self):
return self.End.z != self.Start.z return self.End.z != self.Start.z
@ -195,7 +214,14 @@ class Chord (object):
else: else:
print("Now this really sucks") print("Now this really sucks")
return (x, y) return FreeCAD.Vector(x, y, self.End.z)
def perpendicular(self):
v = self.asVector()
return FreeCAD.Vector(-v.y, v.x, 0)
def footOfPerpendicularFrom(self, vector):
return self.intersection(Chord(vector, vector + self.perpendicular()))
class ObjectDressup: class ObjectDressup:
@ -235,21 +261,11 @@ class ObjectDressup:
def shouldInsertDogbone(self, obj, inChord, outChord): def shouldInsertDogbone(self, obj, inChord, outChord):
return outChord.foldsBackOrTurns(inChord, self.theOtherSideOf(obj.Side)) return outChord.foldsBackOrTurns(inChord, self.theOtherSideOf(obj.Side))
# draw circles where dogbones go, easier to spot during testing
def debugCircleBone(self, obj,inChord, outChord):
di = 0.
dj = 0.5
if inChord.Start.x < outChord.End.x:
dj = -dj
circle = Path.Command("G1", {"I": di, "J": dj}).Parameters
circle.update({"X": inChord.End.x, "Y": inChord.End.y})
return [ Path.Command("G3", circle) ]
def adaptiveBoneLength(self, obj, inChord, outChord, angle): def adaptiveBoneLength(self, obj, inChord, outChord, angle):
iChord = inChord.offsetBy(self.toolRadius) iChord = inChord.offsetBy(self.toolRadius)
oChord = outChord.offsetBy(self.toolRadius) oChord = outChord.offsetBy(self.toolRadius)
x,y = iChord.intersection(oChord, self.toolRadius) v = iChord.intersection(oChord, self.toolRadius)
dest = inChord.moveTo(FreeCAD.Vector(x, y, inChord.End.z)) dest = inChord.moveTo(FreeCAD.Vector(v.x, v.y, v.z))
destAngle = dest.getAngleXY() destAngle = dest.getAngleXY()
distance = dest.getLength() - self.toolRadius * math.fabs(math.cos(destAngle - angle)) distance = dest.getLength() - self.toolRadius * math.fabs(math.cos(destAngle - angle))
#print("adapt") #print("adapt")
@ -258,13 +274,43 @@ class ObjectDressup:
#print(" = (%.2f, %.2f) -> %.2f (%.2f %.2f) -> %.2f" % (x, y, dest.getLength(), destAngle/math.pi, angle/math.pi, distance)) #print(" = (%.2f, %.2f) -> %.2f (%.2f %.2f) -> %.2f" % (x, y, dest.getLength(), destAngle/math.pi, angle/math.pi, distance))
return distance return distance
def smoothChordCommands(self, inChord, outChord, smooth): def smoothChordCommands(self, inChord, outChord, side, smooth):
if smooth == 0: if smooth == 0:
return [ inChord.g1Command(), outChord.g1Command() ] return [ inChord.g1Command(), outChord.g1Command() ]
print("(%.2f, %.2f) -> (%.2f, %.2f) -> (%.2f, %.2f)" % (inChord.Start.x, inChord.Start.y, inChord.End.x, inChord.End.y, outChord.End.x, outChord.End.y))
inAngle = inChord.getAngleXY() inAngle = inChord.getAngleXY()
outAngle = outChord.getAngleXY() outAngle = outChord.getAngleXY()
if inAngle == outAngle: # straight line, combine g1 print(" inAngle = %.2f outAngle = %.2f" % (inAngle/math.pi, outAngle/math.pi))
return [ Chord(inChord.Start, outChord.End).g1Command() ] if inAngle == outAngle: # straight line, outChord includes inChord
print(" ---> (%.2f, %.2f)" %(outChord.End.x, outChord.End.y))
return [ outChord.g1Command() ]
print("%s :: %s" % (inChord, outChord))
inEdge = DraftGeomUtils.edg(inChord.Start, inChord.End)
outEdge = DraftGeomUtils.edg(outChord.Start, outChord.End)
#wire = Part.Wire([inEdge, outEdge])
#print(" => %s" % wire)
#wire = wire.makeOffset2D(self.toolRadius)
#print(" ==> %s" % wire)
#wire = wire.makeOffset2D(-self.toolRadius)
#print(" ===> %s" % wire)
radius = self.toolRadius
while radius > 0:
lastpt = None
commands = ""
edges = DraftGeomUtils.fillet([inEdge, outEdge], radius)
if DraftGeomUtils.isSameLine(edges[0], inEdge) or DraftGeomUtils.isSameLine(edges[1], inEdge):
print("Oh, we got a problem, try smaller radius")
radius = radius - 0.1 * self.toolRadius
continue
print("we're good")
#for edge in wire.Edges[:-1]: # makeOffset2D closes the wire
for edge in edges:
if not lastpt:
lastpt = edge.Vertexes[0].Point
lastpt, cmds = PathUtils.edge_to_path(lastpt, edge, 0)
commands += cmds
path = Path.Path(commands)
return path.Commands
return [ inChord.g1Command(), outChord.g1Command() ] return [ inChord.g1Command(), outChord.g1Command() ]
def inOutBoneCommands(self, obj, inChord, outChord, angle, fixedLength, smooth): def inOutBoneCommands(self, obj, inChord, outChord, angle, fixedLength, smooth):
@ -279,9 +325,12 @@ class ObjectDressup:
boneInChord = inChord.moveBy(x, y, 0) boneInChord = inChord.moveBy(x, y, 0)
boneOutChord = boneInChord.moveTo(outChord.Start) boneOutChord = boneInChord.moveTo(outChord.Start)
debugCircle(boneInChord.Start, self.toolRadius, 'boneStart')
debugCircle(boneInChord.End, self.toolRadius, 'boneEnd')
bones = [] bones = []
bones.extend(self.smoothChordCommands(inChord, boneInChord, smooth & Smooth.In)) bones.extend(self.smoothChordCommands(inChord, boneInChord, obj.Side, smooth & Smooth.In))
bones.extend(self.smoothChordCommands(boneOutChord, outChord, smooth & Smooth.Out)) bones.extend(self.smoothChordCommands(boneOutChord, outChord, obj.Side, smooth & Smooth.Out))
return bones return bones
def dogboneAngle(self, obj, inChord, outChord): def dogboneAngle(self, obj, inChord, outChord):
@ -349,12 +398,7 @@ class ObjectDressup:
return (blacklisted, parentConsumed) return (blacklisted, parentConsumed)
# Generate commands necessary to execute the dogbone # Generate commands necessary to execute the dogbone
def boneCommands(self, obj, boneId, inChord, outChord, smooth): def boneCommands(self, obj, enabled, inChord, outChord, smooth):
loc = (inChord.End.x, inChord.End.y)
blacklisted, inaccessible = self.boneIsBlacklisted(obj, boneId, loc)
enabled = not blacklisted
self.bones.append((boneId, loc, enabled, inaccessible))
if enabled: if enabled:
if obj.Style == Style.Dogbone: if obj.Style == Style.Dogbone:
return self.dogbone(obj, inChord, outChord, smooth) return self.dogbone(obj, inChord, outChord, smooth)
@ -366,13 +410,22 @@ class ObjectDressup:
return self.tboneLongEdge(obj, inChord, outChord, smooth) return self.tboneLongEdge(obj, inChord, outChord, smooth)
if obj.Style == Style.Tbone_S: if obj.Style == Style.Tbone_S:
return self.tboneShortEdge(obj, inChord, outChord, smooth) return self.tboneShortEdge(obj, inChord, outChord, smooth)
return self.debugCircleBone(obj, inChord, outChord)
else: else:
return [] return [ inChord.g1Command(), outChord.g1Command() ]
def insertBone(self, boneId, obj, inChord, outChord, commands, smooth): def insertBone(self, boneId, obj, inChord, outChord, commands, smooth):
bones = self.boneCommands(obj, boneId, inChord, outChord, smooth) print(">----------------------------------- %d --------------------------------------" % boneId)
loc = (inChord.End.x, inChord.End.y)
blacklisted, inaccessible = self.boneIsBlacklisted(obj, boneId, loc)
enabled = not blacklisted
self.bones.append((boneId, loc, enabled, inaccessible))
if boneId < 9:
bones = self.boneCommands(obj, enabled, inChord, outChord, smooth)
else:
bones = self.boneCommands(obj, False, inChord, outChord, smooth)
commands.extend(bones[:-1]) commands.extend(bones[:-1])
print("<----------------------------------- %d --------------------------------------" % boneId)
return boneId + 1, bones[-1] return boneId + 1, bones[-1]
def execute(self, obj): def execute(self, obj):
@ -424,6 +477,8 @@ class ObjectDressup:
commands.append(lastCommand) commands.append(lastCommand)
lastCommand = None lastCommand = None
commands.append(thisCmd) commands.append(thisCmd)
#for cmd in commands:
# print("cmd = '%s'" % cmd)
path = Path.Path(commands) path = Path.Path(commands)
obj.Path = path obj.Path = path

View File

@ -233,6 +233,51 @@ def reverseEdge(e):
return newedge return newedge
def edge_to_path(lastpt, edge, Z, hf=2.0):
if isinstance(edge.Curve, Part.Circle):
# FreeCAD.Console.PrintMessage("arc\n")
arcstartpt = edge.valueAt(edge.FirstParameter)
midpt = edge.valueAt(
(edge.FirstParameter + edge.LastParameter) * 0.5)
arcendpt = edge.valueAt(edge.LastParameter)
# arcchkpt = edge.valueAt(edge.LastParameter * .99)
if DraftVecUtils.equals(lastpt, arcstartpt):
startpt = arcstartpt
endpt = arcendpt
else:
startpt = arcendpt
endpt = arcstartpt
center = edge.Curve.Center
relcenter = center.sub(lastpt)
# FreeCAD.Console.PrintMessage("arc startpt= " + str(startpt)+ "\n")
# FreeCAD.Console.PrintMessage("arc midpt= " + str(midpt)+ "\n")
# FreeCAD.Console.PrintMessage("arc endpt= " + str(endpt)+ "\n")
arc_cw = check_clockwise(
[(startpt.x, startpt.y), (midpt.x, midpt.y), (endpt.x, endpt.y)])
# FreeCAD.Console.PrintMessage("arc_cw="+ str(arc_cw)+"\n")
if arc_cw:
output = "G2"
else:
output = "G3"
output += " X" + str(fmt(endpt.x)) + " Y" + \
str(fmt(endpt.y)) + " Z" + str(fmt(Z)) + " F" + str(hf)
output += " I" + str(fmt(relcenter.x)) + " J" + \
str(fmt(relcenter.y)) + " K" + str(fmt(relcenter.z))
output += "\n"
lastpt = endpt
# FreeCAD.Console.PrintMessage("last pt arc= " + str(lastpt)+ "\n")
else:
point = edge.Vertexes[-1].Point
if DraftVecUtils.equals(point, lastpt): # edges can come flipped
point = edge.Vertexes[0].Point
output = "G1 X" + str(fmt(point.x)) + " Y" + str(fmt(point.y)) + \
" Z" + str(fmt(Z)) + " F" + str(hf) + "\n"
lastpt = point
# FreeCAD.Console.PrintMessage("line\n")
# FreeCAD.Console.PrintMessage("last pt line= " + str(lastpt)+ "\n")
return lastpt, output
def convert(toolpath, Z=0.0, PlungeAngle=90.0, Zprevious=None, StopLength=None, vf=1.0, hf=2.0) : def convert(toolpath, Z=0.0, PlungeAngle=90.0, Zprevious=None, StopLength=None, vf=1.0, hf=2.0) :
'''convert(toolpath,Z=0.0,vf=1.0,hf=2.0,PlungeAngle=90.0,Zprevious=None,StopLength=None) Converts lines and arcs to G1,G2,G3 moves. Returns a string.''' '''convert(toolpath,Z=0.0,vf=1.0,hf=2.0,PlungeAngle=90.0,Zprevious=None,StopLength=None) Converts lines and arcs to G1,G2,G3 moves. Returns a string.'''
@ -248,51 +293,6 @@ def convert(toolpath, Z=0.0, PlungeAngle=90.0, Zprevious=None, StopLength=None,
else: else:
Zprevious = Z Zprevious = Z
def edge_to_path(lastpt, edge, Z):
if isinstance(edge.Curve, Part.Circle):
# FreeCAD.Console.PrintMessage("arc\n")
arcstartpt = edge.valueAt(edge.FirstParameter)
midpt = edge.valueAt(
(edge.FirstParameter + edge.LastParameter) * 0.5)
arcendpt = edge.valueAt(edge.LastParameter)
# arcchkpt = edge.valueAt(edge.LastParameter * .99)
if DraftVecUtils.equals(lastpt, arcstartpt):
startpt = arcstartpt
endpt = arcendpt
else:
startpt = arcendpt
endpt = arcstartpt
center = edge.Curve.Center
relcenter = center.sub(lastpt)
# FreeCAD.Console.PrintMessage("arc startpt= " + str(startpt)+ "\n")
# FreeCAD.Console.PrintMessage("arc midpt= " + str(midpt)+ "\n")
# FreeCAD.Console.PrintMessage("arc endpt= " + str(endpt)+ "\n")
arc_cw = check_clockwise(
[(startpt.x, startpt.y), (midpt.x, midpt.y), (endpt.x, endpt.y)])
# FreeCAD.Console.PrintMessage("arc_cw="+ str(arc_cw)+"\n")
if arc_cw:
output = "G2"
else:
output = "G3"
output += " X" + str(fmt(endpt.x)) + " Y" + \
str(fmt(endpt.y)) + " Z" + str(fmt(Z)) + " F" + str(hf)
output += " I" + str(fmt(relcenter.x)) + " J" + \
str(fmt(relcenter.y)) + " K" + str(fmt(relcenter.z))
output += "\n"
lastpt = endpt
# FreeCAD.Console.PrintMessage("last pt arc= " + str(lastpt)+ "\n")
else:
point = edge.Vertexes[-1].Point
if DraftVecUtils.equals(point, lastpt): # edges can come flipped
point = edge.Vertexes[0].Point
output = "G1 X" + str(fmt(point.x)) + " Y" + str(fmt(point.y)) + \
" Z" + str(fmt(Z)) + " F" + str(hf) + "\n"
lastpt = point
# FreeCAD.Console.PrintMessage("line\n")
# FreeCAD.Console.PrintMessage("last pt line= " + str(lastpt)+ "\n")
return lastpt, output
lastpt = None lastpt = None
output = "" output = ""
path_length = 0.0 path_length = 0.0
@ -334,13 +334,13 @@ def convert(toolpath, Z=0.0, PlungeAngle=90.0, Zprevious=None, StopLength=None,
subwire = edge.split(t) subwire = edge.split(t)
assert(len(subwire.Edges) == 2) assert(len(subwire.Edges) == 2)
Z_cur = Z Z_cur = Z
lastpt, codes = edge_to_path(lastpt, subwire.Edges[0], Z_cur) lastpt, codes = edge_to_path(lastpt, subwire.Edges[0], Z_cur, hf)
output += codes output += codes
edge = subwire.Edges[1] edge = subwire.Edges[1]
else: else:
Z_cur = Z_next Z_cur = Z_next
lastpt, codes = edge_to_path(lastpt, edge, Z_cur) lastpt, codes = edge_to_path(lastpt, edge, Z_cur, hf)
output += codes output += codes
if StopLength: if StopLength: