Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
lpe-gears.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl>
4 * Copyright 2006 Michael G. Sloan <mgsloan@gmail.com>
5 * Copyright 2006 Aaron Spike <aaron@ekips.org>
6 *
7 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
8 */
9
12// TODO due to internal breakage in glibmm headers, this must be last:
13#include <glibmm/i18n.h>
14
15using namespace Geom;
16
17class Gear {
18public:
19 // pitch circles touch on two properly meshed gears
20 // all measurements are taken from the pitch circle
21 double pitch_diameter() {return (_number_of_teeth * _module) / M_PI;}
22 double pitch_radius() {return pitch_diameter() / 2.0;}
23 void pitch_radius(double R) {_module = (2 * M_PI * R) / _number_of_teeth;}
24
25 // base circle serves as the basis for the involute toothe profile
26 double base_diameter() {return pitch_diameter() * cos(_pressure_angle);}
27 double base_radius() {return base_diameter() / 2.0;}
28
29 // diametrical pitch
30 double diametrical_pitch() {return _number_of_teeth / pitch_diameter();}
31
32 // height of the tooth above the pitch circle
33 double addendum() {return 1.0 / diametrical_pitch();}
34 // depth of the tooth below the pitch circle
35 double dedendum() {return addendum() + _clearance;}
36
37 // root circle specifies the bottom of the fillet between teeth
38 double root_radius() {return pitch_radius() - dedendum();}
39 double root_diameter() {return root_radius() * 2.0;}
40
41 // outer circle is the outside diameter of the gear
42 double outer_radius() {return pitch_radius() + addendum();}
43 double outer_diameter() {return outer_radius() * 2.0;}
44
45 // angle covered by the tooth on the pitch circle
46 double tooth_thickness_angle() {return M_PI / _number_of_teeth;}
47
48 Geom::Point centre() {return _centre;}
49 void centre(Geom::Point c) {_centre = c;}
50
51 double angle() {return _angle;}
52 void angle(double a) {_angle = a;}
53
54 int number_of_teeth() {return _number_of_teeth;}
55
56 Geom::Path path();
57 Gear spawn(Geom::Point p);
58
59 Gear(int n, double m, double phi)
60 : _number_of_teeth(n)
61 , _pressure_angle(phi)
62 , _module(m)
63 {
64 }
65private:
66 int _number_of_teeth;
67 double _pressure_angle;
68 double _module;
69 double _clearance = 0.0;
70 double _angle = 0.0;
71 Geom::Point _centre;
72 D2<SBasis> _involute(double start, double stop) {
73 D2<SBasis> B;
75 Linear bo = Linear(start,stop);
76
77 B[0] = cos(bo,2);
78 B[1] = sin(bo,2);
79
80 I = B - Linear(0,1) * derivative(B);
81 I = I*base_radius() + _centre;
82 return I;
83 }
84 D2<SBasis> _arc(double start, double stop, double R) {
85 D2<SBasis> B;
86 Linear bo = Linear(start,stop);
87
88 B[0] = cos(bo,2);
89 B[1] = sin(bo,2);
90
91 B = B*R + _centre;
92 return B;
93 }
94 // angle of the base circle used to create the involute to a certain radius
95 double involute_swath_angle(double R) {
96 if (R <= base_radius()) return 0.0;
97 return sqrt(R*R - base_radius()*base_radius())/base_radius();
98 }
99
100 // angle of the base circle between the origin of the involute and the intersection on another radius
101 double involute_intersect_angle(double R) {
102 if (R <= base_radius()) return 0.0;
103 return (sqrt(R*R - base_radius()*base_radius())/base_radius()) - acos(base_radius()/R);
104 }
105};
106
107static void
109 for(unsigned d=0;d<2;d++)
110 a[d][0][0] = b[d];
111}
112
113Geom::Path Gear::path() {
114 Geom::Path pb;
115
116 // angle covered by a full tooth and fillet
117 double tooth_rotation = 2.0 * tooth_thickness_angle();
118 // angle covered by an involute
119 double involute_advance = involute_intersect_angle(outer_radius()) - involute_intersect_angle(root_radius());
120 // angle covered by the tooth tip
121 double tip_advance = tooth_thickness_angle() - (2 * (involute_intersect_angle(outer_radius()) - involute_intersect_angle(pitch_radius())));
122 // angle covered by the toothe root
123 double root_advance = (tooth_rotation - tip_advance) - (2.0 * involute_advance);
124 // begin drawing the involute at t if the root circle is larger than the base circle
125 double involute_t = involute_swath_angle(root_radius())/involute_swath_angle(outer_radius());
126
127 //rewind angle to start drawing from the leading edge of the tooth
128 double first_tooth_angle = _angle - ((0.5 * tip_advance) + involute_advance);
129
130 Geom::Point prev;
131 for (int i=0; i < _number_of_teeth; i++)
132 {
133 double cursor = first_tooth_angle + (i * tooth_rotation);
134
135 D2<SBasis> leading_I = compose(_involute(cursor, cursor + involute_swath_angle(outer_radius())), Linear(involute_t,1));
136 if(i != 0) makeContinuous(leading_I, prev);
137 pb.append(SBasisCurve(leading_I));
138 cursor += involute_advance;
139 prev = leading_I.at1();
140
141 D2<SBasis> tip = _arc(cursor, cursor+tip_advance, outer_radius());
142 makeContinuous(tip, prev);
143 pb.append(SBasisCurve(tip));
144 cursor += tip_advance;
145 prev = tip.at1();
146
147 cursor += involute_advance;
148 D2<SBasis> trailing_I = compose(_involute(cursor, cursor - involute_swath_angle(outer_radius())), Linear(1,involute_t));
149 makeContinuous(trailing_I, prev);
150 pb.append(SBasisCurve(trailing_I));
151 prev = trailing_I.at1();
152
153 if (base_radius() > root_radius()) {
154 Geom::Point leading_start = trailing_I.at1();
155 Geom::Point leading_end = (root_radius() * unit_vector(leading_start - _centre)) + _centre;
156 prev = leading_end;
157 pb.appendNew<LineSegment>(leading_end);
158 }
159
160 D2<SBasis> root = _arc(cursor, cursor+root_advance, root_radius());
161 makeContinuous(root, prev);
163 //cursor += root_advance;
164 prev = root.at1();
165
166 if (base_radius() > root_radius()) {
167 Geom::Point trailing_start = root.at1();
168 Geom::Point trailing_end = (base_radius() * unit_vector(trailing_start - _centre)) + _centre;
169 pb.appendNew<LineSegment>(trailing_end);
170 prev = trailing_end;
171 }
172 }
173
174 return pb;
175}
176
177Gear Gear::spawn(Geom::Point p) {
178 double radius = Geom::distance(this->centre(), p) - this->pitch_radius();
179 int N = (int) floor( (radius / this->pitch_radius()) * this->number_of_teeth() );
180
181 Gear gear(N, _module, _pressure_angle);
182 gear.centre(p);
183
184 double a = atan2(p - this->centre());
185 double new_angle = 0.0;
186 if (gear.number_of_teeth() % 2 == 0)
187 new_angle -= gear.tooth_thickness_angle();
188 new_angle -= (_angle) * (pitch_radius() / gear.pitch_radius());
189 new_angle += (a) * (pitch_radius() / gear.pitch_radius());
190 gear.angle(new_angle + a);
191 return gear;
192}
193
194
195
196// #################################################################
197
198
199
200namespace Inkscape {
201namespace LivePathEffect {
202
203
205 Effect(lpeobject),
206 teeth(_("_Teeth:"), _("The number of teeth"), "teeth", &wr, this, 10),
207 phi(_("_Phi:"), _("Tooth pressure angle (typically 20-25 deg). The ratio of teeth not in contact."), "phi", &wr, this, 5),
208 min_radius(_("Min Radius:"), _("Minimum radius, low values can be slow"), "min_radius", &wr, this, 5.0)
209{
210 /* Tooth pressure angle: The angle between the tooth profile and a perpendicular to the pitch
211 * circle, usually at the point where the pitch circle meets the tooth profile. Standard angles
212 * are 20 and 25 degrees. The pressure angle affects the force that tends to separate mating
213 * gears. A high pressure angle means that higher ratio of teeth not in contact. However, this
214 * allows the teeth to have higher capacity and also allows fewer teeth without undercutting.
215 */
216
218 teeth.param_set_range(3, 1e10);
219 min_radius.param_set_range(0.01, std::numeric_limits<double>::max());
223}
224
225LPEGears::~LPEGears() = default;
226
229{
230 Geom::PathVector path_out;
231 Geom::Path gearpath = path_in[0];
232
233 Geom::Path::iterator it(gearpath.begin());
234 if ( it == gearpath.end() ) return path_out;
235
236 Gear * gear = new Gear(teeth, 200.0, phi * M_PI / 180);
237 Geom::Point gear_centre = (*it).finalPoint();
238 gear->centre(gear_centre);
239 gear->angle(atan2((*it).initialPoint() - gear_centre));
240
241 ++it;
242 if ( it == gearpath.end() ) return path_out;
243 double radius = Geom::distance(gear_centre, (*it).finalPoint());
244 radius = radius < min_radius?min_radius:radius;
245 gear->pitch_radius(radius);
246
247 path_out.push_back( gear->path());
248
249 for (++it; it != gearpath.end() ; ++it) {
250 if (are_near((*it).initialPoint(), (*it).finalPoint())) {
251 continue;
252 }
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;
260
261 return path_out;
262}
263
264} // namespace LivePathEffect
265} /* namespace Inkscape */
266
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 :
Conversion between Bezier control points and SBasis curves.
Adaptor that creates 2D functions from 1D ones.
Definition d2.h:55
Point at1() const
Definition d2.h:125
Function that interpolates linearly between two values.
Definition linear.h:55
Sequence of subpaths.
Definition pathvector.h:122
void push_back(Path const &path)
Append a path at the end.
Definition pathvector.h:172
Sequence of contiguous curves, aka spline.
Definition path.h:353
const_iterator end() const
Definition path.h:465
void append(Curve *curve)
Add a new curve to the end of the path.
Definition path.h:750
const_iterator begin() const
Definition path.h:464
void appendNew(Args &&... args)
Append a new curve to the path.
Definition path.h:804
Two-dimensional point that doubles as a vector.
Definition point.h:66
Symmetric power basis curve.
void registerParameter(Parameter *param)
Definition effect.cpp:1704
LPEGears(LivePathEffectObject *lpeobject)
Geom::PathVector doEffect_path(Geom::PathVector const &path_in) override
void param_set_range(double min, double max)
RootCluster root
double c[8][4]
auto floor(Geom::Rect const &rect)
Definition geom.h:131
static void makeContinuous(D2< SBasis > &a, Point const b)
Geom::Point start
double angle(std::vector< Point > const &A)
Various utility functions.
Definition affine.h:22
SBasisN< n > cos(LinearN< n > bo, int k)
Angle distance(Angle const &a, Angle const &b)
Definition angle.h:163
double atan2(Point const &p)
SBasisN< n > sqrt(SBasisN< n > const &a, int k)
D2< T > compose(D2< T > const &a, T const &b)
Definition d2.h:405
Bezier derivative(Bezier const &a)
Definition bezier.cpp:282
Point unit_vector(Point const &a)
SBasisN< n > sin(LinearN< n > bo, int k)
bool are_near(Affine const &a1, Affine const &a2, Coord eps=EPSILON)
Helper class to stream background task notifications as a series of messages.
int n
Definition spiro.cpp:57
size_t N