Code

add gears
[inkscape.git] / share / extensions / gears.py
1 #!/usr/bin/env python 
2 '''
3 Copyright (C) 2007 Aaron Spike  (aaron @ ekips.org)
4 Copyright (C) 2007 Tavmjong Bah (tavmjong @ free.fr)
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19 '''
21 import inkex, simplestyle, sys
22 from math import *
24 def involute_intersect_angle(Rb, R):
25     Rb, R = float(Rb), float(R)
26     return (sqrt(R**2 - Rb**2) / (Rb)) - (acos(Rb / R))
28 def point_on_circle(radius, angle):
29     x = radius * cos(angle)
30     y = radius * sin(angle)
31     return (x, y)
33 def points_to_svgd(p):
34     f = p[0]
35     p = p[1:]
36     svgd = 'M%.3f,%.3f' % f
37     for x in p:
38         svgd += 'L%.3f,%.3f' % x
39     svgd += 'z'
40     return svgd
42 class Gears(inkex.Effect):
43     def __init__(self):
44         inkex.Effect.__init__(self)
45         self.OptionParser.add_option("-t", "--teeth",
46                         action="store", type="int", 
47                         dest="teeth", default=24,
48                         help="Number of teeth")
49         self.OptionParser.add_option("-p", "--pitch",
50                         action="store", type="float", 
51                         dest="pitch", default=20.0,
52                         help="Circular Pitch (length of arc from one tooth to next)")
53         self.OptionParser.add_option("-a", "--angle",
54                         action="store", type="float", 
55                         dest="angle", default=20.0,
56                         help="Pressure Angle (common values: 14.5, 20, 25 degrees)")
57     def effect(self):
58     
59         teeth = self.options.teeth
60         pitch = self.options.pitch
61         angle = self.options.angle  # Angle of tangent to tooth at circular pitch wrt radial line.
63         # print >>sys.stderr, "Teeth: %s\n"        % teeth
65         two_pi = 2.0 * pi
66         
67         # Pitch (circular pitch): Length of the arc from one tooth to the next)
68         # Pitch diameter: Diameter of pitch circle.
69         pitch_diameter = float( teeth ) * pitch / pi
70         pitch_radius   = pitch_diameter / 2.0
71        
72         # Base Circle
73         base_diameter = pitch_diameter * cos( radians( angle ) )
74         base_radius   = base_diameter / 2.0
75     
76         # Diametrial pitch: Number of teeth per unit length.
77         pitch_diametrial = float( teeth )/ pitch_diameter
79         # Addendum: Radial distance from pitch circle to outside circle.
80         addendum = 1.0 / pitch_diametrial
81     
82         # Outer Circle
83         outer_radius = pitch_radius + addendum
84         outer_diameter = outer_radius * 2.0
85     
86         # Tooth thickness: Tooth width along pitch circle.
87         tooth  = ( pi * pitch_diameter ) / ( 2.0 * float( teeth ) )
88     
89         # Undercut?
90         undercut = (2.0 / ( sin( radians( angle ) ) ** 2))
91         needs_undercut = teeth < undercut
93     
94         # Clearance: Radial distance between top of tooth on one gear to bottom of gap on another.
95         clearance = 0.0
97         # Dedendum: Radial distance from pitch circle to root diameter.
98         dedendum = addendum + clearance
99     
100         # Root diameter: Diameter of bottom of tooth spaces. 
101         root_radius =  pitch_radius - dedendum
102         root_diameter = root_radius * 2.0
103     
104         half_thick_angle = two_pi / (4.0 * float( teeth ) )
105         pitch_to_base_angle  = involute_intersect_angle( base_radius, pitch_radius )
106         pitch_to_outer_angle = involute_intersect_angle( base_radius, outer_radius ) - pitch_to_base_angle
107     
108         centers = [(x * two_pi / float( teeth) ) for x in range( teeth ) ]
109     
110         points = []
112         for c in centers:
114              # Angles
115              pitch1 = c - half_thick_angle
116              base1  = pitch1 - pitch_to_base_angle
117              outer1 = pitch1 + pitch_to_outer_angle
118         
119              pitch2 = c + half_thick_angle
120              base2  = pitch2 + pitch_to_base_angle
121              outer2 = pitch2 - pitch_to_outer_angle
122         
123              # Points
124              b1 = point_on_circle( base_radius,  base1  )
125              p1 = point_on_circle( pitch_radius, pitch1 )
126              o1 = point_on_circle( outer_radius, outer1 )
128              b2 = point_on_circle( base_radius,  base2  )
129              p2 = point_on_circle( pitch_radius, pitch2 )
130              o2 = point_on_circle( outer_radius, outer2 )
132              if root_radius > base_radius:
133                  pitch_to_root_angle = pitch_to_base_angle - involute_intersect_angle(base_radius, root_radius )
134                  root1 = pitch1 - pitch_to_root_angle
135                  root2 = pitch2 + pitch_to_root_angle
136                  r1 = point_on_circle(root_radius, root1)
137                  r2 = point_on_circle(root_radius, root2)
138                  p_tmp = [r1,p1,o1,o2,p2,r2]
139              else:
140                  r1 = point_on_circle(root_radius, base1)
141                  r2 = point_on_circle(root_radius, base2)
142                  p_tmp = [r1,b1,p1,o1,o2,p2,b2,r2]
143             
144              points.extend( p_tmp )
146         path = points_to_svgd( points )
148         # Create SVG Path for gear
149         gear = self.document.createElement( 'svg:path' )
150         style = { 'stroke': '#000000', 'fill': 'none' }
151         gear.setAttribute( 'style', simplestyle.formatStyle(style) )
152         gear.setAttribute( 'd', path )
154         # Embed gear in group to make animation easier:
155         #  Translate group, Rotate path.
156         g=self.document.createElement('g')
158         g.setAttribute( 'inkscape:label', 'Gear' + str( teeth ) )
159         t = 'translate(' + str( self.view_center[0] ) + ',' + str( self.view_center[1] ) + ')'
160         g.setAttribute( 'transform', t )
161         self.current_layer.appendChild( g )
162         g.appendChild( gear )
164 e = Gears()
165 e.affect()