I hv modified origin gear.py in FreeCAD 0.14 to fit my work.
It can handle bevel and helical gears, also combined ones.
Comments are welcomed.
Hope it would be helpful.
Code: Select all
#Involute Gears Generation Script
#by Marcin Wanczyk (dj_who)
#(c) 2011 LGPL
# modified by Kevin Chen ( kevinttl@gmail.com ) Sep 1, 2013
# delevoped on Ubuntu
from __future__ import division # allows floating point division from integers
import FreeCAD, FreeCADGui, Part, Draft, math # , MeshPart, Mesh
from FreeCAD import Base
from PyQt4 import QtGui,QtCore
App = FreeCAD
Gui = FreeCADGui
class InvoluteGear:
def __init__(self, obj,prop_n,prop_m,prop_alfa,prop_a,prop_b,prop_width,prop_c,prop_j,prop_bevel,prop_helical):
''' Add the properties: Number of Gears, Pressure Angle, Tooth Height Factor, Module, Tooth Clearance, Tooth Lateral Clearance, Width, Bevel Angle'''
obj.addProperty("App::PropertyInteger","N","InvoluteGear","Number of Gears").N=prop_n
obj.addProperty("App::PropertyFloat","m","InvoluteGear","Module").m=prop_m
obj.addProperty("App::PropertyAngle","alfa","InvoluteGear","Pressure Angle").alfa=prop_alfa
obj.addProperty("App::PropertyFloat","a","InvoluteGear","Addendum Factor (* Module)").a=prop_a
obj.addProperty("App::PropertyFloat","b","InvoluteGear","Addendum Factor (* Module)").b=prop_b
obj.addProperty("App::PropertyLength","width","InvoluteGear","Gear Width").width=prop_width
obj.addProperty("App::PropertyLength","c","InvoluteGear","Tooth Clearance").c=prop_c
obj.addProperty("App::PropertyLength","j","InvoluteGear","Tooth Lateral Clearance").j=prop_j
obj.addProperty("App::PropertyAngle","bevel","InvoluteGear","Bevel Angle").bevel=prop_bevel
obj.addProperty("App::PropertyAngle","helical","InvoluteGear","Helical Angle").helical=prop_helical
obj.Proxy = self
def onChanged(self, fp, prop):
if prop == "N" or prop == "m" or prop == "alfa" or prop == "a" or prop == "b" or prop == "width" or prop == "c" or prop == "j" or prop == "bevel" or prop == "helical" : #if one of these is changed
self.execute(fp)
def execute(self, fp): #main part of script
N = fp.N
alfa = fp.alfa
m=fp.m # module -> pitch diameter
a = fp.a * m
b = fp.b * m
p = m * math.pi # circle pitch
c = fp.c* m #standard value 0,1*m - 0,3*m
j = fp.j * m #standard value 0,015 - 0,04*m
width = fp.width #gear width
bevel = fp.bevel
helical = fp.helical
if bevel > 70 or bevel < - 70 :
FreeCAD.Console.PrintError("Bevel Angle should be in -70 ~ 70 degrees\n")
return
# some consts
dv_angle = math.radians(360 / N)
#pitch diameter
d=N*m
#root diameter
df = d - 2 * b * m - 2 * c #df=d-2hf where and hf=y*m+c
#addendum diameter
da = d + 2 * a * m #da=d+2ha where ha=y*m
#base diameter for involute
db=d * math.cos(math.radians(alfa))
if df > db :
# root is larger than base... not allowed!
FreeCAD.Console.PrintError("Root diameter:" + str(df) + " is larger than Base diameter:" + str(db) + "\n")
return
# FreeCAD.Console.PrintError("pitch diameter=" + str(d) + ",base=" + str(db) + ",root=" + str(df) + "\n")
rb = db / 2 # base radius
#Root circle
rootCircleRadius = df / 2
# solve addendum angle dirctly
# r = rb * sqrt(1 + t * t)
# so at addendum situation, r = da / 2
# (d + 2 * y * m) / 2 = (d * cos(alfa) / 2) * sqrt(1 + t * t)
# sqrt(1 + t * t) = (1 + 2 * y * m / d) / cos(alfa)
# t * t = ((1 + 2 * y * m / d) / cos(alfa)) ^ 2 - 1
# t = sqrt(((1 + 2 * y * m / d) / cos(alfa)) ^ 2 - 1)
# pitch is a very important point! so....
pt_angle_invaa = math.cos(math.radians(alfa))
pt_angle = math.sqrt(1 / (pt_angle_invaa * pt_angle_invaa) - 1)
# stop angle
st_angle_aa = (1 + 2 * a * m / d) / pt_angle_invaa
st_angle = math.sqrt(st_angle_aa * st_angle_aa - 1)
# check angle (make sure addendum is reached!
ck_angle = (st_angle - math.atan(st_angle)) * 2
# angle steps -> around 5 pts / mm
# divide into above pitch and below evenly as possible...
n_ang_ap = int((da - d) * 2.5)
if n_ang_ap < 2 :
n_ang_ap = 2
n_ang_bp = int((d - db) * 2.5)
if n_ang_bp < 2 :
n_ang_bp = 2
n_angle = n_ang_ap + n_ang_bp - 1 # pitch point is dup
#************ Calculating right sides of teeth
#Involute of base circle
involute=[]
involutee=[]
involutesav=[]
# FreeCAD.Console.PrintError("n_angle=" + str(n_angle) + ",st_angle=" + str(st_angle) + ",ck_angle=" + str(ck_angle) + "\n")
for t in range(0,n_angle,1):
if t < n_ang_bp :
rang = pt_angle * t / (n_ang_bp - 1)
else :
rang = pt_angle + (st_angle - pt_angle) * (t - n_ang_bp + 1) / (n_ang_ap - 1)
x = rb * (math.cos(rang) + rang * math.sin(rang))
y = rb * (math.sin(rang) - rang * math.cos(rang))
if t == 0:
rAngle = math.atan2(y,x)
involute.append(Part.Vertex(x,y,0).Point)
# FreeCAD.Console.PrintError("cal finish\n")
#************ Drawing rigth sides of teeth
involutesav.extend(involute)
involutee.extend(involute)
startAngle = []
stopAngle = []
involuteL = [ 0 ] # index 0 is useless
# FreeCAD.Console.PrintError("#0.1\n")
sAngle = dv_angle
for angle in range(1,N+1,1):
involutee.insert(0,(rootCircleRadius * math.cos(rAngle),rootCircleRadius * math.sin(rAngle),0))
involuteshape = Part.makePolygon(involutee)
involuteL.insert(angle,involuteshape)
involutee=[]
stopAngle.append(sAngle)
for num in range(0,n_angle,1):
point=involute.pop()
x = point.x*math.cos(sAngle) - point.y*math.sin(sAngle)
y = point.x*math.sin(sAngle) + point.y*math.cos(sAngle)
pointt=Part.Vertex(x,y,0).Point
involutee.insert(0,pointt)
rAngle = math.atan2(y,x)
involute.extend(involutesav)
sAngle += dv_angle
involutee=[]
#************ Calculating difference between tooth spacing on BaseCircle and PitchCircle
# cut = involuteL[1].cut(pitchCircle)
# FreeCAD.ActiveDocument.addObject("Part::Feature","CutInv").Shape=cut
# invPoint=cut.Vertexes[0].Point
# diff=invPoint.y*2 # instead of making axial symmetry and calculating point distance.
diff = involuteL[1].Vertexes[n_ang_bp].Point.y * 2
anglediff = math.radians(180 / N) - dv_angle * (j / p)
if anglediff < ck_angle :
# addendum is not reachable
FreeCAD.Console.PrintError("Tooth angle:" + str(anglediff) + " is less than Check angle:" + str(ck_angle) + "\nSo addendum (a) is not reachable\n")
return
anglediff += 2 * math.asin(diff / d)
#************ Calculating left sides of teeth
# FreeCAD.Console.PrintError("#0.2\n")
#************ Inversing Involute
for num in range(0,n_angle,1):
point=involute.pop()
pointt=Part.Vertex(point.x,- point.y,0).Point
involutee.insert(0,pointt)
involute.extend(involutee)
involutee=[]
#Normal tooth size calculated as: 0,5* p - j j=m * 0,1 below are calculations
# 0,5* p - m * 0,1
# 0,5* p - p /pi * 0,1
# 0,5*360/N - ((360/N)/pi)* 0,1
# 0,5*360/N - (360/N)*((1/pi)*0,1) j=(p/pi)*0,1
# 0,5*360/N - (360/N)*((p/pi)*0,1)/p
# 0,5*360/N - (360/N)*( j )/p
# FreeCAD.Console.PrintError("#0.3\n")
for num in range(0,n_angle,1):
point=involute.pop()
x = point.x*math.cos(anglediff) - point.y*math.sin(anglediff)
y = point.x*math.sin(anglediff) + point.y*math.cos(anglediff)
pointt=Part.Vertex(x,y,0).Point
involutee.insert(0,pointt)
rAngle = math.atan2(y,x)
involute.extend(involutee)
involutesav=[]
involutesav.extend(involute)
#************ Drawing left sides of teeth
involuteR = [0] # index 0 is useless
# FreeCAD.Console.PrintError("#0.4\n")
sAngle = dv_angle
for angle in range(1,N+1,1):
involutee.insert(0,(rootCircleRadius * math.cos(rAngle),rootCircleRadius * math.sin(rAngle),0))
involuteshape = Part.makePolygon(involutee)
involuteR.insert(angle,involuteshape)
involutee=[]
startAngle.append(rAngle)
for num in range(0,n_angle,1):
point=involute.pop()
x = point.x*math.cos(sAngle) - point.y*math.sin(sAngle)
y = point.x*math.sin(sAngle) + point.y*math.cos(sAngle)
pointt=Part.Vertex(x,y,0).Point
involutee.insert(0,pointt)
rAngle = math.atan2(y,x)
involute.extend(involutesav)
sAngle += dv_angle
#************ Forming teeth
# cutCircleE = Part.makeCircle(da) # da because must be bigger than addendumCircle and bigger than whole construction da is right for this but it not has to be.
# cutCircleW = Part.Wire(cutCircleE)
# cutCircle = Part.Face(cutCircleW)
# cutTool=cutCircle.cut(addendumCircle)
# cutshape=Part.show(cutTool)
# FreeCAD.Console.PrintError("#1\n")
for invNum in range(1,N+1,1):
# FreeCAD.Console.PrintError("invNum=" + str(invNum) + "\n")
invL = involuteL[invNum]
invR = involuteR[invNum]
pointL = invL.Vertexes.pop().Point
pointR = invR.Vertexes.pop().Point
faceEdge=Part.makeLine(pointL,pointR)
toothWhole = invL.fuse(invR)
toothWhole=toothWhole.fuse(faceEdge)
toothWire=Part.Wire(toothWhole.Edges)
# toothWire = Part.Wire([invL,invR,faceEdge])
if invNum == 1:
gearedge = toothWire
else:
gearedge = Part.Wire([gearedge,toothWire])
toothArc = Part.makeCircle(rootCircleRadius,FreeCAD.Vector(0,0,0),FreeCAD.Vector(0,0,1),math.degrees(startAngle[invNum - 1]),math.degrees(stopAngle[invNum - 1]))
# FreeCAD.Console.PrintError("#1.3 " + str(rootCircleRadius) + "," + str(math.degrees(startAngle[invNum - 1])) + "," + str(math.degrees(stopAngle[invNum - 1])) + "\n")
gearedge = Part.Wire([gearedge,toothArc])
# FreeCAD.Console.PrintError("#1.4\n")
# FreeCAD.Console.PrintError("#2\n")
gearShape=Part.Face(gearedge)
if bevel == 0 and helical == 0:
ext = gearShape.extrude(Base.Vector(0,0,width))
else :
gearTop = gearShape
tr = Base.Matrix()
tr.move(Base.Vector(0,0,width))
if helical != 0 :
tr.rotateZ(math.radians(helical))
if bevel != 0 :
ss = 1 + width * math.tan(bevel) / d
tr.scale(ss,ss,1)
gearTop = gearTop.transformGeometry(tr)
extloft = Part.makeLoft([gearedge,Part.Wire(gearTop.Edges)])
# extsh1 = Part.Shell([gearShape,gearTop])
# extsh2 = Part.Shell([extsh1,extloft])
extsh1 = extloft.fuse(Part.Face(gearedge))
extsh2 = extsh1.fuse(gearTop)
ext = Part.Solid(extsh2)
fp.Shape = ext # result
default_n = 16
default_m = 1.0
default_alfa = 20
default_a = 1.1
default_b = 1.4
default_c = 0.1
default_j = 0.04
default_width = 6.0
default_bevel = 0.0
default_helical = 0.0
def makeInvoluteGear():
doc = FreeCAD.activeDocument()
if doc == None:
doc = FreeCAD.newDocument()
igearblock=doc.addObject("Part::FeaturePython","InvoluteGear") #add object to document
igearblock.Label = "Involute Gear"
InvoluteGear(igearblock,default_n,default_m,default_alfa,default_a,default_b,default_width,default_c,default_j,default_bevel,default_helical)
igearblock.ViewObject.Proxy=0
App.ActiveDocument.recompute()
Gui.SendMsgToActiveView("ViewFit")
l1 = QtGui.QLineEdit()
l1.setText(str(default_n))
l2 = QtGui.QLineEdit()
l2.setText(str(default_m))
l3 = QtGui.QLineEdit()
l3.setText(str(default_alfa))
l4 = QtGui.QLineEdit()
l4.setText(str(default_a))
l5 = QtGui.QLineEdit()
l5.setText(str(default_c))
l6 = QtGui.QLineEdit()
l6.setText(str(default_j))
l7 = QtGui.QLineEdit()
l7.setText(str(default_width))
l8 = QtGui.QLineEdit()
l8.setText(str(default_bevel))
l9 = QtGui.QLineEdit()
l9.setText(str(default_b))
l10 = QtGui.QLineEdit()
l10.setText(str(default_helical))
def proceed():
try:
compute()
except:
hide()
QtGui.qApp.restoreOverrideCursor()
def compute():
QtGui.qApp.restoreOverrideCursor()
hide()
try:
N = int(l1.text())
m = float(l2.text())
alfa = int(l3.text())
a = float(l4.text())
b = float(l9.text())
c = float(l5.text())*m #standard value 0,1*m - 0,3*m
j = float(l6.text())*m #standard value 0,015 - 0,04*m
width = float(l7.text()) #gear width
bevel = float(l8.text())
helical = float(l10.text())
except:
FreeCAD.Console.PrintError("Wrong input! Only numbers allowed...\n")
doc = FreeCAD.activeDocument()
if doc == None:
doc = FreeCAD.newDocument("Involute Gear")
involutegear = doc.addObject("Part::FeaturePython","InvoluteGear") #add object to document
involutegear.Label = "Involute Gear"
InvoluteGear(involutegear,N,m,alfa,a,b,width,c,j,bevel,helical)
involutegear.ViewObject.Proxy=0
App.ActiveDocument.recompute()
Gui.SendMsgToActiveView("ViewFit")
QtGui.qApp.restoreOverrideCursor()
def hide():
dialog.hide()
if __name__ == "__main__": #feature will be generated after macro execution
# makeInvoluteGear()
QtGui.qApp.setOverrideCursor(QtCore.Qt.WaitCursor)
dialog = QtGui.QDialog()
dialog.resize(200,450)
dialog.setWindowTitle("Gear")
la = QtGui.QVBoxLayout(dialog)
t1 = QtGui.QLabel("Number of teeth (N)")
la.addWidget(t1)
la.addWidget(l1)
t2 = QtGui.QLabel("module (m)")
la.addWidget(t2)
la.addWidget(l2)
t3 = QtGui.QLabel("Pressure angle (alfa)")
la.addWidget(t3)
la.addWidget(l3)
t4 = QtGui.QLabel("Addendum factor (* Module) (a)")
la.addWidget(t4)
la.addWidget(l4)
t9 = QtGui.QLabel("Dedendum factor (* Module) (b)")
la.addWidget(t9)
la.addWidget(l9)
t5 = QtGui.QLabel("Tooth clearance (c)")
la.addWidget(t5)
la.addWidget(l5)
t6 = QtGui.QLabel("Tooth lateral clearance (j)")
la.addWidget(t6)
la.addWidget(l6)
t7 = QtGui.QLabel("Gear width")
la.addWidget(t7)
la.addWidget(l7)
t8 = QtGui.QLabel("Bevel Angle")
la.addWidget(t8)
la.addWidget(l8)
t10 = QtGui.QLabel("Helical Angle")
la.addWidget(t10)
la.addWidget(l10)
okbox = QtGui.QDialogButtonBox(dialog)
okbox.setOrientation(QtCore.Qt.Horizontal)
okbox.setStandardButtons(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok)
la.addWidget(okbox)
QtCore.QObject.connect(okbox, QtCore.SIGNAL("accepted()"), proceed)
QtCore.QObject.connect(okbox, QtCore.SIGNAL("rejected()"), hide)
QtCore.QMetaObject.connectSlotsByName(dialog)
dialog.show()