diff --git a/plugins/gear_generator/README.md b/plugins/gear_generator/README.md
new file mode 100644
index 0000000..954f504
--- /dev/null
+++ b/plugins/gear_generator/README.md
@@ -0,0 +1,73 @@
+# Gear generator
+
+This plugin provide classes to create various gears.
+As for now you can create these gears (all the gears are involutes):
+* Spur gear
+
+
+
+* Helical gear
+
+
+
+* Bevel gear (straight and helical)
+
+
+
+* Bevel gear system (straight and helical)
+
+
+
+
+## Installation
+
+To install this plugin, the following line should be used.
+
+```
+pip install -e "git+https://github.com/CadQuery/cadquery-plugins.git#egg=gear_generator&subdirectory=plugins/gear_generator"
+```
+
+
+## Dependencies
+
+This plugin has no dependencies other than the cadquery library.
+
+## Usage
+
+To use this plugin after it has been installed, import it and create Gear objects
+```python
+import cadquery as cq
+import gear_generator
+
+module = 2
+nb_teeth = 12
+width = 8
+gear = Gear(module, nb_teeth, width).build() #Instantiate a gear object and call it's build method to get the gear in a cq.Workplane
+```
+
+
+Below is the list of implemented gear classes :
+```python
+Gear(args)
+BevelGear(args)
+BevelGearSystem(args)
+
+#You can get info about the parameters by running
+help(BevelGear)
+
+Help on class Gear in module gear_generator.main:
+
+class Gear(BaseGear)
+ | Gear(m: float, z: int, b: float, alpha: float = 20, helix_angle: float = 0, raw: bool = False)
+ |
+ | Base gear class
+ | This class stores attributes that are shared by any types of gear
+ | Other gear classes inherit from this class
+ |
+ | Attributes :
+ | m : gear modulus
+ | b : gear tooth facewidth
+ | z : gear number of teeth
+ | p : gear pitch
+-- Suite --
+```
diff --git a/plugins/gear_generator/gear_generator/__init__.py b/plugins/gear_generator/gear_generator/__init__.py
new file mode 100644
index 0000000..c313e3a
--- /dev/null
+++ b/plugins/gear_generator/gear_generator/__init__.py
@@ -0,0 +1 @@
+from .main import *
\ No newline at end of file
diff --git a/plugins/gear_generator/gear_generator/helpers.py b/plugins/gear_generator/gear_generator/helpers.py
new file mode 100644
index 0000000..c1c7b80
--- /dev/null
+++ b/plugins/gear_generator/gear_generator/helpers.py
@@ -0,0 +1,48 @@
+from math import cos, sin, radians, acos
+import cadquery as cq
+
+def involute(r: float, sign: int = 1):
+ """
+ Defines an involute curve to create the flanks of the involute gears
+
+ Args:
+ r : Radius of the involute (for a gear it's the pitch radius)
+ sign : 1 or -1 , to draw the involute in positive or negative direction
+
+ Returns:
+ x,y -> tuple() : 2-tuple of x and y coordinates in space
+ """
+ def curve(t):
+ x = r*(cos(t) + t*sin(t))
+ y = r*(sin(t) - t*cos(t))
+ return x,sign*y
+ return curve
+
+def spherical_involute(delta, delta_b, R):
+ """
+ Equation of the spherical involute that lies on a sphere
+
+ Args:
+ delta : the function variable, goes from the gear root cone angle to the gear tip cone angle
+ delta_b : angle of the base cone
+ R : radius of the associated sphere
+
+ Returns:
+ x,y,z -> tuple() : 3-tuple of x and y and z coordinates in space
+ """
+ theta = acos(cos(delta)/cos(delta_b))/sin(delta_b)
+ x = R*cos(theta*sin(delta_b))*sin(delta_b)*cos(theta) - R*sin(theta*sin(delta_b))* - sin(theta)
+ y = R*cos(theta*sin(delta_b))*sin(delta_b)*sin(theta) - R*sin(theta*sin(delta_b))* cos(theta)
+ z = R*cos(theta*sin(delta_b))*cos(delta_b)
+ return x,y,z
+
+
+
+def rotate_vector_2D(vector: cq.Vector, angle: float):
+ """
+ Rotates a 2D cq.Vector `vector`by an angle of `angle` in degrees
+ """
+ angle = radians(angle)
+ x = cos(angle)*vector.x - sin(angle)*vector.y
+ y = sin(angle)*vector.x + cos(angle)*vector.y
+ return cq.Vector((x,y))
\ No newline at end of file
diff --git a/plugins/gear_generator/gear_generator/main.py b/plugins/gear_generator/gear_generator/main.py
new file mode 100644
index 0000000..0c58d07
--- /dev/null
+++ b/plugins/gear_generator/gear_generator/main.py
@@ -0,0 +1,362 @@
+import cadquery as cq
+from math import pi, cos, sin, tan, sqrt, degrees, radians, atan2, atan, acos, asin
+from .helpers import involute, spherical_involute, rotate_vector_2D
+
+class BaseGear:
+ """Base gear class
+ This class stores attributes that are shared by any types of gear
+ Other gear classes inherit from this class
+
+ Attributes :
+ m : gear modulus
+ b : gear tooth facewidth
+ z : gear number of teeth
+ p : gear pitch
+ r_p : gear pitch radius
+ alpha : gear pressure angle (radians)
+ helix_angle : helix angle for helical bevel gear (radians)
+
+ """
+
+ def __init__(self, m: float, z: int, b: float, alpha: float, helix_angle: float):
+ """
+ Initialize a gear object with it's parameters
+
+ Args:
+ m : gear modulus
+ b : gear tooth facewidth
+ z : gear number of teeth
+ alpha : gear pressure angle (degrees)
+ helix_angle : helix angle for helical bevel gear (degrees)
+ """
+ self.m = m
+ self.b = b
+ self.z = z
+ self.p = pi*self.m
+ self.r_p = m * z / 2
+ self.alpha = radians(alpha)
+ self.helix_angle = radians(helix_angle)
+
+
+class BevelGear(BaseGear):
+ """Inherits BaseGear class
+
+ This class compute and store all the relevant parameters used to create a bevel gear.
+ The build() method create and returns a cadquery Worplane object with the bevel gear.
+
+ Attributes:
+ clearance : Extra distance at the base of the gear cone to strengthened the gear
+ helix_angle : helix angle for helical bevel gear (radians)
+ right_handed : Direction of the gear helix for helical gears
+ N : Number of sections used to generate helical gear teeth. Higher `N` number gives better teeth surface but increase rendering time
+ delta_p : pitch cone angle (radians)
+ R : radius of the inscribed sphere at pitch circle radius
+ R_min : radius of the inscribed sphere at the end of gear teeth
+ delta_b : base cone angle (radians)
+ theta_a : pitch circle radius to tooth tip radius
+ theta_f : pitch circle radius to tooth root radius
+ r_a : tooth tip circle radius
+ r_f : tooth root circle radius
+ theta_p : angle of rotation of the involute curve to have tooth tickness = p at pitch circle
+ """
+ __doc__ = BaseGear.__doc__ + __doc__
+
+ def __init__(self, m: float, z: int , b: int, delta_p: float, R: float, clearance: float = None, alpha: float = 15, helix_angle: float = 0, right_handed: bool = True, N: int = 4):
+
+ """
+ Args:
+ m : gear modulus
+ z : gear number of teeth
+ b : gear tooth facewidth
+ delta_p : pitch cone angle (degrees)
+ R : radius of the inscribed sphere at pitch circle radius
+ clearance : extra distance at the base of the gear cone to strengthened the gear
+ alpha : pressure angle (degrees)
+ helix_angle : helix angle for helical bevel gear (degrees)
+ right_handed : direction of the helix for helical bevel gear
+ N : number of tooth sections to interpolate when creating a spiral bevel gear
+
+ """
+ if clearance is None:
+ self.clearance = m*z/10
+ else :
+ self.clearance = clearance
+ super().__init__(m, z, b, alpha, helix_angle)
+ self.right_handed = right_handed
+ self.N = N
+ self.delta_p = radians(delta_p) #pitch cone angle
+ self.R = R
+ self.R_min = R - b
+ self.delta_b = asin(sin(self.delta_p)*cos(self.alpha))
+ self.r_b = R * sin(self.delta_b)
+ self.theta_a = atan2(m, R)
+ self.theta_f = atan2(1.25*m, R)
+ self.delta_a = self.delta_p + self.theta_a
+ self.delta_f = self.delta_p - self.theta_f
+ self.r_a = R*sin(self.delta_a)
+ self.r_f = R*sin(self.delta_f)
+ self.theta_p = self.p/(R*sin(self.delta_p))/4 #angle of rotation of the involute curve to have tooth tickness = p at pitch circle
+
+ def make_tooth_profile(self, radius):
+ """
+ This method creates the tooth profile wire that lie on a sphere of radius `radius`
+ """
+ right_involute = cq.Workplane().parametricCurve(lambda delta : spherical_involute(delta, self.delta_b, radius), start = self.delta_b, stop = self.delta_a).rotate((0,0,0),(0,0,1),-degrees(self.theta_p))
+ left_involute = right_involute.mirror("ZX")
+ right_top_pt = right_involute.val().endPoint()
+ left_top_pt = left_involute.val().endPoint()
+ right_bot_pt = right_involute.val().startPoint()
+ left_bot_pt = left_involute.val().startPoint()
+ end_point = right_top_pt-left_top_pt
+
+ if right_top_pt.y > 0:
+ raise ValueError("Inputed parameters leads to undefined tooth geometry.\nThis is due to either : too high gear ratio (z1/z2), too high pressure angle (alpha) or too big tooth face width (b).\
+ \nIn general higher gear ratio needs lower pressure angle to obtain to good tooth geometry.")
+ top_arc = cq.Workplane("XY", origin=left_top_pt.toTuple()).radiusArc(end_point.toTuple(), self.r_a)
+ pos = (0,0,radius*cos(self.delta_f))
+
+ if self.r_b <= self.r_f: #if undercut
+ bot_arc = cq.Edge.makeCircle(self.r_f, pos, (0,0,1), angle1 = -self.theta_p, angle2 = self.theta_p)
+ tooth_profile = cq.Wire.assembleEdges([right_involute.val(), top_arc.val(), left_involute.val(), bot_arc])
+ else:
+ bot_arc = cq.Edge.makeCircle(tan(self.delta_f)*(radius*cos(self.delta_f)), pos, (0,0,1), angle1=-degrees(self.theta_p), angle2=degrees(self.theta_p))
+ left_bot_arc_pt = bot_arc.endPoint()
+ right_bot_arc_pt = bot_arc.startPoint()
+ left_bot_line = cq.Edge.makeLine(left_bot_arc_pt,left_bot_pt)
+ right_bot_line = cq.Edge.makeLine(right_bot_arc_pt,right_bot_pt)
+
+ tooth_profile = cq.Wire.assembleEdges([right_involute.val(), top_arc.val(), left_involute.val(), left_bot_line, bot_arc, right_bot_line])
+
+ return tooth_profile
+
+ def make_tooth(self):
+ """
+ This function creates the tooth solid from lofting tooth section wires.
+ If the gear is standard, loft from wire at R radius to wire at R_min radius.
+ If the gear is helical, creates intermediary tooth profiles rotated along the cone height. Finally loft by all the tooth sections.
+ """
+ N = self.N
+ R_sphere = cq.Solid.makeSphere(self.R,angleDegrees1=-90, angleDegrees2=90).rotate((0,0,0),(0,0,1),90)
+ Rmin_sphere = cq.Solid.makeSphere(self.R_min,angleDegrees1=-90, angleDegrees2=90).rotate((0,0,0),(0,0,1),90)
+ outer = self.make_tooth_profile(self.R*1.2)
+ inner = self.make_tooth_profile(self.R_min*0.8)
+ splitter = cq.Solid.makeLoft([outer,inner])
+
+ closing_face1 = R_sphere.Faces()[0].split(splitter).Faces()[1] # cuts a sphere to retrieve the surface enclosed by tooth profile wire
+ closing_face2 = Rmin_sphere.Faces()[0].split(splitter).Faces()[1] # cuts a sphere to retrieve the surface enclosed by tooth profile wire
+
+ sections = []
+ dr = (self.b)/N
+ dangle = degrees(self.b * tan(self.helix_angle) / self.r_p) / N
+
+ if self.helix_angle == 0:
+ tooth = cq.Solid.makeLoft([closing_face1.Wires()[0], closing_face2.Wires()[0]])
+ else :
+ if self.right_handed:
+ rot_dir = -1
+ else:
+ rot_dir = 1
+ for i in range(N+1):
+ sections.append(self.make_tooth_profile(self.R-dr*i).rotate((0,0,0),(0,0,1),rot_dir*dangle*i))
+ sides = cq.Solid.makeLoft(sections)
+
+ closing_face2 = Rmin_sphere.Faces()[0].split(sides).Faces()[1] # cuts a sphere to retrieve the surface enclosed by tooth profile wire
+ sections[0] = closing_face1.Wires()[0]# remove the first and last section since we will loft from the closing face wires
+ sections[-1] = closing_face2.Wires()[0]
+
+
+ sides = cq.Solid.makeLoft(sections) # There is a bug here, the solid isnt valid for unknown reasons.
+ shell = cq.Shell.makeShell([closing_face1, sides, closing_face2]) # But the workaround is to build a shell from the faces that are valid
+ tooth = cq.Solid.makeSolid(shell)
+
+ return tooth
+
+ def build(self):
+ """
+ Creates the cadquery objects and returns them as a cq.Workplane
+
+ Returns:
+ gear -> cq.Workplane : The gear shape in a cq.Workplane
+ """
+ tooths = []
+ tooth = self.make_tooth()
+
+ for i in range(1,self.z+1):
+ angle = i*360/self.z
+ tooths.append(tooth.rotate(cq.Vector(0,0,0),cq.Vector(0,0,1),angle))
+
+ comp = cq.Compound.makeCompound(tooths)
+
+
+ self.gear = (cq.Workplane("ZX",obj=comp).moveTo(self.R_min*cos(self.delta_f),0)
+ .vLineTo(self.R_min*sin(self.delta_f))
+ .lineTo(self.R*cos(self.delta_f),self.R*sin(self.delta_f))
+ .line(self.clearance*sin(self.delta_p), -self.clearance*cos(self.delta_p))
+ .vLineTo(0)
+ .close()
+ .revolve(axisStart=(0,0,0), axisEnd=(1,0,0))
+ )
+ return self.gear
+
+class BevelGearSystem():
+ """Inherits BaseGear class
+ This class compute the right parameters for the creation of a system of 2 bevels gears positioned in a way they can mesh.
+
+ Attributes:
+ m : gears modulus (gears can mesh only if they have the same modulus)
+ b : gears tooth facewidth
+ z1 : pinion number of teeth
+ z2 : gear number of teeth
+ delta_p1 : pinion pitch cone angle (radians)
+ delta_p2 : gear pitch cone angle (radians)
+ clearance1 : extra distance at the base of the pinion cone to strengthened the pinion
+ clearance2 : extra distance at the base of the gear cone to strengthened the gear
+ helix_angle : helix angle of the pinion and gear teeth (radians)
+ alpha : pressure angle of the pinion and gear (radians)
+ R : radius of the inscribed sphere at pitch circle radius
+ N : number of tooth sections to interpolate when creating a spiral bevel gear system
+ """
+ __doc__ = BaseGear.__doc__ + __doc__
+
+ def __init__(self, m: float, z1: int, z2: int, b: float, clearance1: float = None, clearance2: float = None, helix_angle: float = 0, alpha: float = 15, N: int = 4):
+ """
+ Args:
+ m : gears modulus (gears can mesh only if they have the same modulus)
+ z1 : pinion number of teeth
+ z2 : gear number of teeth (note that if z1 > z2, the value will be swap to be sure that the pinion is always the gear with the smallest number of teeth)
+ b : gears tooth facewidth
+ clearance1 : extra distance at the base of the pinion cone to strengthened the pinion
+ clearance2 : extra distance at the base of the gear cone to strengthened the gear
+ alpha : pressure angle (degrees)
+ helix_angle : helix angle of the pinion and gear teeth (degrees)
+ alpha : pressure angle of the pinion and gear (degrees)
+ N : number of tooth sections to interpolate when creating a spiral bevel gear system
+
+ """
+ if z1>z2:
+ z1,z2 = z2,z1
+ if clearance1 == None:
+ self.clearance1 = m*z1/20
+ else:
+ self.clearance1 = clearance1
+ if clearance2 == None:
+ self.clearance2 = m*z2/20
+ else:
+ self.clearance2 = clearance2
+ self.m = m
+ self.b = b
+ self.z1 = z1
+ self.z2 = z2
+ self.delta_p1 = degrees(atan2(z1, z2)) #pitch cone angle
+ self.delta_p2 = degrees(atan2(z2, z1)) #pitch cone angle
+ self.R = (m*z2/2)/sin(radians(self.delta_p2))
+ self.helix_angle = helix_angle
+ self.alpha = alpha
+ self.N = N
+
+ def build(self):
+ """
+ Creates the cadquery objects and returns them as a cq.Workplane
+
+ Returns:
+ pinion, gear -> tuple(cq.Workplane, cq.Workplane) : The pinion and gear bevel gears of the system positionned correctly in space
+ """
+ self.pinion = BevelGear(self.m, self.z1, self.b, self.delta_p1, self.R, self.clearance1, helix_angle=self.helix_angle, right_handed=False, alpha=self.alpha, N=self.N)
+ self.gear = BevelGear(self.m, self.z2, self.b, self.delta_p2, self.R, self.clearance2, helix_angle=self.helix_angle, alpha=self.alpha, N=self.N)
+
+ if self.gear.z % 2 == 0:
+ return self.pinion.build(), self.gear.build().rotate((0,0,0),(0,1,0), 90).rotate((0,0,0),(1,0,0), (360/self.gear.z)/2)
+ else:
+ return self.pinion.build(), self.gear.build().rotate((0,0,0),(0,1,0),90)
+
+class Gear(BaseGear):
+ """Inherits BaseGear
+ """
+ __doc__ = BaseGear.__doc__ + __doc__
+
+ def __init__(self, m: float, z: int, b: float, alpha: float = 20, helix_angle: float = 0, raw: bool = False):
+ """
+ Args:
+ m : gear modulus
+ b : gear tooth facewidth
+ z : gear number of teeth
+ alpha : gear pressure angle (degrees)
+ helix_angle : helix angle for helical gear (degrees)
+ raw : `False` : adds filleting a the root teeth edges; `True` : leaves the gear with no filleting
+ """
+ super().__init__(m, z, b, alpha, helix_angle)
+ self.raw = raw
+
+ self.r_a = self.r_p + m
+ self.r_b = self.r_p*cos(self.alpha)
+ self.r_f = self.r_p - 1.25*m
+
+ def make_tooth_profile(self):
+ """
+ Creates a single tooth wire to be rotated to create the full cross section of the gear
+
+ Returns:
+ cq.Wire : a wire representing the tooth wire
+ """
+ inv = lambda a: tan(a) - a
+
+ # angles of interest
+ alpha_inv = inv(self.alpha)
+ alpha_tip = acos(self.r_b/self.r_a)
+ alpha_tip_inv = inv(alpha_tip)
+ a = 90/self.z + degrees(alpha_inv)
+ a2 = 90/self.z + degrees(alpha_inv)-degrees(alpha_tip_inv)
+ STOP = sqrt((self.r_a/self.r_b)**2 - 1)
+
+ # construct all the profiles
+ left = (
+ cq.Workplane()
+ .transformed(rotate=(0,0,a))
+ .parametricCurve(involute(self.r_b,-1), start=0, stop = STOP, makeWire=False, N=8)
+ .val()
+ )
+
+ right = (
+ cq.Workplane()
+ .transformed(rotate=(0,0,-a))
+ .parametricCurve(involute(self.r_b), start=0, stop = STOP, makeWire=False, N=8)
+ .val()
+ )
+
+ top = cq.Edge.makeCircle(self.r_a,angle1=-a2, angle2=a2)
+
+ p0 = left.startPoint()
+ p1 = right.startPoint()
+ side0 = cq.Workplane(origin=p0.toTuple()).hLine(self.r_f-self.r_b).val()
+ side1 = cq.Workplane(origin=p1.toTuple()).hLine(self.r_f-self.r_b).val()
+ side2 = side0.rotate(cq.Vector(0, 0, 0), cq.Vector(0, 0, 1), -360/self.z)
+
+ p_bot_left = side1.endPoint()
+ p_bot_right = rotate_vector_2D(side0.endPoint(), -360/self.z)
+
+ bottom = cq.Workplane().moveTo(p_bot_left.x, p_bot_left.y).radiusArc(p_bot_right,self.r_f).val()
+
+ # single tooth profile
+ profile = cq.Wire.assembleEdges([left,top,right,side1,bottom, side2])
+ # return cross_section
+ if not self.raw:
+ profile = profile.fillet2D(0.25*self.m, profile.Vertices()[-3:-1])
+ return profile
+
+ def build(self) :
+ """
+ Creates the gear and returns it in a cq.Workplane
+ """
+ # complete cross section
+ gear = (
+ cq.Workplane()
+ .polarArray(0,0,360,self.z)
+ .each(lambda loc: self.make_tooth_profile().located(loc))
+ .consolidateWires()
+ )
+ if self.helix_angle == 0:
+ self.gear = gear.extrude(self.b)
+ else:
+ self.gear = gear.twistExtrude(self.b, degrees(self.b * tan(self.helix_angle))/self.r_p)
+
+ return self.gear
diff --git a/plugins/gear_generator/images/bevel_gear.PNG b/plugins/gear_generator/images/bevel_gear.PNG
new file mode 100644
index 0000000..cc9b835
Binary files /dev/null and b/plugins/gear_generator/images/bevel_gear.PNG differ
diff --git a/plugins/gear_generator/images/bevel_gear_system.PNG b/plugins/gear_generator/images/bevel_gear_system.PNG
new file mode 100644
index 0000000..2db2d9a
Binary files /dev/null and b/plugins/gear_generator/images/bevel_gear_system.PNG differ
diff --git a/plugins/gear_generator/images/helical_gear.PNG b/plugins/gear_generator/images/helical_gear.PNG
new file mode 100644
index 0000000..3a0f74b
Binary files /dev/null and b/plugins/gear_generator/images/helical_gear.PNG differ
diff --git a/plugins/gear_generator/images/readme_example.PNG b/plugins/gear_generator/images/readme_example.PNG
new file mode 100644
index 0000000..50ef59b
Binary files /dev/null and b/plugins/gear_generator/images/readme_example.PNG differ
diff --git a/plugins/gear_generator/images/straight_gear.PNG b/plugins/gear_generator/images/straight_gear.PNG
new file mode 100644
index 0000000..1484155
Binary files /dev/null and b/plugins/gear_generator/images/straight_gear.PNG differ
diff --git a/plugins/gear_generator/setup.py b/plugins/gear_generator/setup.py
new file mode 100644
index 0000000..5255ede
--- /dev/null
+++ b/plugins/gear_generator/setup.py
@@ -0,0 +1,44 @@
+from setuptools import setup, find_packages
+
+version = '1.0.0' # Please update this version number when updating the plugin
+plugin_name = 'gear_generator'
+description = 'Add classes to create all sort of gears'
+long_description = ''
+author = 'Romain FERRU'
+author_email = 'Romain.ferru@gmail.com'
+install_requires = [] # Any dependencies that pip also needs to install to make this plugin work
+
+
+setup(
+ name=plugin_name,
+ version=version,
+ url='https://github.com/CadQuery/cadquery-plugins',
+ license='Apache Public License 2.0',
+ author=author,
+ author_email=author_email,
+ description=description,
+ long_description=long_description,
+ packages=find_packages(where='gear_generator'),
+ install_requires=install_requires,
+ include_package_data=True,
+ zip_safe=False,
+ platforms='any',
+ test_suite='tests',
+
+ classifiers=[
+ 'Development Status :: 5 - Production/Stable',
+ 'Intended Audience :: Developers',
+ 'Intended Audience :: End Users/Desktop',
+ 'Intended Audience :: Information Technology',
+ 'Intended Audience :: Science/Research',
+ 'Intended Audience :: System Administrators',
+ 'License :: OSI Approved :: Apache Software License',
+ 'Operating System :: POSIX',
+ 'Operating System :: MacOS',
+ 'Operating System :: Unix',
+ 'Programming Language :: Python',
+ 'Topic :: Software Development :: Libraries :: Python Modules',
+ 'Topic :: Internet',
+ 'Topic :: Scientific/Engineering'
+ ]
+)
diff --git a/tests/test_gear_generator.py b/tests/test_gear_generator.py
new file mode 100644
index 0000000..aebbf8d
--- /dev/null
+++ b/tests/test_gear_generator.py
@@ -0,0 +1,78 @@
+import cadquery as cq
+from plugins.gear_generator.gear_generator import *
+from unittest import TestCase
+from math import sin, radians
+
+class TestGearGenerator(TestCase):
+ def test_bevel_gear(self):
+ """
+ Tests if the bevel gear has been created successfully
+ by checking if it's valid and it's resulting volume
+ Numerical values volumes were obtained with the Volume() functions on a Windows 10 machine with python 3.9.2
+ """
+ m = 1.5
+ z = 16
+ b = 6
+ delta = 45
+ R = (m*z/2) / sin(radians(delta))
+ alpha = 20
+ helix_angle = 20
+ clearance = 6
+
+ gear1 = BevelGear(m, z, b, delta, R, alpha = alpha, clearance = clearance).build()
+ gear2 = BevelGear(m, z, b, delta, R, alpha = alpha, clearance = clearance, helix_angle=helix_angle).build()
+
+ self.assertTrue(gear1.val().isValid())
+ self.assertAlmostEqual(gear1.val().Volume(),2505.7966225157024,1)
+
+ self.assertTrue(gear2.val().isValid())
+ self.assertAlmostEqual(gear1.val().Volume(),2505.7531046471067,1)
+
+
+ def test_bevel_gear_system(self):
+ """
+ Tests if the bevel gear system been created successfully
+ by checking if it's valid and it's resulting volume
+ Numerical values volumes were obtained with the Volume() functions on a Windows 10 machine with python 3.9.2
+ """
+ m = 1
+ z1 = 16
+ z2 = 22
+ b = 2
+ alpha = 20
+ helix_angle = 20
+ clearance = 3
+
+ pinion1, gear1 = BevelGearSystem(m, z1, z2, b, clearance1 = clearance, clearance2= clearance, alpha=alpha).build()
+ self.assertTrue(pinion1.val().isValid())
+ self.assertTrue(gear1.val().isValid())
+ self.assertAlmostEqual(pinion1.val().Volume(),483.3443067836134,1)
+ self.assertAlmostEqual(gear1.val().Volume(),1126.5921554741049,1)
+
+ pinion2, gear2 = BevelGearSystem(m, z1, z2, b, clearance1 = clearance, clearance2= clearance, helix_angle=helix_angle, alpha=alpha).build()
+ self.assertTrue(pinion2.val().isValid())
+ self.assertTrue(gear2.val().isValid())
+ self.assertAlmostEqual(pinion2.val().Volume(),483.3523144907097,1)
+ self.assertAlmostEqual(gear2.val().Volume(),1126.605502545581,1)
+
+ def test_gear(self):
+ """
+ Tests if the gear has been created successfully
+ by checking if it's valid and it's resulting volume
+ Numerical values volumes were obtained with the Volume() functions on a Windows 10 machine with python 3.9.2
+ """
+ m = 1.5
+ z = 22
+ b = 12
+ alpha = 14
+ helix_angle = 35
+
+ gear1 = Gear(m, z, b, alpha=alpha, raw = False).build()
+ self.assertTrue(gear1.val().isValid())
+ self.assertTrue((gear1.val().Volume() > 10113 - 70 and gear1.val().Volume() < 10113 + 70))
+
+ gear2 = Gear(m, z, b, alpha=alpha, helix_angle = helix_angle, raw = True).build()
+ self.assertTrue(gear2.val().isValid())
+ self.assertAlmostEqual(gear2.val().Volume(),10033.574314576623,1)
+
+