diff --git a/src/Mod/PartDesign/Gui/Resources/icons/PartDesign_InternalExternalGear.svg b/src/Mod/PartDesign/Gui/Resources/icons/PartDesign_InternalExternalGear.svg
new file mode 100644
index 000000000..9cc7c91eb
--- /dev/null
+++ b/src/Mod/PartDesign/Gui/Resources/icons/PartDesign_InternalExternalGear.svg
@@ -0,0 +1,71 @@
+
+
+
+
diff --git a/src/Mod/PartDesign/InvoluteGearFeature.py b/src/Mod/PartDesign/InvoluteGearFeature.py
index 3572d38e8..b79390aed 100644
--- a/src/Mod/PartDesign/InvoluteGearFeature.py
+++ b/src/Mod/PartDesign/InvoluteGearFeature.py
@@ -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)
diff --git a/src/Mod/PartDesign/InvoluteGearFeature.ui b/src/Mod/PartDesign/InvoluteGearFeature.ui
index 19b9c4a2c..ee97e90c1 100644
--- a/src/Mod/PartDesign/InvoluteGearFeature.ui
+++ b/src/Mod/PartDesign/InvoluteGearFeature.ui
@@ -7,7 +7,7 @@
0
0
195
- 116
+ 136
@@ -122,26 +122,53 @@
-
- Number of Curves:
+ High Precision:
-
-
+
- false
+ true
false
-
- 2x3
+ True
-
- 1x4
+ False
+
+
+
+
+ -
+
+
+ external Gear:
+
+
+
+ -
+
+
+ true
+
+
+ false
+
+
-
+
+ True
+
+
+ -
+
+ False
diff --git a/src/Mod/PartDesign/fcgear/involute.py b/src/Mod/PartDesign/fcgear/involute.py
index 27f070bd0..620afb052 100644
--- a/src/Mod/PartDesign/fcgear/involute.py
+++ b/src/Mod/PartDesign/fcgear/involute.py
@@ -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):