-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathgear.py
120 lines (90 loc) · 3.83 KB
/
gear.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
import sys
import numpy
import argparse
import itertools
import backends.dxf
import backends.text
from shapely.ops import cascaded_union
from shapely.geometry import Point, MultiPoint, Polygon, box
from shapely.affinity import rotate, scale, translate
def rot_matrix(x):
c, s = numpy.cos(x), numpy.sin(x)
return numpy.array([[c, -s], [s, c]])
def rotation(X, angle, center = None):
if center is None:
return numpy.dot(X, rot_matrix(angle))
else:
return numpy.dot(X - center, rot_matrix(angle)) + center
def deg2rad(x):
return (numpy.pi / 180) * x
def generate(teeth_count = 8,
tooth_width = 1.,
pressure_angle = deg2rad(20.),
backlash = 0.,
frame_count = 16):
tooth_width -= backlash
pitch_circumference = tooth_width * 2 * teeth_count
pitch_radius = pitch_circumference / (2 * numpy.pi)
addendum = tooth_width * (2 / numpy.pi)
dedendum = addendum
outer_radius = pitch_radius + addendum
print pitch_radius - dedendum
# Tooth profile
profile = numpy.array([
[-(.5 * tooth_width + addendum * numpy.tan(pressure_angle)), addendum],
[-(.5 * tooth_width - dedendum * numpy.tan(pressure_angle)), -dedendum],
[ (.5 * tooth_width - dedendum * numpy.tan(pressure_angle)), -dedendum],
[ (.5 * tooth_width + addendum * numpy.tan(pressure_angle)) , addendum]
])
outer_circle = Point(0., 0.).buffer(outer_radius)
poly_list = []
prev_X = None
l = 2 * tooth_width / pitch_radius
for theta in numpy.linspace(0, l, frame_count):
X = rotation(profile + numpy.array((-theta * pitch_radius, pitch_radius)), theta)
if prev_X is not None:
poly_list.append(MultiPoint([x for x in X] + [x for x in prev_X]).convex_hull)
prev_X = X
def circle_sector(angle, r):
box_a = rotate(box(0., -2 * r, 2 * r, 2 * r), -angle / 2, Point(0., 0.))
box_b = rotate(box(-2 * r, -2 * r, 0, 2 * r), angle / 2, Point(0., 0.))
return Point(0., 0.).buffer(r).difference(box_a.union(box_b))
# Generate a tooth profile
tooth_poly = cascaded_union(poly_list)
tooth_poly = tooth_poly.union(scale(tooth_poly, -1, 1, 1, Point(0., 0.)))
# Generate the full gear
gear_poly = Point(0., 0.).buffer(outer_radius)
for i in range(0, teeth_count):
gear_poly = rotate(gear_poly.difference(tooth_poly), (2 * numpy.pi) / teeth_count, Point(0., 0.), use_radians = True)
# Job done
return gear_poly, pitch_radius
def main():
# Command line parsing
parser = argparse.ArgumentParser(description = 'Generate 2d spur gears profiles')
parser.add_argument('-c', '--teeth-count', type = int, default = 17, help = 'Teeth count')
parser.add_argument('-w', '--tooth-width', type = float, default = 10., help = 'Tooth width')
parser.add_argument('-p', '--pressure-angle', type = float, default = 20., help = 'Pressure angle in degrees')
parser.add_argument('-n', '--frame-count', type = int, default = 16, help = 'Number of frames used to build the involute')
parser.add_argument('-b', '--backlash', type = float, default = 0.2, help = 'Backlash')
parser.add_argument('-t', '--output-type', choices = ['dxf', 'text'], default = 'dxf', help = 'Output type')
parser.add_argument('-o', '--output-path', default = 'out', help = 'Output file')
args = parser.parse_args()
# Input parameters safety checks
if args.teeth_count <= 0:
sys.stderr.write('Invalid teeth count\n')
sys.exit(1)
# Generate the shape
poly, pitch_radius = generate(args.teeth_count,
args.tooth_width,
deg2rad(args.pressure_angle),
args.backlash,
args.frame_count)
# Write the shape to the output
print 'pitch radius =', pitch_radius
with open(args.output_path, 'w') as f:
if args.output_type == 'dxf':
backends.dxf.write(f, poly)
elif args.output_type == 'text':
backends.text.write(f, poly)
if __name__ == '__main__':
main()