Code

Mnemonics in "Input devices", and LPE dialogs (Bug 170765)
[inkscape.git] / src / live_effects / lpe-gears.cpp
1 #define INKSCAPE_LPE_DOEFFECT_STACK_CPP
3 /*
4  * Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl>
5  * Copyright 2006 Michael G. Sloan <mgsloan@gmail.com>
6  * Copyright 2006 Aaron Spike <aaron@ekips.org>
7  *
8  * Released under GNU GPL, read the file 'COPYING' for more information
9  */
11 #include "live_effects/lpe-gears.h"
13 #include <vector>
14 #include <2geom/d2.h>
15 #include <2geom/sbasis.h>
16 #include <2geom/bezier-to-sbasis.h>
17 #include <2geom/path.h>
19 using std::vector;
20 using namespace Geom;
22 class Gear {
23 public:
24     // pitch circles touch on two properly meshed gears
25     // all measurements are taken from the pitch circle
26     double pitch_diameter() {return (_number_of_teeth * _module) / M_PI;}
27     double pitch_radius() {return pitch_diameter() / 2.0;}
28     void pitch_radius(double R) {_module = (2 * M_PI * R) / _number_of_teeth;}
30     // base circle serves as the basis for the involute toothe profile
31     double base_diameter() {return pitch_diameter() * cos(_pressure_angle);}
32     double base_radius() {return base_diameter() / 2.0;}
34     // diametrical pitch
35     double diametrical_pitch() {return _number_of_teeth / pitch_diameter();}
37     // height of the tooth above the pitch circle
38     double addendum() {return 1.0 / diametrical_pitch();}
39     // depth of the tooth below the pitch circle
40     double dedendum() {return addendum() + _clearance;}
42     // root circle specifies the bottom of the fillet between teeth
43     double root_radius() {return pitch_radius() - dedendum();}
44     double root_diameter() {return root_radius() * 2.0;}
46     // outer circle is the outside diameter of the gear
47     double outer_radius() {return pitch_radius() + addendum();}
48     double outer_diameter() {return outer_radius() * 2.0;}
50     // angle covered by the tooth on the pitch circle
51     double tooth_thickness_angle() {return M_PI / _number_of_teeth;}
53     Geom::Point centre() {return _centre;}
54     void centre(Geom::Point c) {_centre = c;}
56     double angle() {return _angle;}
57     void angle(double a) {_angle = a;}
59     int number_of_teeth() {return _number_of_teeth;}
61     Geom::Path path();
62     Gear spawn(Geom::Point p);
64     Gear(int n, double m, double phi) {
65         _number_of_teeth = n;
66         _module = m;
67         _pressure_angle = phi;
68         _clearance = 0.0;
69         _angle = 0.0;
70         _centre = Geom::Point(0.0,0.0);
71     }
72 private:
73     int _number_of_teeth;
74     double _pressure_angle;
75     double _module;
76     double _clearance;
77     double _angle;
78     Geom::Point _centre;
79     D2<SBasis> _involute(double start, double stop) {
80         D2<SBasis> B;
81         D2<SBasis> I;
82         Linear bo = Linear(start,stop);
84         B[0] = cos(bo,2);
85         B[1] = sin(bo,2);
87         I = B - Linear(0,1) * derivative(B);
88         I = I*base_radius() + _centre;
89         return I;
90     }
91     D2<SBasis> _arc(double start, double stop, double R) {
92         D2<SBasis> B;
93         Linear bo = Linear(start,stop);
95         B[0] = cos(bo,2);
96         B[1] = sin(bo,2);
98         B = B*R + _centre;
99         return B;
100     }
101     // angle of the base circle used to create the involute to a certain radius
102     double involute_swath_angle(double R) {
103         if (R <= base_radius()) return 0.0;
104         return sqrt(R*R - base_radius()*base_radius())/base_radius();
105     }
107     // angle of the base circle between the origin of the involute and the intersection on another radius
108     double involute_intersect_angle(double R) {
109         if (R <= base_radius()) return 0.0;
110         return (sqrt(R*R - base_radius()*base_radius())/base_radius()) - acos(base_radius()/R);
111     }
112 };
114 void makeContinuous(D2<SBasis> &a, Point const b) {
115     for(unsigned d=0;d<2;d++)
116         a[d][0][0] = b[d];
119 Geom::Path Gear::path() {
120     Geom::Path pb;
122     // angle covered by a full tooth and fillet
123     double tooth_rotation = 2.0 * tooth_thickness_angle();
124     // angle covered by an involute
125     double involute_advance = involute_intersect_angle(outer_radius()) - involute_intersect_angle(root_radius());
126     // angle covered by the tooth tip
127     double tip_advance = tooth_thickness_angle() - (2 * (involute_intersect_angle(outer_radius()) - involute_intersect_angle(pitch_radius())));
128     // angle covered by the toothe root
129     double root_advance = (tooth_rotation - tip_advance) - (2.0 * involute_advance);
130     // begin drawing the involute at t if the root circle is larger than the base circle
131     double involute_t = involute_swath_angle(root_radius())/involute_swath_angle(outer_radius());
133     //rewind angle to start drawing from the leading edge of the tooth
134     double first_tooth_angle = _angle - ((0.5 * tip_advance) + involute_advance);
136     Geom::Point prev;
137     for (int i=0; i < _number_of_teeth; i++)
138     {
139         double cursor = first_tooth_angle + (i * tooth_rotation);
141         D2<SBasis> leading_I = compose(_involute(cursor, cursor + involute_swath_angle(outer_radius())), Linear(involute_t,1));
142         if(i != 0) makeContinuous(leading_I, prev);
143         pb.append(SBasisCurve(leading_I));
144         cursor += involute_advance;
145         prev = leading_I.at1();
147         D2<SBasis> tip = _arc(cursor, cursor+tip_advance, outer_radius());
148         makeContinuous(tip, prev);
149         pb.append(SBasisCurve(tip));
150         cursor += tip_advance;
151         prev = tip.at1();
153         cursor += involute_advance;
154         D2<SBasis> trailing_I = compose(_involute(cursor, cursor - involute_swath_angle(outer_radius())), Linear(1,involute_t));
155         makeContinuous(trailing_I, prev);
156         pb.append(SBasisCurve(trailing_I));
157         prev = trailing_I.at1();
159         if (base_radius() > root_radius()) {
160             Geom::Point leading_start = trailing_I.at1();
161             Geom::Point leading_end = (root_radius() * unit_vector(leading_start - _centre)) + _centre;
162             prev = leading_end;
163             pb.appendNew<LineSegment>(leading_end);
164         }
166         D2<SBasis> root = _arc(cursor, cursor+root_advance, root_radius());
167         makeContinuous(root, prev);
168         pb.append(SBasisCurve(root));
169         cursor += root_advance;
170         prev = root.at1();
172         if (base_radius() > root_radius()) {
173             Geom::Point trailing_start = root.at1();
174             Geom::Point trailing_end = (base_radius() * unit_vector(trailing_start - _centre)) + _centre;
175             pb.appendNew<LineSegment>(trailing_end);
176             prev = trailing_end;
177         }
178     }
180     return pb;
183 Gear Gear::spawn(Geom::Point p) {
184     double radius = Geom::distance(this->centre(), p) - this->pitch_radius();
185     int N  = (int) floor( (radius / this->pitch_radius()) * this->number_of_teeth() );
187     Gear gear(N, _module, _pressure_angle);
188     gear.centre(p);
190     double a = atan2(p - this->centre());
191     double new_angle = 0.0;
192     if (gear.number_of_teeth() % 2 == 0)
193         new_angle -= gear.tooth_thickness_angle();
194     new_angle -= (_angle) * (pitch_radius() / gear.pitch_radius());
195     new_angle += (a) * (pitch_radius() / gear.pitch_radius());
196     gear.angle(new_angle + a);
197     return gear;
202 // #################################################################
206 namespace Inkscape {
207 namespace LivePathEffect {
210 LPEGears::LPEGears(LivePathEffectObject *lpeobject) :
211     Effect(lpeobject),
212     teeth(_("_Teeth:"), _("The number of teeth"), "teeth", &wr, this, 10),
213     phi(_("_Phi:"), _("Tooth pressure angle (typically 20-25 deg).  The ratio of teeth not in contact."), "phi", &wr, this, 5)
215     /* Tooth pressure angle: The angle between the tooth profile and a perpendicular to the pitch
216      * circle, usually at the point where the pitch circle meets the tooth profile. Standard angles
217      * are 20 and 25 degrees. The pressure angle affects the force that tends to separate mating
218      * gears. A high pressure angle means that higher ratio of teeth not in contact. However, this
219      * allows the teeth to have higher capacity and also allows fewer teeth without undercutting.
220      */
222     teeth.param_make_integer();
223     teeth.param_set_range(3, 1e10);
224     registerParameter( dynamic_cast<Parameter *>(&teeth) );
225     registerParameter( dynamic_cast<Parameter *>(&phi) );
228 LPEGears::~LPEGears()
233 std::vector<Geom::Path>
234 LPEGears::doEffect_path (std::vector<Geom::Path> const & path_in)
236     std::vector<Geom::Path> path_out;
237     Geom::Path gearpath = path_in[0];
239     Geom::Path::iterator it(gearpath.begin());
240     if ( it == gearpath.end() ) return path_out;
242     Gear * gear = new Gear(teeth, 200.0, phi * M_PI / 180);
243     Geom::Point gear_centre = (*it).finalPoint();
244     gear->centre(gear_centre);
245     gear->angle(atan2((*it).initialPoint() - gear_centre));
247     it++; if ( it == gearpath.end() ) return path_out;
248     gear->pitch_radius(Geom::distance(gear_centre, (*it).finalPoint()));
250     path_out.push_back( gear->path());
252     for (it++ ; it != gearpath.end() ; it++) {
253         // iterate through Geom::Curve in path_in
254         Gear* gearnew = new Gear(gear->spawn( (*it).finalPoint() ));
255         path_out.push_back( gearnew->path() );
256         delete gear;
257         gear = gearnew;
258     }
259     delete gear;
261     return path_out;
264 } // namespace LivePathEffect
265 } /* namespace Inkscape */
267 /*
268   Local Variables:
269   mode:c++
270   c-file-style:"stroustrup"
271   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
272   indent-tabs-mode:nil
273   fill-column:99
274   End:
275 */
276 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :