]> git.donarmstrong.com Git - lilypond.git/blob - lily/bezier.cc
Update.
[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
16 binomial_coefficient (Real over, int under)
17 {
18   Real x = 1.0;
19
20   while (under)
21     {
22       x *= over / Real (under);
23
24       over -= 1.0;
25       under--;
26     }
27   return x;
28 }
29
30 void
31 scale (Array<Offset> *array, Real x, Real y)
32 {
33   for (int i = 0; i < array->size (); i++)
34     {
35       (*array)[i][X_AXIS] = x * (*array)[i][X_AXIS];
36       (*array)[i][Y_AXIS] = y * (*array)[i][Y_AXIS];
37     }
38 }
39
40 void
41 rotate (Array<Offset> *array, Real phi)
42 {
43   Offset rot (complex_exp (Offset (0, phi)));
44   for (int i = 0; i < array->size (); i++)
45     (*array)[i] = complex_multiply (rot, (*array)[i]);
46 }
47
48 void
49 translate (Array<Offset> *array, Offset o)
50 {
51   for (int i = 0; i < array->size (); i++)
52     (*array)[i] += o;
53 }
54
55 /*
56   Formula of the bezier 3-spline
57
58   sum_{j = 0}^3 (3 over j) z_j (1-t)^ (3-j)  t^j
59
60
61   A is the axis of X coordinate.
62 */
63
64 Real
65 Bezier::get_other_coordinate (Axis a, Real x) const
66 {
67   Axis other = Axis ((a +1)%NO_AXES);
68   Array<Real> ts = solve_point (a, x);
69
70   if (ts.size () == 0)
71     {
72       programming_error ("No solution found for Bezier intersection.");
73       return 0.0;
74     }
75
76   Offset c = curve_point (ts[0]);
77
78   if (fabs (c[a] - x) > 1e-8)
79     programming_error ("Bezier intersection not correct?");
80
81   return c[other];
82 }
83
84 Offset
85 Bezier::curve_point (Real t) const
86 {
87   Real tj = 1;
88   Real one_min_tj = (1 - t) * (1 - t) * (1 - t);
89
90   Offset o;
91   for (int j = 0; j < 4; j++)
92     {
93       o += control_[j] * binomial_coefficient (3, j)
94         * pow (t, j) * pow (1 - t, 3 - j);
95
96       tj *= t;
97       if (1 - t)
98         one_min_tj /= (1 - t);
99     }
100
101 #ifdef PARANOID
102   assert (fabs (o[X_AXIS] - polynomial (X_AXIS).eval (t)) < 1e-8);
103   assert (fabs (o[Y_AXIS] - polynomial (Y_AXIS).eval (t)) < 1e-8);
104 #endif
105
106   return o;
107 }
108
109 Polynomial
110 Bezier::polynomial (Axis a) const
111 {
112   Polynomial p (0.0);
113   for (int j = 0; j <= 3; j++)
114     {
115       p
116         += (control_[j][a] * binomial_coefficient (3, j))
117         * Polynomial::power (j, Polynomial (0, 1))
118         * Polynomial::power (3 - j, Polynomial (1, -1));
119     }
120
121   return p;
122 }
123
124 /**
125    Remove all numbers outside [0, 1] from SOL
126 */
127 Array<Real>
128 filter_solutions (Array<Real> sol)
129 {
130   for (int i = sol.size (); i--;)
131     if (sol[i] < 0 || sol[i] > 1)
132       sol.del (i);
133   return sol;
134 }
135
136 /**
137    find t such that derivative is proportional to DERIV
138 */
139 Array<Real>
140 Bezier::solve_derivative (Offset deriv) const
141 {
142   Polynomial xp = polynomial (X_AXIS);
143   Polynomial yp = polynomial (Y_AXIS);
144   xp.differentiate ();
145   yp.differentiate ();
146
147   Polynomial combine = xp * deriv[Y_AXIS] - yp * deriv [X_AXIS];
148
149   return filter_solutions (combine.solve ());
150 }
151
152 /*
153   Find t such that curve_point (t)[AX] == COORDINATE
154 */
155 Array<Real>
156 Bezier::solve_point (Axis ax, Real coordinate) const
157 {
158   Polynomial p (polynomial (ax));
159   p.coefs_[0] -= coordinate;
160
161   Array<Real> sol (p.solve ());
162   return filter_solutions (sol);
163 }
164
165 /**
166    Compute the bounding box dimensions in direction of A.
167 */
168 Interval
169 Bezier::extent (Axis a) const
170 {
171   int o = (a + 1)%NO_AXES;
172   Offset d;
173   d[Axis (o)] = 1.0;
174   Interval iv;
175   Array<Real> sols (solve_derivative (d));
176   sols.push (1.0);
177   sols.push (0.0);
178   for (int i = sols.size (); i--;)
179     {
180       Offset o (curve_point (sols[i]));
181       iv.unite (Interval (o[a], o[a]));
182     }
183   return iv;
184 }
185
186 /**
187    Flip around axis A
188 */
189 void
190 Bezier::scale (Real x, Real y)
191 {
192   for (int i = CONTROL_COUNT; i--;)
193     {
194       control_[i][X_AXIS] = x * control_[i][X_AXIS];
195       control_[i][Y_AXIS] = y * control_[i][Y_AXIS];
196     }
197 }
198
199 void
200 Bezier::rotate (Real phi)
201 {
202   Offset rot (complex_exp (Offset (0, phi)));
203   for (int i = 0; i < CONTROL_COUNT; i++)
204     control_[i] = complex_multiply (rot, control_[i]);
205 }
206
207 void
208 Bezier::translate (Offset o)
209 {
210   for (int i = 0; i < CONTROL_COUNT; i++)
211     control_[i] += o;
212 }
213
214 void
215 Bezier::assert_sanity () const
216 {
217   for (int i = 0; i < CONTROL_COUNT; i++)
218     assert (!isnan (control_[i].length ())
219             && !isinf (control_[i].length ()));
220 }
221
222 void
223 Bezier::reverse ()
224 {
225   Bezier b2;
226   for (int i = 0; i < CONTROL_COUNT; i++)
227     b2.control_[CONTROL_COUNT - i - 1] = control_[i];
228   *this = b2;
229 }