1 #define INKSCAPE_LPE_DOEFFECT_STACK_CPP\r
2 \r
3 /*\r
4 * Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl>\r
5 *\r
6 * Released under GNU GPL, read the file 'COPYING' for more information\r
7 */\r
8 \r
9 #include "live_effects/lpe-gears.h"\r
10 \r
11 #include <vector>\r
12 #include <2geom/d2.h>\r
13 #include <2geom/sbasis.h>\r
14 #include <2geom/bezier-to-sbasis.h>\r
15 #include <2geom/path.h>\r
16 \r
17 using std::vector;\r
18 using namespace Geom;\r
19 \r
20 class Gear {\r
21 public:\r
22 // pitch circles touch on two properly meshed gears\r
23 // all measurements are taken from the pitch circle\r
24 double pitch_diameter() {return (_number_of_teeth * _module) / M_PI;}\r
25 double pitch_radius() {return pitch_diameter() / 2.0;}\r
26 void pitch_radius(double R) {_module = (2 * M_PI * R) / _number_of_teeth;}\r
27 \r
28 // base circle serves as the basis for the involute toothe profile\r
29 double base_diameter() {return pitch_diameter() * cos(_pressure_angle);}\r
30 double base_radius() {return base_diameter() / 2.0;}\r
31 \r
32 // diametrical pitch\r
33 double diametrical_pitch() {return _number_of_teeth / pitch_diameter();}\r
34 \r
35 // height of the tooth above the pitch circle\r
36 double addendum() {return 1.0 / diametrical_pitch();}\r
37 // depth of the tooth below the pitch circle\r
38 double dedendum() {return addendum() + _clearance;}\r
39 \r
40 // root circle specifies the bottom of the fillet between teeth\r
41 double root_radius() {return pitch_radius() - dedendum();}\r
42 double root_diameter() {return root_radius() * 2.0;}\r
43 \r
44 // outer circle is the outside diameter of the gear\r
45 double outer_radius() {return pitch_radius() + addendum();}\r
46 double outer_diameter() {return outer_radius() * 2.0;}\r
47 \r
48 // angle covered by the tooth on the pitch circle\r
49 double tooth_thickness_angle() {return M_PI / _number_of_teeth;}\r
50 \r
51 Geom::Point centre() {return _centre;}\r
52 void centre(Geom::Point c) {_centre = c;}\r
53 \r
54 double angle() {return _angle;}\r
55 void angle(double a) {_angle = a;}\r
56 \r
57 int number_of_teeth() {return _number_of_teeth;}\r
58 \r
59 Geom::Path path();\r
60 Gear spawn(Geom::Point p);\r
61 \r
62 Gear(int n, double m, double phi) {\r
63 _number_of_teeth = n;\r
64 _module = m;\r
65 _pressure_angle = phi;\r
66 _clearance = 0.0;\r
67 _angle = 0.0;\r
68 _centre = Geom::Point(0.0,0.0);\r
69 }\r
70 private:\r
71 int _number_of_teeth;\r
72 double _pressure_angle;\r
73 double _module;\r
74 double _clearance;\r
75 double _angle;\r
76 Geom::Point _centre;\r
77 D2<SBasis> _involute(double start, double stop) {\r
78 D2<SBasis> B;\r
79 D2<SBasis> I;\r
80 Linear bo = Linear(start,stop);\r
81 \r
82 B[0] = cos(bo,2);\r
83 B[1] = sin(bo,2);\r
84 \r
85 I = B - Linear(0,1) * derivative(B);\r
86 I = I*base_radius() + _centre;\r
87 return I;\r
88 }\r
89 D2<SBasis> _arc(double start, double stop, double R) {\r
90 D2<SBasis> B;\r
91 Linear bo = Linear(start,stop);\r
92 \r
93 B[0] = cos(bo,2);\r
94 B[1] = sin(bo,2);\r
95 \r
96 B = B*R + _centre;\r
97 return B;\r
98 }\r
99 // angle of the base circle used to create the involute to a certain radius\r
100 double involute_swath_angle(double R) {\r
101 if (R <= base_radius()) return 0.0;\r
102 return sqrt(R*R - base_radius()*base_radius())/base_radius();\r
103 }\r
104 \r
105 // angle of the base circle between the origin of the involute and the intersection on another radius\r
106 double involute_intersect_angle(double R) {\r
107 if (R <= base_radius()) return 0.0;\r
108 return (sqrt(R*R - base_radius()*base_radius())/base_radius()) - acos(base_radius()/R);\r
109 }\r
110 };\r
111 \r
112 void makeContinuous(D2<SBasis> &a, Point const b) {\r
113 for(unsigned d=0;d<2;d++)\r
114 a[d][0][0] = b[d];\r
115 }\r
116 \r
117 Geom::Path Gear::path() {\r
118 Geom::Path pb;\r
119 \r
120 // angle covered by a full tooth and fillet\r
121 double tooth_rotation = 2.0 * tooth_thickness_angle();\r
122 // angle covered by an involute\r
123 double involute_advance = involute_intersect_angle(outer_radius()) - involute_intersect_angle(root_radius());\r
124 // angle covered by the tooth tip\r
125 double tip_advance = tooth_thickness_angle() - (2 * (involute_intersect_angle(outer_radius()) - involute_intersect_angle(pitch_radius())));\r
126 // angle covered by the toothe root\r
127 double root_advance = (tooth_rotation - tip_advance) - (2.0 * involute_advance);\r
128 // begin drawing the involute at t if the root circle is larger than the base circle\r
129 double involute_t = involute_swath_angle(root_radius())/involute_swath_angle(outer_radius());\r
130 \r
131 //rewind angle to start drawing from the leading edge of the tooth\r
132 double first_tooth_angle = _angle - ((0.5 * tip_advance) + involute_advance);\r
133 \r
134 Geom::Point prev;\r
135 for (int i=0; i < _number_of_teeth; i++)\r
136 {\r
137 double cursor = first_tooth_angle + (i * tooth_rotation);\r
138 \r
139 D2<SBasis> leading_I = compose(_involute(cursor, cursor + involute_swath_angle(outer_radius())), Linear(involute_t,1));\r
140 if(i != 0) makeContinuous(leading_I, prev);\r
141 pb.append(SBasisCurve(leading_I));\r
142 cursor += involute_advance;\r
143 prev = leading_I.at1();\r
144 \r
145 D2<SBasis> tip = _arc(cursor, cursor+tip_advance, outer_radius());\r
146 makeContinuous(tip, prev);\r
147 pb.append(SBasisCurve(tip));\r
148 cursor += tip_advance;\r
149 prev = tip.at1();\r
150 \r
151 cursor += involute_advance;\r
152 D2<SBasis> trailing_I = compose(_involute(cursor, cursor - involute_swath_angle(outer_radius())), Linear(1,involute_t));\r
153 makeContinuous(trailing_I, prev);\r
154 pb.append(SBasisCurve(trailing_I));\r
155 prev = trailing_I.at1();\r
156 \r
157 if (base_radius() > root_radius()) {\r
158 Geom::Point leading_start = trailing_I.at1();\r
159 Geom::Point leading_end = (root_radius() * unit_vector(leading_start - _centre)) + _centre;\r
160 prev = leading_end;\r
161 pb.appendNew<LineSegment>(leading_end);\r
162 }\r
163 \r
164 D2<SBasis> root = _arc(cursor, cursor+root_advance, root_radius());\r
165 makeContinuous(root, prev);\r
166 pb.append(SBasisCurve(root));\r
167 cursor += root_advance;\r
168 prev = root.at1();\r
169 \r
170 if (base_radius() > root_radius()) {\r
171 Geom::Point trailing_start = root.at1();\r
172 Geom::Point trailing_end = (base_radius() * unit_vector(trailing_start - _centre)) + _centre;\r
173 pb.appendNew<LineSegment>(trailing_end);\r
174 prev = trailing_end;\r
175 }\r
176 }\r
177 \r
178 return pb;\r
179 }\r
180 \r
181 Gear Gear::spawn(Geom::Point p) {\r
182 double radius = Geom::distance(this->centre(), p) - this->pitch_radius();\r
183 int N = (int) floor( (radius / this->pitch_radius()) * this->number_of_teeth() );\r
184 \r
185 Gear gear(N, _module, _pressure_angle);\r
186 gear.centre(p);\r
187 \r
188 double a = atan2(p - this->centre());\r
189 double new_angle = 0.0;\r
190 if (gear.number_of_teeth() % 2 == 0)\r
191 new_angle -= gear.tooth_thickness_angle();\r
192 new_angle -= (_angle) * (pitch_radius() / gear.pitch_radius());\r
193 new_angle += (a) * (pitch_radius() / gear.pitch_radius());\r
194 gear.angle(new_angle + a);\r
195 return gear;\r
196 }\r
197 \r
198 \r
199 \r
200 // #################################################################\r
201 \r
202 \r
203 \r
204 namespace Inkscape {\r
205 namespace LivePathEffect {\r
206 \r
207 \r
208 LPEGears::LPEGears(LivePathEffectObject *lpeobject) :\r
209 Effect(lpeobject),\r
210 teeth(_("Teeth"), _("The number of teeth"), "teeth", &wr, this, 10),\r
211 phi(_("Phi"), _("???"), "phi", &wr, this, 5)\r
212 {\r
213 registerParameter( dynamic_cast<Parameter *>(&teeth) );\r
214 registerParameter( dynamic_cast<Parameter *>(&phi) );\r
215 }\r
216 \r
217 LPEGears::~LPEGears()\r
218 {\r
219 \r
220 }\r
221 \r
222 std::vector<Geom::Path>\r
223 LPEGears::doEffect (std::vector<Geom::Path> & path_in)\r
224 {\r
225 std::vector<Geom::Path> path_out;\r
226 Geom::Path gearpath = path_in[0];\r
227 \r
228 Geom::Path::iterator it(gearpath.begin());\r
229 if ( it == gearpath.end() ) return path_out;\r
230 \r
231 Gear * gear = new Gear(teeth, 200.0, phi * M_PI / 180);\r
232 Geom::Point gear_centre = (*it).finalPoint();\r
233 gear->centre(gear_centre);\r
234 gear->angle(atan2((*it).initialPoint() - gear_centre));\r
235 \r
236 it++; if ( it == gearpath.end() ) return path_out;\r
237 gear->pitch_radius(Geom::distance(gear_centre, (*it).finalPoint()));\r
238 \r
239 path_out.push_back( gear->path());\r
240 \r
241 for (it++ ; it != gearpath.end() ; it++) {\r
242 // iterate through Geom::Curve in path_in\r
243 Gear* gearnew = new Gear(gear->spawn( (*it).finalPoint() ));\r
244 path_out.push_back( gearnew->path() );\r
245 delete gear;\r
246 gear = gearnew;\r
247 }\r
248 delete gear;\r
249 \r
250 return path_out;\r
251 }\r
252 \r
253 \r
254 } // namespace LivePathEffect\r
255 } /* namespace Inkscape */\r
256 \r
257 /*\r
258 Local Variables:\r
259 mode:c++\r
260 c-file-style:"stroustrup"\r
261 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))\r
262 indent-tabs-mode:nil\r
263 fill-column:99\r
264 End:\r
265 */\r
266 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :\r