]> git.donarmstrong.com Git - lilypond.git/blob - lily/bezier.cc
38c40f3ffc00e2c4df2827408ef94c2d65aca4fd
[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    Assuming AX is X_AXIS, and D is UP, finds the
218    maximum value of curve_coordinate(t, Y_AXIS) subject to
219    l <= curve_coordinate(t, X_AXIS) <= r.
220 */
221 Real
222 Bezier::minmax (Axis ax, Real l, Real r, Direction d) const
223 {
224   Axis other = other_axis (ax);
225   Interval lr (l, r);
226   vector<Real> solutions;
227
228   // Possible solutions are:
229   // t = 0 or 1, or...
230   solutions.push_back (0);
231   solutions.push_back (1);
232
233   // t is a critical point for the other-axis polynomial, or...
234   Polynomial p_prime (polynomial (other));
235   p_prime.differentiate ();
236   vector<Real> criticals = p_prime.solve ();
237   solutions.insert (solutions.end (), criticals.begin (), criticals.end ());
238
239   // t solves curve_coordinate(t, X_AXIS) = l or r.
240   Direction dir = LEFT;
241   do
242     {
243       Polynomial p (polynomial (ax));
244       p.coefs_[0] -= lr[dir];
245
246       vector<Real> sol = p.solve ();
247       solutions.insert (solutions.end (), sol.begin (), sol.end ());
248     }
249   while (flip (&dir) != LEFT);
250
251   Polynomial p (polynomial (ax));
252   Polynomial other_p (polynomial (other));
253   vector<Real> values;
254   for (vsize i = solutions.size (); i--;)
255     {
256       Real t = solutions[i];
257       if (t >= 0 && t <= 1
258           // FIXME: floating point comparison for equality
259           // Two of the t in solutions were found by solving
260           // p(t) = l, bzw. r, and we want this test to pass for these t,
261           // but it can easily fail if floating point internal precision
262           // differs from storage precision.
263           // Better to store separately the two t for which p(t) = l and r
264           && p.eval (t) >= l && p.eval (t) <= r)
265         values.push_back (other_p.eval (t));
266     }
267
268   if (values.empty ())
269     {
270       programming_error ("no solution found for Bezier intersection");
271       return 0.0;
272     }
273
274   vector_sort (values, less<Real> ());
275   return (d == DOWN) ? values[0] : values.back ();
276 }
277
278 /**
279    Compute the bounding box dimensions in direction of A.
280 */
281 Interval
282 Bezier::extent (Axis a) const
283 {
284   int o = (a + 1) % NO_AXES;
285   Offset d;
286   d[Axis (o)] = 1.0;
287   Interval iv;
288   vector<Real> sols (solve_derivative (d));
289   sols.push_back (1.0);
290   sols.push_back (0.0);
291   for (vsize i = sols.size (); i--;)
292     {
293       Offset o (curve_point (sols[i]));
294       iv.unite (Interval (o[a], o[a]));
295     }
296   return iv;
297 }
298
299 Interval
300 Bezier::control_point_extent (Axis a) const
301 {
302   Interval ext;
303   for (int i = CONTROL_COUNT; i--;)
304     ext.add_point (control_[i][a]);
305
306   return ext;
307 }
308
309 /**
310    Flip around axis A
311 */
312 void
313 Bezier::scale (Real x, Real y)
314 {
315   for (int i = CONTROL_COUNT; i--;)
316     {
317       control_[i][X_AXIS] = x * control_[i][X_AXIS];
318       control_[i][Y_AXIS] = y * control_[i][Y_AXIS];
319     }
320 }
321
322 void
323 Bezier::rotate (Real phi)
324 {
325   Offset rot (complex_exp (Offset (0, phi)));
326   for (int i = 0; i < CONTROL_COUNT; i++)
327     control_[i] = complex_multiply (rot, control_[i]);
328 }
329
330 void
331 Bezier::translate (Offset o)
332 {
333   for (int i = 0; i < CONTROL_COUNT; i++)
334     control_[i] += o;
335 }
336
337 void
338 Bezier::assert_sanity () const
339 {
340   for (int i = 0; i < CONTROL_COUNT; i++)
341     assert (!isnan (control_[i].length ())
342             && !isinf (control_[i].length ()));
343 }
344
345 void
346 Bezier::reverse ()
347 {
348   Bezier b2;
349   for (int i = 0; i < CONTROL_COUNT; i++)
350     b2.control_[CONTROL_COUNT - i - 1] = control_[i];
351   *this = b2;
352 }
353
354 /*
355   Subdivide a bezier at T into LEFT_PART and RIGHT_PART
356   using deCasteljau's algorithm.
357 */
358 void
359 Bezier::subdivide (Real t, Bezier *left_part, Bezier *right_part) const
360 {
361   Offset p[CONTROL_COUNT][CONTROL_COUNT];
362
363   for (int i = 0; i < CONTROL_COUNT; i++)
364     p[i][CONTROL_COUNT - 1 ] = control_[i];
365   for (int j = CONTROL_COUNT - 2; j >= 0; j--)
366     for (int i = 0; i < CONTROL_COUNT - 1; i++)
367       p[i][j] = p[i][j + 1] + t * (p[i + 1][j + 1] - p[i][j + 1]);
368   for (int i = 0; i < CONTROL_COUNT; i++)
369     {
370       left_part->control_[i] = p[0][CONTROL_COUNT - 1 - i];
371       right_part->control_[i] = p[i][i];
372     }
373 }
374
375 /*
376   Extract a portion of a bezier from T_MIN to T_MAX
377 */
378
379 Bezier
380 Bezier::extract (Real t_min, Real t_max) const
381 {
382   if ((t_min < 0) || (t_max) > 1)
383     programming_error
384     ("bezier extract arguments outside of limits: curve may have bad shape");
385   if (t_min >= t_max)
386     programming_error
387     ("lower bezier extract value not less than upper value: curve may have bad shape");
388   Bezier bez1, bez2, bez3, bez4;
389   if (t_min == 0.0)
390     bez2 = *this;
391   else
392     subdivide (t_min, &bez1, &bez2);
393   if (t_max == 1.0)
394     return bez2;
395   else
396     {
397       bez2.subdivide ((t_max - t_min) / (1 - t_min), &bez3, &bez4);
398       return bez3;
399     }
400 }