Add an "internal involute gear" command and feature

This reuses the same (external) tooth profile from pygear but swap some
parameters to make the resulting gear internal.
This prototype is mostly a copy of the external involute gear. Eventually
we should refactor this to share more code but this has to be coordinated
with the megagrant endevour. Otherwise merging becomes a nightmere.
Note that in contrast to the involute rack I choose to base the
"thickness" on the pitch diameter, not the root diameter. This has the
benefit of keeping the outside diameter stable when e.g. adjusting the
clearance. And setting the outside diameter directly could result in an
invalid shape when chaning the numnber of teeth.
The default head value of "-0.4" is choosen to match the invernal gear
profile from the PartDewign WB.
This commit is contained in:
Jonas Bähr
2021-07-26 21:38:23 +02:00
committed by looooo
parent 4e16fd2560
commit 6daa3114be
3 changed files with 154 additions and 3 deletions

View File

@@ -19,7 +19,7 @@
import os
import FreeCAD
import FreeCADGui as Gui
from .features import ViewProviderGear, InvoluteGear, InvoluteGearRack
from .features import ViewProviderGear, InvoluteGear, InternalInvoluteGear, InvoluteGearRack
from .features import CycloidGear, BevelGear, CrownGear, WormGear, TimingGear, LanternGear, HypoCycloidGear
@@ -79,7 +79,15 @@ class CreateInvoluteGear(BaseCommand):
GEAR_FUNCTION = InvoluteGear
Pixmap = os.path.join(BaseCommand.ICONDIR, 'involutegear.svg')
MenuText = 'Involute gear'
ToolTip = 'Create an Involute gear'
ToolTip = 'Create an external involute gear'
class CreateInternalInvoluteGear(BaseCommand):
NAME = "internalinvolutegear"
GEAR_FUNCTION = InternalInvoluteGear
Pixmap = os.path.join(BaseCommand.ICONDIR, 'involutegear.svg')
MenuText = 'Internal involute gear'
ToolTip = 'Create an internal involute gear'
class CreateInvoluteRack(BaseCommand):

View File

@@ -272,6 +272,147 @@ class InvoluteGear(BaseGear):
def __setstate__(self, state):
return None
class InternalInvoluteGear(BaseGear):
"""FreeCAD internal involute gear
Using the same tooth as the external, just turning it inside-out:
addedum becomes dedendum, clearance becomes head, negate the backslash, ...
"""
def __init__(self, obj):
super(InternalInvoluteGear, self).__init__(obj)
self.involute_tooth = InvoluteTooth()
obj.addProperty(
"App::PropertyBool", "simple", "precision", "simple")
obj.addProperty("App::PropertyInteger",
"teeth", "gear_parameter", "number of teeth")
obj.addProperty(
"App::PropertyLength", "module", "gear_parameter", "normal module if properties_from_tool=True, \
else it's the transverse module.")
obj.addProperty(
"App::PropertyFloat", "shift", "gear_parameter", "shift")
obj.addProperty(
"App::PropertyLength", "height", "gear_parameter", "height")
obj.addProperty(
"App::PropertyLength", "thickness", "gear_parameter", "thickness")
obj.addProperty(
"App::PropertyAngle", "pressure_angle", "involute_parameter", "pressure angle")
obj.addProperty(
"App::PropertyFloat", "clearance", "gear_parameter", "clearance")
obj.addProperty("App::PropertyInteger", "numpoints",
"precision", "number of points for spline")
obj.addProperty(
"App::PropertyAngle", "beta", "gear_parameter", "beta ")
obj.addProperty(
"App::PropertyBool", "double_helix", "gear_parameter", "double helix")
obj.addProperty(
"App::PropertyLength", "backlash", "tolerance", "backlash")
obj.addProperty(
"App::PropertyBool", "reversed_backlash", "tolerance", "backlash direction")
obj.addProperty(
"App::PropertyFloat", "head", "gear_parameter", "head_value * modul_value = additional length of head")
obj.addProperty(
"App::PropertyBool", "properties_from_tool", "gear_parameter", "if beta is given and properties_from_tool is enabled, \
gear parameters are internally recomputed for the rotated gear")
obj.addProperty("App::PropertyPythonObject",
"gear", "gear_parameter", "test")
obj.addProperty("App::PropertyLength", "dw",
"computed", "pitch diameter", 1)
obj.addProperty("App::PropertyLength", "transverse_pitch",
"computed", "transverse_pitch", 1)
obj.addProperty("App::PropertyLength", "outside_diameter",
"computed", "Outside diameter", 1)
self.add_limiting_diameter_properties(obj)
obj.gear = self.involute_tooth
obj.simple = False
obj.teeth = 15
obj.module = '1. mm'
obj.shift = 0.
obj.pressure_angle = '20. deg'
obj.beta = '0. deg'
obj.height = '5. mm'
obj.thickness = '5 mm'
obj.clearance = 0.25
obj.head = -0.4 # using head=0 and shift=0.5 may be better, but makes placeing the pinion less intuitive
obj.numpoints = 6
obj.double_helix = False
obj.backlash = '0.00 mm'
obj.reversed_backlash = False
obj.properties_from_tool = False
self.obj = obj
obj.Proxy = self
def add_limiting_diameter_properties(self, obj):
obj.addProperty("App::PropertyLength", "da",
"computed", "inside diameter", 1)
obj.addProperty("App::PropertyLength", "df",
"computed", "root diameter", 1)
def generate_gear_shape(self, fp):
fp.gear.double_helix = fp.double_helix
fp.gear.m_n = fp.module.Value
fp.gear.z = fp.teeth
fp.gear.undercut = False # no undercut for internal gears
fp.gear.shift = fp.shift
fp.gear.pressure_angle = fp.pressure_angle.Value * np.pi / 180.
fp.gear.beta = fp.beta.Value * np.pi / 180
fp.gear.clearance = fp.head # swap head and clearance to become "internal"
fp.gear.backlash = fp.backlash.Value * \
(fp.reversed_backlash - 0.5) * 2. # negate "reversed_backslash", for "internal"
fp.gear.head = fp.clearance # swap head and clearance to become "internal"
# checksbackwardcompatibility:
if "properties_from_tool" in fp.PropertiesList:
fp.gear.properties_from_tool = fp.properties_from_tool
fp.gear._update()
# computed properties
fp.dw = "{}mm".format(fp.gear.dw)
fp.transverse_pitch = "{}mm".format(fp.gear.pitch)
fp.outside_diameter = fp.dw + 2 * fp.thickness
# checksbackwardcompatibility:
if not "da" in fp.PropertiesList:
self.add_limiting_diameter_properties(fp)
fp.da = "{}mm".format(fp.gear.df) # swap addednum and dedendum for "internal"
fp.df = "{}mm".format(fp.gear.da) # swap addednum and dedendum for "internal"
pts = fp.gear.points(num=fp.numpoints)
rotated_pts = pts
rot = rotation(-fp.gear.phipart)
for i in range(fp.gear.z - 1):
rotated_pts = list(map(rot, rotated_pts))
pts.append(np.array([pts[-1][-1], rotated_pts[0][0]]))
pts += rotated_pts
pts.append(np.array([pts[-1][-1], pts[0][0]]))
outer_circle = Part.Wire(Part.makeCircle(fp.outside_diameter / 2.))
if not fp.simple:
wi = []
for i in pts:
out = BSplineCurve()
out.interpolate(list(map(fcvec, i)))
wi.append(out.toShape())
wi = Wire(wi)
wi.reverse() # turn inside out
if fp.height.Value == 0:
return Part.makeCompound([outer_circle, wi])
elif fp.beta.Value == 0:
sh = Face([outer_circle, wi])
return sh.extrude(App.Vector(0, 0, fp.height.Value))
else:
tool = helicalextrusion(
wi, fp.height.Value, fp.height.Value * np.tan(fp.gear.beta) * 2 / fp.gear.d, fp.double_helix)
return Part.makeCylinder(fp.outside_diameter / 2., fp.height.Value).cut(tool)
else:
inner_circle = Part.Wire(Part.makeCircle(fp.dw / 2.))
inner_circle.reverse()
sh = Face([outer_circle, inner_circle])
return sh.extrude(App.Vector(0, 0, fp.height.Value))
def __getstate__(self):
return None
def __setstate__(self, state):
return None
class InvoluteGearRack(BaseGear):

View File

@@ -37,6 +37,7 @@ class GearWorkbench(Workbench):
Icon = os.path.join(__dirname__, 'icons', 'gearworkbench.svg')
commands = [
"CreateInvoluteGear",
"CreateInternalInvoluteGear",
"CreateInvoluteRack",
"CreateCycloidGear",
"CreateBevelGear",
@@ -50,7 +51,7 @@ class GearWorkbench(Workbench):
return "Gui::PythonWorkbench"
def Initialize(self):
from .commands import CreateCycloidGear, CreateInvoluteGear
from .commands import CreateCycloidGear, CreateInvoluteGear, CreateInternalInvoluteGear
from .commands import CreateBevelGear, CreateInvoluteRack, CreateCrownGear
from .commands import CreateWormGear, CreateTimingGear, CreateLanternGear
from .commands import CreateHypoCycloidGear
@@ -59,6 +60,7 @@ class GearWorkbench(Workbench):
self.appendMenu("Gear", self.commands)
# Gui.addIconPath(App.getHomePath()+"Mod/gear/icons/")
Gui.addCommand('CreateInvoluteGear', CreateInvoluteGear())
Gui.addCommand('CreateInternalInvoluteGear', CreateInternalInvoluteGear())
Gui.addCommand('CreateCycloidGear', CreateCycloidGear())
Gui.addCommand('CreateBevelGear', CreateBevelGear())
Gui.addCommand('CreateInvoluteRack', CreateInvoluteRack())