Involute gear script extended for internal gears

This commit is contained in:
Aglef Kaiser 2014-08-08 12:10:38 +02:00 committed by wmayer
parent 6b5b1a7443
commit 420e71afbb
4 changed files with 268 additions and 13 deletions

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 23 KiB

View File

@ -46,7 +46,7 @@ def makeInvoluteGear(name):
class _CommandInvoluteGear:
"the Fem InvoluteGear command definition"
def GetResources(self):
return {'Pixmap' : 'PartDesign_InvoluteGear',
return {'Pixmap' : 'PartDesign_InternalExternalGear',
'MenuText': QtCore.QT_TRANSLATE_NOOP("PartDesign_InvoluteGear","Involute gear..."),
'Accel': "",
'ToolTip': QtCore.QT_TRANSLATE_NOOP("PartDesign_InvoluteGear","Creates or edit the involute gear definition.")}
@ -72,12 +72,14 @@ class _InvoluteGear:
obj.addProperty("App::PropertyInteger","NumberOfTeeth","Gear","Number of gear teeth")
obj.addProperty("App::PropertyLength","Modules","Gear","Modules of the gear")
obj.addProperty("App::PropertyAngle","PressureAngle","Gear","Pressure angle of gear teeth")
obj.addProperty("App::PropertyInteger","NumberOfCurves","Gear","0=2x3 1=1x4 ")
obj.addProperty("App::PropertyBool","HighPrecision","Gear","True=2 curves with each 3 control points False=1 curve with 4 control points ")
obj.addProperty("App::PropertyBool","ExternalGear","Gear","True=external Gear False=internal Gear ")
obj.NumberOfTeeth = 26
obj.Modules = "2.5 mm"
obj.PressureAngle = "20 deg"
obj.NumberOfCurves = 0
obj.HighPrecision = True
obj.ExternalGear = True
obj.Proxy = self
@ -85,7 +87,10 @@ class _InvoluteGear:
def execute(self,obj):
#print "_InvoluteGear.execute()"
w = fcgear.FCWireBuilder()
involute.CreateExternalGear(w, obj.Modules.Value,obj.NumberOfTeeth, obj.PressureAngle.Value, obj.NumberOfCurves == 0)
if obj.ExternalGear:
involute.CreateExternalGear(w, obj.Modules.Value,obj.NumberOfTeeth, obj.PressureAngle.Value, obj.HighPrecision)
else:
involute.CreateInternalGear(w, obj.Modules.Value,obj.NumberOfTeeth, obj.PressureAngle.Value, obj.HighPrecision)
gearw = Part.Wire([o.toShape() for o in w.wire])
obj.Shape = gearw
return
@ -129,22 +134,36 @@ class _InvoluteGearTaskPanel:
self.obj = obj
self.form=FreeCADGui.PySideUic.loadUi(FreeCAD.getHomePath() + "Mod/PartDesign/InvoluteGearFeature.ui")
QtCore.QObject.connect(self.form.Quantity_Modules, QtCore.SIGNAL("valueChanged(double)"), self.modulesChanged)
QtCore.QObject.connect(self.form.Quantity_PressureAngle, QtCore.SIGNAL("valueChanged(double)"), self.angleChanged)
QtCore.QObject.connect(self.form.spinBox_NumberOfTeeth, QtCore.SIGNAL("valueChanged(int)"), self.numTeethChanged)
QtCore.QObject.connect(self.form.comboBox_HighPrecision, QtCore.SIGNAL("currentIndexChanged(int)"), self.numCurvesChanged)
#QtCore.QObject.connect(self.form.comboBox_ExternalGear, QtCore.SIGNAL("activated(QString)"), self.externalGearChanged)
#QtCore.QObject.connect(self.form.comboBox_ExternalGear, QtCore.SIGNAL("currentIndexChanged(int)"), self.externalGearChanged)
QtCore.QObject.connect(self.form.comboBox_ExternalGear, QtCore.SIGNAL("currentIndexChanged(int)"), self.externalGearChanged)
self.update()
if mode == 0: # fresh created
self.obj.Proxy.execute(self.obj) # calculate once
FreeCAD.Gui.SendMsgToActiveView("ViewFit")
def transferTo(self):
"Transfer from the dialog to the object"
self.obj.NumberOfTeeth = self.form.spinBox_NumberOfTeeth.value()
self.obj.Modules = self.form.Quantity_Modules.text()
self.obj.PressureAngle = self.form.Quantity_PressureAngle.text()
self.obj.NumberOfCurves = self.form.comboBox_NumberOfCurves.currentIndex()
if self.form.comboBox_HighPrecision.currentIndex() == 0:
self.obj.HighPrecision = True
else:
self.obj.HighPrecision = False
#self.obj.HighPrecision = self.form.comboBox_HighPrecision.currentIndex()
if self.form.comboBox_ExternalGear.currentIndex() == 0:
self.obj.ExternalGear = True
else:
self.obj.ExternalGear = False
#self.obj.ExternalGear = self.form.comboBox_ExternalGear.currentIndex()
def transferFrom(self):
@ -152,12 +171,22 @@ class _InvoluteGearTaskPanel:
self.form.spinBox_NumberOfTeeth.setValue(self.obj.NumberOfTeeth)
self.form.Quantity_Modules.setText(self.obj.Modules.UserString)
self.form.Quantity_PressureAngle.setText(self.obj.PressureAngle.UserString)
self.form.comboBox_NumberOfCurves.setCurrentIndex(self.obj.NumberOfCurves)
if self.obj.HighPrecision:
self.form.comboBox_HighPrecision.setCurrentIndex(0)
else:
self.form.comboBox_HighPrecision.setCurrentIndex(1)
#self.form.comboBox_HighPrecision.setCurrentIndex(self.obj.HighPrecision)
if self.obj.ExternalGear:
self.form.comboBox_ExternalGear.setCurrentIndex(0)
else:
self.form.comboBox_ExternalGear.setCurrentIndex(1)
#self.form.comboBox_ExternalGear.setCurrentIndex(self.obj.ExternalGear)
def modulesChanged(self, value):
#print value
self.obj.Modules = value
self.obj.Proxy.execute(self.obj)
FreeCAD.Gui.SendMsgToActiveView("ViewFit")
def angleChanged(self, value):
#print value
@ -168,6 +197,25 @@ class _InvoluteGearTaskPanel:
#print value
self.obj.NumberOfTeeth = value
self.obj.Proxy.execute(self.obj)
FreeCAD.Gui.SendMsgToActiveView("ViewFit")
def numCurvesChanged(self, value):
#print value
if value == 0:
v=True
else:
v=False
self.obj.HighPrecision = v
self.obj.Proxy.execute(self.obj)
def externalGearChanged(self, value):
#print value
if value == 0:
v=True
else:
v=False
self.obj.ExternalGear = v
self.obj.Proxy.execute(self.obj)
def getStandardButtons(self):
return int(QtGui.QDialogButtonBox.Ok) | int(QtGui.QDialogButtonBox.Cancel)| int(QtGui.QDialogButtonBox.Apply)

View File

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>195</width>
<height>116</height>
<height>136</height>
</rect>
</property>
<property name="windowTitle">
@ -122,26 +122,53 @@
<item row="3" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Number of Curves:</string>
<string>High Precision:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QComboBox" name="comboBox_NumberOfCurves">
<widget class="QComboBox" name="comboBox_HighPrecision">
<property name="enabled">
<bool>false</bool>
<bool>true</bool>
</property>
<property name="editable">
<bool>false</bool>
</property>
<item>
<property name="text">
<string>2x3</string>
<string>True</string>
</property>
</item>
<item>
<property name="text">
<string>1x4</string>
<string>False</string>
</property>
</item>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_10">
<property name="text">
<string>external Gear:</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QComboBox" name="comboBox_ExternalGear">
<property name="enabled">
<bool>true</bool>
</property>
<property name="editable">
<bool>false</bool>
</property>
<item>
<property name="text">
<string>True</string>
</property>
</item>
<item>
<property name="text">
<string>False</string>
</property>
</item>
</widget>

View File

@ -124,6 +124,115 @@ def CreateExternalGear(w, m, Z, phi, split=True):
w.close()
return w
def CreateInternalGear(w, m, Z, phi, split=True):
"""
Create an internal gear
w is wirebuilder object (in which the gear will be constructed)
if split is True, each profile of a teeth will consist in 2 Bezier
curves of degree 3, otherwise it will be made of one Bezier curve
of degree 4
"""
# ****** external gear specifications
addendum = 0.6 * m # distance from pitch circle to tip circle (ref G.M.Maitra)
dedendum = 1.25 * m # pitch circle to root, sets clearance
clearance = 0.25 * m
# Calculate radii
Rpitch = Z * m / 2 # pitch circle radius
Rb = Rpitch*cos(phi * pi / 180) # base circle radius
Ra = Rpitch - addendum # tip (addendum) circle radius
Rroot = Rpitch + dedendum # root circle radius
fRad = 1.5 * clearance # fillet radius, max 1.5*clearance
Rf = Rroot - clearance # radius at top of fillet (end of profile)
# ****** calculate angles (all in radians)
pitchAngle = 2 * pi / Z # angle subtended by whole tooth (rads)
baseToPitchAngle = genInvolutePolar(Rb, Rpitch)
tipToPitchAngle = baseToPitchAngle
if (Ra > Rb): # start profile at top of fillet (if its greater)
tipToPitchAngle -= genInvolutePolar(Rb, Ra)
pitchToFilletAngle = genInvolutePolar(Rb, Rf) - baseToPitchAngle;
filletAngle = 1.414*clearance/Rf # // to make fillet tangential to root
# ****** generate Higuchi involute approximation
fe = 1 # fraction of profile length at end of approx
fs = 0.01 # fraction of length offset from base to avoid singularity
if (Ra > Rb):
fs = (Ra**2 - Rb**2) / (Rf**2 - Rb**2) # offset start to top of fillet
if split:
# approximate in 2 sections, split 25% along the involute
fm = fs + (fe - fs) / 4 # fraction of length at junction (25% along profile)
addInv = BezCoeffs(m, Z, phi, 3, fs, fm)
dedInv = BezCoeffs(m, Z, phi, 3, fm, fe)
# join the 2 sets of coeffs (skip duplicate mid point)
invR = addInv + dedInv[1:]
else:
invR = BezCoeffs(m, Z, phi, 4, fs, fe)
# create the back profile of tooth (mirror image)
inv = []
for i, pt in enumerate(invR):
# rotate involute to put center of tooth at y = 0
ptx, pty = invR[i] = rotate(pt, pitchAngle / 4 - baseToPitchAngle)
# generate the back of tooth profile nodes, flip Y coords
inv.append((ptx, -pty))
# ****** calculate section junction points R=back of tooth, Next=front of next tooth)
#fillet = inv[6] # top of fillet, front of tooth #toCartesian(Rf, -pitchAngle / 4 - pitchToFilletAngle) # top of fillet
fillet = [ptx,-pty]
tip = toCartesian(Ra, -pitchAngle/4+tipToPitchAngle) # tip, front of tooth
tipR = [ tip[0], -tip[1] ]
#filletR = [fillet[0], -fillet[1]] # flip to make same point on back of tooth
rootR = toCartesian(Rroot, pitchAngle / 4 + pitchToFilletAngle + filletAngle)
rootNext = toCartesian(Rroot, 3 * pitchAngle / 4 - pitchToFilletAngle - filletAngle)
filletNext = rotate(fillet, pitchAngle) # top of fillet, front of next tooth
# Build the shapes using FreeCAD.Part
t_inc = 2.0 * pi / float(Z)
thetas = [(x * t_inc) for x in range(Z)]
w.move(fillet) # start at top of front profile
for theta in thetas:
w.theta = theta
if split:
w.curve(inv[5], inv[4], inv[3])
w.curve(inv[2], inv[1], inv[0])
else:
w.curve(*inv[-2::-1])
if (Ra < Rb):
w.line(tip) # line from fillet up to base circle
if split:
w.arc(tipR, Ra, 0) # arc across addendum circle
else:
#w.arc(tipR[-1], Ra, 0) # arc across addendum circle
w.arc(tipR, Ra, 0)
if (Ra < Rb):
w.line(invR[0]) # line down to topof fillet
if split:
w.curve(invR[1], invR[2], invR[3])
w.curve(invR[4], invR[5], invR[6])
else:
w.curve(*invR[1:])
if (rootNext[1] > rootR[1]): # is there a section of root circle between fillets?
w.arc(rootR, fRad, 1) # back fillet
w.arc(rootNext, Rroot, 0) # root circle arc
w.arc(filletNext, fRad, 1)
w.close()
return w
def genInvolutePolar(Rb, R):