diff --git a/.gitignore b/.gitignore index b6e4761..89e437d 100644 --- a/.gitignore +++ b/.gitignore @@ -127,3 +127,5 @@ dmypy.json # Pyre type checker .pyre/ +*.ipynb +plugins/gear_generator/src/gear-generator/ diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..2c7c8e4 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "python.pythonPath": "D:\\Programmes\\miniconda\\envs\\test_gear_generator\\python.exe" +} \ No newline at end of file diff --git a/plugins/gear_generator/README.md b/plugins/gear_generator/README.md new file mode 100644 index 0000000..af6ecbf --- /dev/null +++ b/plugins/gear_generator/README.md @@ -0,0 +1,95 @@ +# Gear generator + +This plugin provide additionals methods to create various gears. +As for now you can create these gears (all the gears are involutes): +* Spur gear + + + +* Helical gear + + + +* Rack gear + + + +* Helical rack gear + + + +* Crown (or face) gear + + + +* Bevel gear (straight) (experimental) + + + +* Bevel gear system (straight) (very experimental) + + + + +## 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, just import it and use the make_... methods to create your gears +```python +import cadquery as cq +import gear_generator # automatically links the plugin functions to the cq.Workplane class + +module = 2 +nb_teeth = 12 +width = 8 +gear = cq.Workplane("XY").make_gear(module, nb_teeth, width) +``` + + +Currently in cq-editor the automatical linking doesn't work so you should try to link it manually as below : +```python +import cadquery as cq +import gear_generator +gear_generator.cutter_objects.register_cutter_objects() +gear_generator.register() +``` + +Below is the list of implemented methods : +```python +cq.Workplane().make_gear(params) +cq.Workplane().make_rack_gear(params) +cq.Workplane().make_crown_gear(params) +cq.Workplane().make_bevel_gear(params) +cq.Workplane().make_bevel_gear_system(params) + +#You can get info about the parameters by running +help(cq.Workplane().make_gear) +>>> _make_gear(m, z, b, alpha=20, helix_angle=None, raw=False) method of cadquery.cq.Workplane instance +>>> Creates a spur or helical involute gear +>>> +>>> Parameters +>>> ---------- +>>> m : float +>>> Spur gear modulus +>>> z : int +>>> Number of teeth +>>> b : float +>>> Tooth width +>>> alpha : float +>>> Pressure angle in degrees, industry standard is 20\ufffd +>>> helix_angle : float +>>> Helix angle of the helical gear in degrees +>>> If None creates a spur gear, if specified create a helical gear +``` diff --git a/plugins/gear_generator/gear_generator/__init__.py b/plugins/gear_generator/gear_generator/__init__.py new file mode 100644 index 0000000..adbbe21 --- /dev/null +++ b/plugins/gear_generator/gear_generator/__init__.py @@ -0,0 +1,9 @@ +from .main import ( + _make_gear, + _make_bevel_gear, + _make_bevel_gear_system, + _make_crown_gear, + _make_rack_gear, + register +) +register() \ No newline at end of file diff --git a/plugins/gear_generator/gear_generator/cutter_objects.py b/plugins/gear_generator/gear_generator/cutter_objects.py new file mode 100644 index 0000000..1a8380f --- /dev/null +++ b/plugins/gear_generator/gear_generator/cutter_objects.py @@ -0,0 +1,182 @@ +""" +This module stores functions that are used by the make_XXX_gear functions but are not intended to be used by the final user +""" + +import cadquery as cq +from math import * +from .helpers import involute, test_bevel_parameters, rotate_vector_2D + + +def _make_rack_tooth_gap(self, m, b, alpha = 20, helix_angle = None): + """ + Creates a solid which represents the gap between the teeth of the rack gear + + Parameters + ---------- + m : float + Crown gear modulus + b : float + Tooth width + alpha : float + Pressure angle in degrees, industry standard is 20° + helix_angle : float + Helix angle in degrees to create a helical rack gear + + Returns + ------- + cq.Workplane + Returns the tooth gap solid in a cq.Workplane using eachpoint method + """ + p = pi*m + alpha = radians(alpha) + + A = (-2.25*m*sin(alpha)-(p/2 - 2*m*sin(alpha)),0) + B = (-(p/2 - 2*m*sin(alpha)), -2.25*m*cos(alpha)) + C = (B[0] + (p/2 - 2*m*sin(alpha)), -2.25*m*cos(alpha)) + D = (C[0] + 2.25*m*sin(alpha), 0) + tooth_wire = (cq.Workplane("XY").polyline([A,B,C,D])).close() + if helix_angle is None: + tooth = tooth_wire.extrude(b) + else: + helix_angle = radians(helix_angle) + tooth = tooth_wire.center(tan(helix_angle)*b, 0).workplane(offset=b).polyline([A,B,C,D]).close().loft() + return self.eachpoint(lambda loc: tooth.val().located(loc), True) + +def _make_bevel_tooth_gap_wire(self, Z_W, m, phi, r_a, r_f, r_base): + """ + Creates the bevel tooth gap wire that will be lofted to a vertex to make the cutter object + + Parameters + ---------- + Z_W : float + Z position of the top of the complementary cone in the coord system which as it's origin at the bevel gear cone top point + m : float + Bevel gear modulus + phi : float + Complementary cone angle in radians + r_a : float + Associated virtual spur gear top radius + r_f : float + Associated virtual spur gear root radius + r_base : float + Associated virtual spur gear base radius + + Returns + ------- + cq.Workplane + Returns a wire in a cq.Workplane that defines the tooth gap cross section at the bevel outer radius + """ + STOP = 2*sqrt((r_a/r_base)**2 - 1) # 2* To be sure to have extra working room + #right side + right = (cq.Workplane("XY", origin=(0,0,Z_W)).transformed(rotate=(-pi*m,-90+degrees(phi),0)) + .tag("baseplane") + .parametricCurve(involute(r_base), N=8, stop=STOP, makeWire=False)) + bot_right = right.moveTo(r_f,0).hLine(r_base-r_f) + #left side + left = (cq.Workplane("XY", origin=(0,0,Z_W)).transformed(rotate=(pi*m,-90+degrees(phi),0)) + .moveTo(r_f,0) + .hLine(r_base-r_f) + .parametricCurve(involute(r_base, sign=-1), N=8, stop=STOP, makeWire=False)) + bot_left = left.moveTo(r_f,0).hLine(r_base-r_f) + #Getting points to close the wire + pt_top_right = right.vertices(">X").val() + pt_bot_right = bot_right.vertices("X").val() + pt_bot_left = bot_left.vertices(" clearance_max: + raise ValueError(f"Too much clearance, for this set of parameters clearance must be <= {round(clearance_max,3)}") + +def rotate_vector_2D(vector, angle): + """ + Rotates a 2D cq.Vector + + Parameters + ---------- + vector : cq.Vector + Vector to be rotated + angle : float + The angle in degrees + + Returns + ------- + cq.Vector + Returns a vector rotated by the specified angle + """ + 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..9e39f52 --- /dev/null +++ b/plugins/gear_generator/gear_generator/main.py @@ -0,0 +1,306 @@ +import cadquery as cq +from math import pi, cos, sin, tan, sqrt, degrees, radians, atan2, atan, acos +from .helpers import involute, test_bevel_parameters, rotate_vector_2D +from .cutter_objects import _make_bevel_tooth_gap_wire, _make_rack_tooth_gap, _make_crown_gear_tooth_gap, register_cutter_objects +from OCP.BRepOffsetAPI import BRepOffsetAPI_ThruSections + +register_cutter_objects() + +def _make_bevel_gear(self, m, z, b, delta, alpha = 20, clearance = None): + """ + Creates a bevel gear + + Parameters + ---------- + m : float + Crown gear modulus + z : int + Number of teeth + b : float + Tooth width + delta : float + Cone angle in degrees + alpha : float + Pressure angle in degrees, industry standard is 20° + clearance : float + Spacing after teeth root to add some material below the teeth + Below a (half) bevel gear sketch to understand what is the clearance + ____ + / / } addendum + ____/___/ } dedendum + _______/ } clearance (the length of the tilted bar) + + Returns + ------- + cq.Workplane + Returns the bevel gear solid in a cq.Workplane using eachpoint method + """ + + # PARAMETERS + delta = radians(delta) # cone angle + phi = pi/2 - delta # complementary cone angle + r = m*z/2 # pitch radius + r_inner = r-b*sin(delta) #pitch radius inner radius + r_equiv_outer = r/sin(phi) #pitch radius of the associated virtual spur gear + r_base_equiv = r_equiv_outer*cos(radians(alpha)) #base radius of the associated virtual spur gear + r_f_equiv = (r - 1.25*m*cos(delta))/sin(phi) #root radius of the associated virtual spur gear + r_a_equiv = (r + m*cos(delta))/sin(phi) #top radius of the associated virtual spur gear + h_f = 1.25*m # dedendum + h_a = m # addendum + # Z positions of outer and inner pitch radius and top of complementary cone + Z_P = -r/tan(delta) + Z_P_inner = - r_inner/tan(delta) + Z_W = (Z_P - r/tan(phi))*1.0000001 # This allow for the tooth gap wire to be slightly shifted otherwise leads to failing cut operation + if clearance is None: + clearance = 0.2*r + # Test if input parameters make a valid gear + + test_bevel_parameters(m, z, b, r_inner, delta, alpha, phi, clearance, r_f_equiv, r_base_equiv) + + #Definition of bevel half cross section points + A = (r + h_a*cos(delta), 0, Z_P + h_a*sin(delta)) + B = (r - h_f*cos(delta), 0, Z_P - h_f*sin(delta)) + C = (r - clearance*cos(delta), 0, Z_P - clearance*sin(delta)) + D = (0, 0, C[2]) + H = (r_inner + h_a*cos(delta), 0, Z_P_inner + h_a*sin(delta)) + G = (r_inner - h_f*cos(delta), 0, Z_P_inner - h_f*sin(delta)) + F = (r_inner - clearance*cos(delta), 0, Z_P_inner - clearance*sin(delta)) + E = (0, 0, F[2]) + + #Creation of the half cross section + edges = [] + edges.append(cq.Edge.makeLine(cq.Vector(A), cq.Vector (B))) + edges.append(cq.Edge.makeLine(cq.Vector(B), cq.Vector (C))) + edges.append(cq.Edge.makeLine(cq.Vector(C), cq.Vector (D))) + edges.append(cq.Edge.makeLine(cq.Vector(D), cq.Vector (E))) + edges.append(cq.Edge.makeLine(cq.Vector(E), cq.Vector (F))) + edges.append(cq.Edge.makeLine(cq.Vector(F), cq.Vector (G))) + edges.append(cq.Edge.makeLine(cq.Vector(G), cq.Vector (H))) + edges.append(cq.Edge.makeLine(cq.Vector(H), cq.Vector (A))) + cross_sec = cq.Wire.assembleEdges(edges) + #Making base solid + base = cq.Solid.revolve(cross_sec,[],360, cq.Vector(0,0,0), cq.Vector(0,0,1)) + + tooth_gap = cq.Workplane("XY")._make_bevel_tooth_gap_wire(Z_W, m, phi, r_a_equiv, r_f_equiv, r_base_equiv) + + builder = BRepOffsetAPI_ThruSections(True,False) #Builder to create loft to vertex + builder.AddWire(tooth_gap.val().wrapped) + builder.AddVertex(cq.Vertex.makeVertex(0,0,0).wrapped) + cutter = cq.Workplane(obj=cq.Shape.cast(builder.Shape())) #cutter solid + #Make a compound of cutters + cutters = [] + for i in range(z): + angle = 360/z*i + cutters.append(cutter.rotate((0,0,0),(0,0,1),angle).val()) + final_cutter = cq.Compound.makeCompound(cutters) + gear = base.cut(final_cutter) + return self.eachpoint(lambda loc: gear.located(loc), True) + +def _make_bevel_gear_system(self, m, z1, z2, b, alpha=20, clearance = None, compound = True): + """ + Creates a bevel gear system made of two bevel gears + + Parameters + ---------- + m : float + Bevel gears modulus (to be able to mesh they need the same modulus) + z1 : int + Number of teeth of the first bevel gear + z2 : int + Number of teeth of the second bevel gear + b : float + Tooth width + delta : float + Cone angle in degrees + alpha : float + Pressure angle in degrees, industry standard is 20° + clearance : float + Spacing after teeth root to add some material below the teeth + Below a (half) bevel gear sketch to understand what is the clearance + ____ + / / } addendum + ____/___/ } dedendum + _______/ } clearance (the length of the tilted bar) + + Returns + ------- + cq.Workplane + If compound = True, returns a cq.Workplane object with the two gears in a cq.Compound + tuple + If compound = False, returns a 2-tuple with the two gear solid in a cq.Workplane + """ + delta_1 = degrees(atan2(z2,z1)) + delta_2 = degrees(atan2(z1,z2)) + gear1 = cq.Workplane("XY").make_bevel_gear(m, z1, b, delta_1, alpha = alpha, clearance = clearance) + gear2 = cq.Workplane("YZ").make_bevel_gear(m, z2, b, delta_2, alpha = alpha, clearance = clearance) + + if compound: + compound = cq.Compound.makeCompound([gear1.val(), gear2.val()]) + return self.eachpoint(lambda loc: compound.located(loc), True) + else: + return gear1, gear2 + +def _make_gear(self, m, z, b, alpha=20, helix_angle = None, raw = False): + """ + Creates a spur or helical involute gear + + Parameters + ---------- + m : float + Spur gear modulus + z : int + Number of teeth + b : float + Tooth width + alpha : float + Pressure angle in degrees, industry standard is 20� + helix_angle : float + Helix angle of the helical gear in degrees + If None creates a spur gear, if specified create a helical gear + raw : bool + False : Adds filleting a the root teeth edges + True : Leave the gear with no filleting + + Returns + ------- + cq.Workplane + Returns the gear solid in a cq.Workplane using eachpoint method + """ + + alpha = radians(alpha) + + # radii + r_p = m*z/2 + r_a = r_p + m + r_base = r_p*cos(alpha) + r_f = r_p - 1.25*m + p = pi * m + inv = lambda a: tan(a) - a + + # angles of interest + alpha_inv = inv(alpha) + alpha_tip = acos(r_base/r_a) + alpha_tip_inv = inv(alpha_tip) + + a = 90/z+degrees(alpha_inv) + a2 = 90/z+degrees(alpha_inv)-degrees(alpha_tip_inv) + STOP = sqrt((r_a/r_base)**2 - 1) + # construct all the profiles + left = ( + cq.Workplane() + .transformed(rotate=(0,0,a)) + .parametricCurve(involute(r_base,-1), start=0, stop = STOP, makeWire=False, N=8) + .val() + ) + + right = ( + cq.Workplane() + .transformed(rotate=(0,0,-a)) + .parametricCurve(involute(r_base), start=0, stop = STOP, makeWire=False, N=8) + .val() + ) + + top = cq.Edge.makeCircle(r_a,angle1=-a2, angle2=a2) + + p0 = left.startPoint() + p1 = right.startPoint() + side0 = cq.Workplane(origin=p0.toTuple()).hLine(r_f-r_base).val() + side1 = cq.Workplane(origin=p1.toTuple()).hLine(r_f-r_base).val() + side2 = side0.rotate(cq.Vector(0, 0, 0), cq.Vector(0, 0, 1), -360/z) + + p_bot_left = side1.endPoint() + p_bot_right = rotate_vector_2D(side0.endPoint(), -360/z) + + bottom = cq.Workplane().moveTo(p_bot_left.x, p_bot_left.y).radiusArc(p_bot_right,r_f).val() + + # single tooth profile + profile = cq.Wire.assembleEdges([left,top,right,side1,bottom, side2]) + # return profile + if not raw: + profile = profile.fillet2D(0.25*m, profile.Vertices()[-3:-1]) + # complete gear + gear = ( + cq.Workplane() + .polarArray(0,0,360,z) + .each(lambda loc: profile.located(loc)) + .consolidateWires() + ) + if helix_angle is None: + gear = gear.extrude(b) + else: + gear = gear.twistExtrude(b, helix_angle) + return self.eachpoint(lambda loc: gear.val().located(loc), True) + +def _make_crown_gear(self, m, z, b, alpha = 20, clearance = None): + """ + Create a crown gear (which is the same as a rack gear made circular, also called face gear) + + Parameters + ---------- + m : float + Crown gear modulus + z : int + Number of teeth + b : float + Tooth width + alpha : float + Pressure angle in degrees, industry standard is 20° + clearance : float + The height of the cylinder under the teeth + If None, clearance is equal to 1.7*m + + Returns + ------- + cq.Workplane + Returns the crown gear solid in a cq.Workplane using eachpoint method + """ + r = m*z/2 + if clearance is None: + clearance = 1.7*m + base = cq.Workplane("XY").tag("base").circle(r).extrude(-2.25*m-clearance) + + teeths = cq.Workplane("XY").polarArray(0,0,360,z)._make_crown_gear_tooth_gap(m, r, alpha) + teeths = cq.Compound.makeCompound(teeths.vals()) + gear = base.cut(teeths) + gear = gear.cut(cq.Workplane("XY", origin=(0,0,-2.25*m)).circle(r-b).extrude(2.25*m)) + + return self.eachpoint(lambda loc: gear.val().located(loc), True) + +def _make_rack_gear(self, m, b, length, clearance, alpha = 20, helix_angle = None): + """ + Creates a rack gear + + Parameters + ---------- + m : float + Crown gear modulus + b : float + Tooth width / rack gear width + length : float + Length of the rack gear + alpha : float + Pressure angle in degrees, industry standard is 20° + helix_angle : float + Helix angle in degrees to create a helical rack gear + + Returns + ------- + cq.Workplane + Returns the rack gear solid in a cq.Workplane using eachpoint method + """ + p = pi*m + z = int(length // p)+1 + height = 2.25*m*cos(alpha) + clearance + points = [(p*i,0) for i in range(z)] + [(-p*i,0) for i in range(z)] + teeths = cq.Workplane("XY").pushPoints(points)._make_rack_tooth_gap(m, b, alpha, helix_angle) + base = cq.Workplane("ZY").rect(b, -height, centered=False).extrude(-length) + gear = base.cut(teeths) + return self.eachpoint(lambda loc: gear.val().located(loc), True) + +# Adds the functions to cq.Workplane class, it seems to be needed for cq-editor +def register(): + cq.Workplane.make_gear = _make_gear + cq.Workplane.make_bevel_gear = _make_bevel_gear + cq.Workplane.make_bevel_gear_system = _make_bevel_gear_system + cq.Workplane.make_rack_gear = _make_rack_gear + cq.Workplane.make_crown_gear = _make_crown_gear \ No newline at end of file diff --git a/plugins/gear_generator/images/bevel_gear.PNG b/plugins/gear_generator/images/bevel_gear.PNG new file mode 100644 index 0000000..a475df0 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..12343f8 Binary files /dev/null and b/plugins/gear_generator/images/bevel_gear_system.PNG differ diff --git a/plugins/gear_generator/images/crown_gear.PNG b/plugins/gear_generator/images/crown_gear.PNG new file mode 100644 index 0000000..ae90119 Binary files /dev/null and b/plugins/gear_generator/images/crown_gear.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/rack_gear_helix.PNG b/plugins/gear_generator/images/rack_gear_helix.PNG new file mode 100644 index 0000000..7fee284 Binary files /dev/null and b/plugins/gear_generator/images/rack_gear_helix.PNG differ diff --git a/plugins/gear_generator/images/rack_gear_straight.PNG b/plugins/gear_generator/images/rack_gear_straight.PNG new file mode 100644 index 0000000..406dc0f Binary files /dev/null and b/plugins/gear_generator/images/rack_gear_straight.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..766a2c3 --- /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 methods 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..340cd97 --- /dev/null +++ b/tests/test_gear_generator.py @@ -0,0 +1,106 @@ +import cadquery as cq +import plugins.gear_generator.gear_generator +from unittest import TestCase + +class TestGearGenerator(TestCase): + def test_make_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.8.8 + """ + m = 1.5 + z = 16 + b = 6 + delta = 40 + alpha = 20 + clearance = 6 + gear = cq.Workplane().make_bevel_gear(m, z, b, delta, alpha = alpha, clearance = clearance) + self.assertTrue(gear.val().isValid()) + self.assertAlmostEqual(gear.val().Volume(),2226.028600964595) + + + def test_make_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.8.8 + """ + m = 1 + z1 = 16 + z2 = 22 + b = 2 + alpha = 20 + clearance = 3 + + frozen_gear_system = cq.Workplane().make_bevel_gear_system(m, z1, z2, b, alpha=alpha, clearance = clearance, compound = True) + self.assertTrue(frozen_gear_system.val().isValid()) + self.assertAlmostEqual(frozen_gear_system.val().Volume(),1024.4382489861382) + + gear1, gear2 = cq.Workplane().make_bevel_gear_system(m, z1, z2, b, alpha=alpha, clearance = clearance, compound = False) + self.assertTrue(gear1.val().isValid()) + self.assertAlmostEqual(gear1.val().Volume(),338.4940609342948) + + self.assertTrue(gear2.val().isValid()) + self.assertAlmostEqual(gear2.val().Volume(),685.9422647793748) + + + def test_make_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.8.8 + """ + m = 1.5 + z = 16 + b = 6 + alpha = 20 + clearance = 6 + helix_angle = 40 + + gear1 = cq.Workplane().make_gear(m, z, b, alpha=alpha, raw = False) + self.assertTrue(gear1.val().isValid()) + self.assertAlmostEqual(gear1.val().Volume(),2646.766251684174) + + gear2 = cq.Workplane().make_gear(m, z, b, alpha=alpha, helix_angle = helix_angle, raw = False) + self.assertTrue(gear2.val().isValid()) + self.assertAlmostEqual(gear2.val().Volume(),2646.816968237718) + + + def test_make_crown_gear(self): + """ + Tests if the crown 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.8.8 + """ + m = 1.5 + z = 16 + b = 6 + alpha = 20 + clearance = 6 + + gear = cq.Workplane().make_crown_gear(m, z, b, alpha = alpha, clearance = clearance) + self.assertTrue(gear.val().isValid()) + self.assertAlmostEqual(gear.val().Volume(),3352.5459114045543) + + def test_make_rack_gear(self): + """ + Tests if the rack 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.8.8 + """ + m = 1.5 + z = 16 + b = 6 + length = 40 + alpha = 20 + clearance = 6 + helix_angle = 45 + + gear1 = cq.Workplane().make_rack_gear(m, b, length, clearance, alpha = alpha) + self.assertTrue(gear1.val().isValid()) + self.assertAlmostEqual(gear1.val().Volume(),1381.355198257374) + + gear2 = cq.Workplane().make_rack_gear(m, b, length, clearance, alpha = alpha, helix_angle = helix_angle) + self.assertTrue(gear2.val().isValid()) + self.assertAlmostEqual(gear2.val().Volume(),1369.2236732856904)