Code

Extensions. Gear extension with center hole (see Bug #692719, gear extension should...
[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
22 import simplestyle, sys
23 from math import *
24 import string
26 def involute_intersect_angle(Rb, R):
27     Rb, R = float(Rb), float(R)
28     return (sqrt(R**2 - Rb**2) / (Rb)) - (acos(Rb / R))
30 def point_on_circle(radius, angle):
31     x = radius * cos(angle)
32     y = radius * sin(angle)
33     return (x, y)
35 def points_to_svgd(p):
36     f = p[0]
37     p = p[1:]
38     svgd = 'M%.3f,%.3f' % f
39     for x in p:
40         svgd += 'L%.3f,%.3f' % x
41     svgd += 'z'
42     return svgd
44 class Gears(inkex.Effect):
45     def __init__(self):
46         inkex.Effect.__init__(self)
47         self.OptionParser.add_option("-t", "--teeth",
48                         action="store", type="int",
49                         dest="teeth", default=24,
50                         help="Number of teeth")
51         self.OptionParser.add_option("-p", "--pitch",
52                         action="store", type="float",
53                         dest="pitch", default=20.0,
54                         help="Circular Pitch (length of arc from one tooth to next)")
55         self.OptionParser.add_option("-a", "--angle",
56                         action="store", type="float",
57                         dest="angle", default=20.0,
58                         help="Pressure Angle (common values: 14.5, 20, 25 degrees)")
59         self.OptionParser.add_option("-c", "--centerdiameter",
60                         action="store", type="float",
61                         dest="centerdiameter", default=10.0,
62                         help="Diameter of central hole - 0.0 for no hole")
63         self.OptionParser.add_option("-u", "--unit",
64                         action="store", type="string",
65                         dest="unit", default="px",
66                         help="unit of measure for circular pitch and center diameter")
67     def effect(self):
69         teeth = self.options.teeth
70         pitch = inkex.unittouu( str(self.options.pitch) + self.options.unit)
71         angle = self.options.angle  # Angle of tangent to tooth at circular pitch wrt radial line.
72         centerdiameter = inkex.unittouu( str(self.options.centerdiameter) + self.options.unit)
74         # print >>sys.stderr, "Teeth: %s\n"        % teeth
76         two_pi = 2.0 * pi
78         # Pitch (circular pitch): Length of the arc from one tooth to the next)
79         # Pitch diameter: Diameter of pitch circle.
80         pitch_diameter = float( teeth ) * pitch / pi
81         pitch_radius   = pitch_diameter / 2.0
83         # Base Circle
84         base_diameter = pitch_diameter * cos( radians( angle ) )
85         base_radius   = base_diameter / 2.0
87         # Diametrial pitch: Number of teeth per unit length.
88         pitch_diametrial = float( teeth )/ pitch_diameter
90         # Addendum: Radial distance from pitch circle to outside circle.
91         addendum = 1.0 / pitch_diametrial
93         # Outer Circle
94         outer_radius = pitch_radius + addendum
95         outer_diameter = outer_radius * 2.0
97         # Tooth thickness: Tooth width along pitch circle.
98         tooth  = ( pi * pitch_diameter ) / ( 2.0 * float( teeth ) )
100         # Undercut?
101         undercut = (2.0 / ( sin( radians( angle ) ) ** 2))
102         needs_undercut = teeth < undercut
105         # Clearance: Radial distance between top of tooth on one gear to bottom of gap on another.
106         clearance = 0.0
108         # Dedendum: Radial distance from pitch circle to root diameter.
109         dedendum = addendum + clearance
111         # Root diameter: Diameter of bottom of tooth spaces. 
112         root_radius =  pitch_radius - dedendum
113         root_diameter = root_radius * 2.0
115         half_thick_angle = two_pi / (4.0 * float( teeth ) )
116         pitch_to_base_angle  = involute_intersect_angle( base_radius, pitch_radius )
117         pitch_to_outer_angle = involute_intersect_angle( base_radius, outer_radius ) - pitch_to_base_angle
119         centers = [(x * two_pi / float( teeth) ) for x in range( teeth ) ]
121         points = []
123         for c in centers:
125             # Angles
126             pitch1 = c - half_thick_angle
127             base1  = pitch1 - pitch_to_base_angle
128             outer1 = pitch1 + pitch_to_outer_angle
130             pitch2 = c + half_thick_angle
131             base2  = pitch2 + pitch_to_base_angle
132             outer2 = pitch2 - pitch_to_outer_angle
134             # Points
135             b1 = point_on_circle( base_radius,  base1  )
136             p1 = point_on_circle( pitch_radius, pitch1 )
137             o1 = point_on_circle( outer_radius, outer1 )
139             b2 = point_on_circle( base_radius,  base2  )
140             p2 = point_on_circle( pitch_radius, pitch2 )
141             o2 = point_on_circle( outer_radius, outer2 )
143             if root_radius > base_radius:
144                 pitch_to_root_angle = pitch_to_base_angle - involute_intersect_angle(base_radius, root_radius )
145                 root1 = pitch1 - pitch_to_root_angle
146                 root2 = pitch2 + pitch_to_root_angle
147                 r1 = point_on_circle(root_radius, root1)
148                 r2 = point_on_circle(root_radius, root2)
149                 p_tmp = [r1,p1,o1,o2,p2,r2]
150             else:
151                 r1 = point_on_circle(root_radius, base1)
152                 r2 = point_on_circle(root_radius, base2)
153                 p_tmp = [r1,b1,p1,o1,o2,p2,b2,r2]
155             points.extend( p_tmp )
157         path = points_to_svgd( points )
159         # Embed gear in group to make animation easier:
160         #  Translate group, Rotate path.
161         t = 'translate(' + str( self.view_center[0] ) + ',' + str( self.view_center[1] ) + ')'
162         g_attribs = {inkex.addNS('label','inkscape'):'Gear' + str( teeth ),
163                      'transform':t }
164         g = inkex.etree.SubElement(self.current_layer, 'g', g_attribs)
166         # Create SVG Path for gear
167         style = { 'stroke': '#000000', 'fill': 'none' }
168         gear_attribs = {'style':simplestyle.formatStyle(style), 'd':path}
169         gear = inkex.etree.SubElement(g, inkex.addNS('path','svg'), gear_attribs )
170         if(centerdiameter > 0.0):
171             center_attribs = {'style':simplestyle.formatStyle(style), 
172                 inkex.addNS('cx','sodipodi')        :'0.0',
173                 inkex.addNS('cy','sodipodi')        :'0.0',
174                 inkex.addNS('rx','sodipodi')        :str(centerdiameter/2),
175                 inkex.addNS('ry','sodipodi')        :str(centerdiameter/2),
176                 inkex.addNS('type','sodipodi')      :'arc'
177             }
178             center = inkex.etree.SubElement(g, inkex.addNS('path','svg'), center_attribs )
180 if __name__ == '__main__':
181     e = Gears()
182     e.affect()
185 # vim: expandtab shiftwidth=4 tabstop=8 softtabstop=4 fileencoding=utf-8 textwidth=99