Code

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