]> git.donarmstrong.com Git - lilypond.git/blob - lily/bezier.cc
d6ecd0b3135ba1fce8766b3d473cb6c16c415ef6
[lilypond.git] / lily / bezier.cc
1 /*
2   bezier.cc -- implement Bezier and Bezier_bow
3
4   source file of the GNU LilyPond music typesetter
5
6   (c) 1998--2005 Jan Nieuwenhuizen <janneke@gnu.org>
7 */
8
9 #include <math.h>
10
11 #include "bezier.hh"
12 #include "warn.hh"
13 #include "libc-extension.hh"
14
15 Real binomial_coefficient_3[] = {
16   1, 3, 3, 1
17 };
18
19 Real
20 binomial_coefficient (Real over, int under)
21 {
22   Real x = 1.0;
23
24   while (under)
25     {
26       x *= over / Real (under);
27
28       over -= 1.0;
29       under--;
30     }
31   return x;
32 }
33
34 void
35 scale (Array<Offset> *array, Real x, Real y)
36 {
37   for (int i = 0; i < array->size (); i++)
38     {
39       (*array)[i][X_AXIS] = x * (*array)[i][X_AXIS];
40       (*array)[i][Y_AXIS] = y * (*array)[i][Y_AXIS];
41     }
42 }
43
44 void
45 rotate (Array<Offset> *array, Real phi)
46 {
47   Offset rot (complex_exp (Offset (0, phi)));
48   for (int i = 0; i < array->size (); i++)
49     (*array)[i] = complex_multiply (rot, (*array)[i]);
50 }
51
52 void
53 translate (Array<Offset> *array, Offset o)
54 {
55   for (int i = 0; i < array->size (); i++)
56     (*array)[i] += o;
57 }
58
59 /*
60   Formula of the bezier 3-spline
61
62   sum_{j = 0}^3 (3 over j) z_j (1-t)^ (3-j)  t^j
63
64
65   A is the axis of X coordinate.
66 */
67
68 Real
69 Bezier::get_other_coordinate (Axis a, Real x) const
70 {
71   Axis other = Axis ((a +1)%NO_AXES);
72   Array<Real> ts = solve_point (a, x);
73
74   if (ts.size () == 0)
75     {
76       programming_error ("no solution found for Bezier intersection");
77       return 0.0;
78     }
79
80 #ifdef PARANOID
81   Offset c = curve_point (ts[0]);
82   if (fabs (c[a] - x) > 1e-8)
83     programming_error ("bezier intersection not correct?");
84 #endif
85
86   return curve_coordinate (ts[0], other);
87 }
88
89 Real
90 Bezier::curve_coordinate (Real t, Axis a) const
91 {
92   Real tj = 1;
93   Real one_min_tj[4];
94   one_min_tj[0] = 1;
95   for (int i = 1; i < 4; i++)
96     one_min_tj[i] = one_min_tj[i - 1] * (1 - t);
97
98   Real r = 0.0;
99   for (int j = 0; j < 4; j++)
100     {
101       r += control_[j][a] * binomial_coefficient_3[j]
102         * tj * one_min_tj[3 - j];
103
104       tj *= t;
105     }
106
107   return r;
108 }
109
110 Offset
111 Bezier::curve_point (Real t) const
112 {
113   Real tj = 1;
114   Real one_min_tj[4];
115   one_min_tj[0] = 1;
116   for (int i = 1; i < 4; i++)
117     one_min_tj[i] = one_min_tj[i - 1] * (1 - t);
118
119   Offset o;
120   for (int j = 0; j < 4; j++)
121     {
122       o += control_[j] * binomial_coefficient_3[j]
123         * tj * one_min_tj[3 - j];
124
125       tj *= t;
126     }
127
128 #ifdef PARANOID
129   assert (fabs (o[X_AXIS] - polynomial (X_AXIS).eval (t)) < 1e-8);
130   assert (fabs (o[Y_AXIS] - polynomial (Y_AXIS).eval (t)) < 1e-8);
131 #endif
132
133   return o;
134 }
135
136 /*
137   Cache binom(3,j) t^j (1-t)^{3-j}
138 */
139 static struct Polynomial bezier_term_cache[4];
140 static bool done_cache_init;
141
142 void
143 init_polynomial_cache ()
144 {
145   for (int j = 0; j <= 3; j++)
146     bezier_term_cache[j]
147       = binomial_coefficient_3[j]
148       * Polynomial::power (j, Polynomial (0, 1))
149       * Polynomial::power (3 - j, Polynomial (1, -1));
150   done_cache_init = true;
151 }
152
153 Polynomial
154 Bezier::polynomial (Axis a) const
155 {
156   if (!done_cache_init)
157     init_polynomial_cache ();
158
159   Polynomial p (0.0);
160   Polynomial q;
161   for (int j = 0; j <= 3; j++)
162     {
163       q = bezier_term_cache[j];
164       q *= control_[j][a];
165       p += q;
166     }
167
168   return p;
169 }
170
171 /**
172    Remove all numbers outside [0, 1] from SOL
173 */
174 Array<Real>
175 filter_solutions (Array<Real> sol)
176 {
177   for (int i = sol.size (); i--;)
178     if (sol[i] < 0 || sol[i] > 1)
179       sol.del (i);
180   return sol;
181 }
182
183 /**
184    find t such that derivative is proportional to DERIV
185 */
186 Array<Real>
187 Bezier::solve_derivative (Offset deriv) const
188 {
189   Polynomial xp = polynomial (X_AXIS);
190   Polynomial yp = polynomial (Y_AXIS);
191   xp.differentiate ();
192   yp.differentiate ();
193
194   Polynomial combine = xp * deriv[Y_AXIS] - yp * deriv [X_AXIS];
195
196   return filter_solutions (combine.solve ());
197 }
198
199 /*
200   Find t such that curve_point (t)[AX] == COORDINATE
201 */
202 Array<Real>
203 Bezier::solve_point (Axis ax, Real coordinate) const
204 {
205   Polynomial p (polynomial (ax));
206   p.coefs_[0] -= coordinate;
207
208   Array<Real> sol (p.solve ());
209   return filter_solutions (sol);
210 }
211
212 /**
213    Compute the bounding box dimensions in direction of A.
214 */
215 Interval
216 Bezier::extent (Axis a) const
217 {
218   int o = (a + 1)%NO_AXES;
219   Offset d;
220   d[Axis (o)] = 1.0;
221   Interval iv;
222   Array<Real> sols (solve_derivative (d));
223   sols.push (1.0);
224   sols.push (0.0);
225   for (int i = sols.size (); i--;)
226     {
227       Offset o (curve_point (sols[i]));
228       iv.unite (Interval (o[a], o[a]));
229     }
230   return iv;
231 }
232
233 /**
234    Flip around axis A
235 */
236 void
237 Bezier::scale (Real x, Real y)
238 {
239   for (int i = CONTROL_COUNT; i--;)
240     {
241       control_[i][X_AXIS] = x * control_[i][X_AXIS];
242       control_[i][Y_AXIS] = y * control_[i][Y_AXIS];
243     }
244 }
245
246 void
247 Bezier::rotate (Real phi)
248 {
249   Offset rot (complex_exp (Offset (0, phi)));
250   for (int i = 0; i < CONTROL_COUNT; i++)
251     control_[i] = complex_multiply (rot, control_[i]);
252 }
253
254 void
255 Bezier::translate (Offset o)
256 {
257   for (int i = 0; i < CONTROL_COUNT; i++)
258     control_[i] += o;
259 }
260
261 void
262 Bezier::assert_sanity () const
263 {
264   for (int i = 0; i < CONTROL_COUNT; i++)
265     assert (!isnan (control_[i].length ())
266             && !isinf (control_[i].length ()));
267 }
268
269 void
270 Bezier::reverse ()
271 {
272   Bezier b2;
273   for (int i = 0; i < CONTROL_COUNT; i++)
274     b2.control_[CONTROL_COUNT - i - 1] = control_[i];
275   *this = b2;
276 }