]> git.donarmstrong.com Git - lilypond.git/blob - lily/bezier.cc
a68c03907d047eaa49db73aa8f051e8ea22b586e
[lilypond.git] / lily / bezier.cc
1 /*
2   This file is part of LilyPond, the GNU music typesetter.
3
4   Copyright (C) 1998--2011 Jan Nieuwenhuizen <janneke@gnu.org>
5
6   LilyPond is free software: you can redistribute it and/or modify
7   it under the terms of the GNU General Public License as published by
8   the Free Software Foundation, either version 3 of the License, or
9   (at your option) any later version.
10
11   LilyPond is distributed in the hope that it will be useful,
12   but WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14   GNU General Public License for more details.
15
16   You should have received a copy of the GNU General Public License
17   along with LilyPond.  If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "bezier.hh"
21 #include "warn.hh"
22 #include "libc-extension.hh"
23
24 Real binomial_coefficient_3[] =
25 {
26   1, 3, 3, 1
27 };
28
29 void
30 scale (vector<Offset> *array, Real x, Real y)
31 {
32   for (vsize i = 0; i < array->size (); i++)
33     {
34       (*array)[i][X_AXIS] = x * (*array)[i][X_AXIS];
35       (*array)[i][Y_AXIS] = y * (*array)[i][Y_AXIS];
36     }
37 }
38
39 void
40 rotate (vector<Offset> *array, Real phi)
41 {
42   Offset rot (complex_exp (Offset (0, phi)));
43   for (vsize i = 0; i < array->size (); i++)
44     (*array)[i] = complex_multiply (rot, (*array)[i]);
45 }
46
47 void
48 translate (vector<Offset> *array, Offset o)
49 {
50   for (vsize i = 0; i < array->size (); i++)
51     (*array)[i] += o;
52 }
53
54 /*
55   Formula of the bezier 3-spline
56
57   sum_{j = 0}^3 (3 over j) z_j (1-t)^ (3-j)  t^j
58
59
60   A is the axis of X coordinate.
61 */
62
63 Real
64 Bezier::get_other_coordinate (Axis a, Real x) const
65 {
66   Axis other = Axis ((a + 1) % NO_AXES);
67   vector<Real> ts = solve_point (a, x);
68
69   if (ts.size () == 0)
70     {
71       programming_error ("no solution found for Bezier intersection");
72       return 0.0;
73     }
74
75 #ifdef PARANOID
76   Offset c = curve_point (ts[0]);
77   if (fabs (c[a] - x) > 1e-8)
78     programming_error ("bezier intersection not correct?");
79 #endif
80
81   return curve_coordinate (ts[0], other);
82 }
83
84 vector<Real>
85 Bezier::get_other_coordinates (Axis a, Real x) const
86 {
87   Axis other = other_axis (a);
88   vector<Real> ts = solve_point (a, x);
89   vector<Real> sols;
90   for (vsize i = 0; i < ts.size (); i++)
91     sols.push_back (curve_coordinate (ts[i], other));
92   return sols;
93 }
94
95 Real
96 Bezier::curve_coordinate (Real t, Axis a) const
97 {
98   Real tj = 1;
99   Real one_min_tj[4];
100   one_min_tj[0] = 1;
101   for (int i = 1; i < 4; i++)
102     one_min_tj[i] = one_min_tj[i - 1] * (1 - t);
103
104   Real r = 0.0;
105   for (int j = 0; j < 4; j++)
106     {
107       r += control_[j][a] * binomial_coefficient_3[j]
108            * tj * one_min_tj[3 - j];
109
110       tj *= t;
111     }
112
113   return r;
114 }
115
116 Offset
117 Bezier::curve_point (Real t) const
118 {
119   Real tj = 1;
120   Real one_min_tj[4];
121   one_min_tj[0] = 1;
122   for (int i = 1; i < 4; i++)
123     one_min_tj[i] = one_min_tj[i - 1] * (1 - t);
124
125   Offset o;
126   for (int j = 0; j < 4; j++)
127     {
128       o += control_[j] * binomial_coefficient_3[j]
129            * tj * one_min_tj[3 - j];
130
131       tj *= t;
132     }
133
134 #ifdef PARANOID
135   assert (fabs (o[X_AXIS] - polynomial (X_AXIS).eval (t)) < 1e-8);
136   assert (fabs (o[Y_AXIS] - polynomial (Y_AXIS).eval (t)) < 1e-8);
137 #endif
138
139   return o;
140 }
141
142 /*
143   Cache binom (3, j) t^j (1-t)^{3-j}
144 */
145 struct Polynomial_cache
146 {
147   Polynomial terms_[4];
148   Polynomial_cache ()
149   {
150     for (int j = 0; j <= 3; j++)
151       terms_[j]
152         = binomial_coefficient_3[j]
153           * Polynomial::power (j, Polynomial (0, 1))
154           * Polynomial::power (3 - j, Polynomial (1, -1));
155   }
156 };
157
158 static Polynomial_cache poly_cache;
159
160 Polynomial
161 Bezier::polynomial (Axis a) const
162 {
163   Polynomial p (0.0);
164   Polynomial q;
165   for (int j = 0; j <= 3; j++)
166     {
167       q = poly_cache.terms_[j];
168       q *= control_[j][a];
169       p += q;
170     }
171
172   return p;
173 }
174
175 /**
176    Remove all numbers outside [0, 1] from SOL
177 */
178 vector<Real>
179 filter_solutions (vector<Real> sol)
180 {
181   for (vsize i = sol.size (); i--;)
182     if (sol[i] < 0 || sol[i] > 1)
183       sol.erase (sol.begin () + i);
184   return sol;
185 }
186
187 /**
188    find t such that derivative is proportional to DERIV
189 */
190 vector<Real>
191 Bezier::solve_derivative (Offset deriv) const
192 {
193   Polynomial xp = polynomial (X_AXIS);
194   Polynomial yp = polynomial (Y_AXIS);
195   xp.differentiate ();
196   yp.differentiate ();
197
198   Polynomial combine = xp * deriv[Y_AXIS] - yp * deriv [X_AXIS];
199
200   return filter_solutions (combine.solve ());
201 }
202
203 /*
204   Find t such that curve_point (t)[AX] == COORDINATE
205 */
206 vector<Real>
207 Bezier::solve_point (Axis ax, Real coordinate) const
208 {
209   Polynomial p (polynomial (ax));
210   p.coefs_[0] -= coordinate;
211
212   vector<Real> sol (p.solve ());
213   return filter_solutions (sol);
214 }
215
216 /**
217    For the portion of the curve between L and R along axis AX,
218    return the bounding box limit in direction D along the cross axis to AX.
219    If there is no portion between L and R, return 0.0 and report error.
220 */
221 Real
222 Bezier::minmax (Axis ax, Real l, Real r, Direction d) const
223 {
224   Axis bx = other_axis (ax);
225
226   // The curve could hit its bounding box limit along BX at:
227   //  points where the curve is parallel to AX,
228   Offset vec (0.0, 0.0);
229   vec[ax] = 1.0;
230   vector<Real> sols (solve_derivative (vec));
231   //  or endpoints of the curve,
232   sols.push_back (0.999);
233   sols.push_back (0.001);
234   // (using points just inside the ends, so that an endpoint is evaulated
235   //  if it falls within rounding error of L or R and the curve lies inside)
236
237   Interval iv;
238   for (vsize i = sols.size (); i--;)
239     {
240       Offset p (curve_point (sols[i]));
241       if (p[ax] >= l && p[ax] <= r)
242         iv.add_point (p[bx]);
243     }
244
245   //  or intersections of the curve with the bounding lines at L and R.
246   Interval lr (l, r);
247   Direction dir = LEFT;
248   do
249     {
250       vector<Real> v = get_other_coordinates (ax, lr[dir]);
251       for (vsize i = v.size (); i--;)
252         iv.add_point (v[i]);
253     }
254   while (flip (&dir) != LEFT);
255
256   if (iv.is_empty ())
257     {
258       programming_error ("Bezier curve does not cross region of concern");
259       return 0.0;
260     }
261
262   return iv.at (d);
263 }
264
265 /**
266    Compute the bounding box dimensions in direction of A.
267 */
268 Interval
269 Bezier::extent (Axis a) const
270 {
271   int o = (a + 1) % NO_AXES;
272   Offset d;
273   d[Axis (o)] = 1.0;
274   Interval iv;
275   vector<Real> sols (solve_derivative (d));
276   sols.push_back (1.0);
277   sols.push_back (0.0);
278   for (vsize i = sols.size (); i--;)
279     {
280       Offset o (curve_point (sols[i]));
281       iv.unite (Interval (o[a], o[a]));
282     }
283   return iv;
284 }
285
286 Interval
287 Bezier::control_point_extent (Axis a) const
288 {
289   Interval ext;
290   for (int i = CONTROL_COUNT; i--;)
291     ext.add_point (control_[i][a]);
292
293   return ext;
294 }
295
296 /**
297    Flip around axis A
298 */
299 void
300 Bezier::scale (Real x, Real y)
301 {
302   for (int i = CONTROL_COUNT; i--;)
303     {
304       control_[i][X_AXIS] = x * control_[i][X_AXIS];
305       control_[i][Y_AXIS] = y * control_[i][Y_AXIS];
306     }
307 }
308
309 void
310 Bezier::rotate (Real phi)
311 {
312   Offset rot (complex_exp (Offset (0, phi)));
313   for (int i = 0; i < CONTROL_COUNT; i++)
314     control_[i] = complex_multiply (rot, control_[i]);
315 }
316
317 void
318 Bezier::translate (Offset o)
319 {
320   for (int i = 0; i < CONTROL_COUNT; i++)
321     control_[i] += o;
322 }
323
324 void
325 Bezier::assert_sanity () const
326 {
327   for (int i = 0; i < CONTROL_COUNT; i++)
328     assert (!isnan (control_[i].length ())
329             && !isinf (control_[i].length ()));
330 }
331
332 void
333 Bezier::reverse ()
334 {
335   Bezier b2;
336   for (int i = 0; i < CONTROL_COUNT; i++)
337     b2.control_[CONTROL_COUNT - i - 1] = control_[i];
338   *this = b2;
339 }
340
341 /*
342   Subdivide a bezier at T into LEFT_PART and RIGHT_PART
343   using deCasteljau's algorithm.
344 */
345 void
346 Bezier::subdivide (Real t, Bezier *left_part, Bezier *right_part) const
347 {
348   Offset p[CONTROL_COUNT][CONTROL_COUNT];
349
350   for (int i = 0; i < CONTROL_COUNT; i++)
351     p[i][CONTROL_COUNT - 1 ] = control_[i];
352   for (int j = CONTROL_COUNT - 2; j >= 0; j--)
353     for (int i = 0; i < CONTROL_COUNT - 1; i++)
354       p[i][j] = p[i][j + 1] + t * (p[i + 1][j + 1] - p[i][j + 1]);
355   for (int i = 0; i < CONTROL_COUNT; i++)
356     {
357       left_part->control_[i] = p[0][CONTROL_COUNT - 1 - i];
358       right_part->control_[i] = p[i][i];
359     }
360 }
361
362 /*
363   Extract a portion of a bezier from T_MIN to T_MAX
364 */
365
366 Bezier
367 Bezier::extract (Real t_min, Real t_max) const
368 {
369   if ((t_min < 0) || (t_max) > 1)
370     programming_error
371     ("bezier extract arguments outside of limits: curve may have bad shape");
372   if (t_min >= t_max)
373     programming_error
374     ("lower bezier extract value not less than upper value: curve may have bad shape");
375   Bezier bez1, bez2, bez3, bez4;
376   if (t_min == 0.0)
377     bez2 = *this;
378   else
379     subdivide (t_min, &bez1, &bez2);
380   if (t_max == 1.0)
381     return bez2;
382   else
383     {
384       bez2.subdivide ((t_max - t_min) / (1 - t_min), &bez3, &bez4);
385       return bez3;
386     }
387 }