I created a workaround that allows you to make a helix of pretty much any dimension. I didn't do an exhaustive debug, but it worked where it didn't before, so that's something.
Included in the code below (I also couldn't get my HelicalShapes.py file to upload...) are 2 implementations of my helix fix.
The Spring() function requires 4 selected sketches (using Sketcher or Draft if you'd like) - create a circle for the spring's base, a circle for the spring's cross section, a line for its axis, and a line for its pitch.
The Screw() function creates the threads of a screw. It requires 3 sketches - create a circle for the screw's base, a profile shape for the thread's cross section (usually a triangle), and a line representing the axis. It finds a line in the thread profile to use as the pitch (the longest line that is perpendicular to the base).
Again, sorry I couldn't show the pictures... I realize that's gonna make it harder to figure out how to use this... I guess this may help anyone who just happens to be looking for this.
Code: Select all
import Part, FreeCADGui, FreeCAD, math, PartGui
from FreeCAD import Base
def HelixOK(H):
#This tells you whether the helix keeps its form well, or goes haywire at the top.
#It's part of a workaround for a bug in the Part.makeHelix function.
L=H.Edges[0].Length
R=H.Edges[0].valueAt(0).sub(H.Edges[0].centerOfCurvatureAt(0)).Length
C=[]
Csum=0
n=10
for i in range(n):
center=H.Edges[0].centerOfCurvatureAt(L/10*i)
offset=math.sqrt(center.x**2+center.y**2)
C.append(offset)
Csum+=offset
Cavg=Csum/n
Cstdev=0
for each in C:
Cstdev+=(each-Cavg)**2
Cstdev=math.sqrt(Cstdev)/n
state = (Cstdev<.01)
return state
def FixHelix(Pitch,Height,Radius):
#This function replaces Part.makeHelix(), creating a helix that won't go crazy at the top.
Helix=Part.makeHelix(Pitch,Height,Radius)
OK=False
n=0
while not OK:
for i in range(n):
H2=Helix.copy()
H2.translate(Base.Vector(0,0,Height))
HEdges=Helix.Edges
HEdges.extend(H2.Edges)
Helix=Part.Wire(HEdges)
if HelixOK(Helix):
OK=True
else:
n+=1
Height=math.floor(Height/Pitch)*Pitch/2
Helix=Part.makeHelix(Pitch,Height,Radius)
return Helix
def Spring():
#Select (0) a circle for the spring diameter, (1) a circle for the cross section,
#(2) a line whose length is the height of the spring, and (3) a line whose
#length is the pitch, in that order.
Radius = FreeCADGui.Selection.getSelection()[0].Shape.Edges[0].Curve.Radius
try:
Profile = FreeCADGui.Selection.getSelection()[1].Shape.Wires[0]
except:
Profile = FreeCADGui.Selection.getSelection()[1].Shape.Edges[0]
Height = FreeCADGui.Selection.getSelection()[2].Shape.Length
Pitch = FreeCADGui.Selection.getSelection()[3].Shape.Length
Helix=FixHelix(Pitch,Height,Radius)
Spring = Helix.makePipeShell([Profile],1,1)
Part.show(Spring)
return Spring
def Screw():
#Select (0) a circle for the screw outer diameter, (1) a triangle or polygon for the thread profile,
#and (2) a line whose length is the length of the screw.
#Note: the profile must have at least one line that is perpendicular to the circle.
#This function only creates the threads; you have to make your own cylinder for the core.
Circle = FreeCADGui.Selection.getSelection()[0].Shape.Edges[0]
Radius = Circle.Curve.Radius
Profile = FreeCADGui.Selection.getSelection()[1].Shape.Wires[0]
Height = FreeCADGui.Selection.getSelection()[2].Shape.Length
PitchFinder = FreeCADGui.Selection.getSelection()[1].Shape.Edges
C=Circle.centerOfCurvatureAt(0).sub(Circle.valueAt(0)).normalize()
T=Circle.tangentAt(0)
N=T.cross(C).normalize()
#Finds the largest vertical line in the profile sketch; uses that as the pitch.
Pitch=0.
for eachedge in PitchFinder:
if eachedge.tangentAt(0).sub(N).Length < .001 and eachedge.Length>Pitch:
Pitch = eachedge.Length
Helix=FixHelix(Pitch,Height,Radius)
Screw = Helix.makePipeShell([Profile],1,1)
Part.show(Screw)
return Screw