Code

fix compositing for premultiplication and non-alpha cases
[inkscape.git] / src / live_effects / lpe-gears.cpp
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