]> git.donarmstrong.com Git - lilypond.git/blob - lily/bezier-bow.cc
2206a6663f1c8b9385e31917c4db6b76bc8dcf6b
[lilypond.git] / lily / bezier-bow.cc
1 /*
2   bezier.cc -- implement Bezier and Bezier_bow
3
4   source file of the GNU LilyPond music typesetter
5
6   (c) 1998--2000 Jan Nieuwenhuizen <janneke@gnu.org>
7 */
8
9 #include <math.h>
10 #include "bezier-bow.hh"
11 #include "misc.hh"
12 #include "bezier.hh"
13 #include "dimensions.hh"
14 #include "direction.hh"
15 #include "debug.hh"
16 #include "main.hh"
17 #include "lily-guile.hh"
18
19 void
20 flipy (Array<Offset>  &c)
21 {
22   for (int i = c.size (); i--;)
23     c[i][Y_AXIS] = - c[i][Y_AXIS];
24 }
25
26 void
27 rotate (Array<Offset> &c, Real phi)
28 {
29   Offset rot (complex_exp (Offset (0, phi)));
30   for (int i = 0; i < c.size (); i++)
31     c[i] = complex_multiply (rot, c[i]);
32 }
33
34 void
35 translate (Array<Offset> &c, Offset o)
36 {
37   for (int i = 0; i < c.size (); i++)
38     c[i] += o;
39 }
40
41
42 Bezier_bow::Bezier_bow (Array<Offset> points, Direction dir)
43 {
44   dir_ = dir;
45   encompass_ = points;
46   to_canonic_form ();
47
48   rc_factor_ = 1.0;
49   height_limit_ = 1.0;
50   ratio_ = 1.0;
51 }
52
53 void
54 Bezier_bow::blow_fit ()
55 {
56   Real len = curve_.control_[3][X_AXIS] ; 
57   Real ind = curve_.control_[1][X_AXIS] / len;
58   Real h = curve_.control_[1][Y_AXIS] * fit_factor () / len;
59
60   // ugh. Unhardcode this
61   if (h > 4 * ind)
62     {
63       h = 4* ind; 
64     }
65
66   if (h > 0.8 + -2 * ind)
67     {
68       h = 0.8 - 2  *ind; 
69     }
70   
71   curve_.control_[1][Y_AXIS] = h * len;
72   curve_.control_[2][Y_AXIS] = h * len;  
73
74   curve_.check_sanity ();
75 }
76
77 Real
78 Bezier_bow::calc_enclosed_area_f () const
79 {
80   Real a = 0;
81   for (int i=0; i < encompass_.size (); i++)
82     {
83       Interval x;
84       Interval y;
85       if (i == 0)
86         x = Interval (0, encompass_[1][X_AXIS]/2);
87       else if (i == encompass_.size () - 1)
88         x = Interval ((encompass_[i-1][X_AXIS] + encompass_[i][X_AXIS])/2, 
89                       encompass_[i][X_AXIS]);
90       else
91         x = Interval ((encompass_[i-1][X_AXIS] + encompass_[i][X_AXIS])/2, 
92                       (encompass_[i][X_AXIS] + encompass_[i+1][X_AXIS])/2);
93       
94       y[MIN] = encompass_[i][Y_AXIS];
95       // solve_point (X, 0|Xmax) has no solutions...
96       y[MAX] = (x[MIN] ? curve_.get_other_coordinate (X_AXIS, x[MIN]) : 0
97                 + curve_.get_other_coordinate (X_AXIS, (x[MIN] + x[MAX])/2)
98                 + x[MAX] != encompass_.top ()[X_AXIS] ? curve_.get_other_coordinate (X_AXIS, x[MAX]) : 0)/3;
99       Real da = x.length () * y.length ();
100       if (da > 0)
101         a += da;
102     }
103   return a;
104 }
105
106 void
107 Bezier_bow::minimise_enclosed_area ()
108 {
109   Real area = calc_enclosed_area_f ();
110   
111   Array<Offset> da (2);
112   for (int i=1; i < 3; i++)
113     {
114       for (Axis a=X_AXIS; a < NO_AXES; incr (a)) 
115         {
116           Real r = curve_.control_[i][a];
117           curve_.control_[i][a] += 1;
118           da[i-1][a] = (calc_enclosed_area_f () - area) / area;
119           curve_.control_[i][a] = r;
120         }
121     }
122   
123   for (int i=1; i < 3; i++)
124     {
125       da[i-1] *= da[i-1].length () ? 1.0 / da[i-1].length () : 1.0;
126       curve_.control_[i] -= da[i-1] * curve_.control_[i].length ();
127     }
128 }
129
130 void
131 Bezier_bow::calculate ()
132 {
133   calc_default ();
134   if (fit_factor () > 1.0)
135     {
136       //    calc_tangent_controls ();
137       blow_fit ();
138       minimise_enclosed_area ();
139     }
140 }
141
142
143   
144 Bezier
145 Bezier_bow::get_curve ()const
146 {
147   Bezier rv = curve_;
148   if (dir_ == DOWN)
149     {
150       rv.flip (Y_AXIS);
151     }
152
153   rv.rotate (alpha_);
154   rv.translate (origin_);
155   
156   return rv;
157 }
158
159 static Real const FUDGE = 1e-8;
160
161 /*
162   This function calculates 2 center control points,
163   based on lines through c_0 --> left disturbing
164   and c_3--> right disturbing encompass points.
165   
166   See Documentation/fonts.tex
167  */
168 void
169 Bezier_bow::calc_tangent_controls ()
170 {
171   Real b = curve_.control_[3][X_AXIS];
172   Real h = curve_.control_[1][Y_AXIS];
173   
174
175   Drul_array<Offset> disturb;
176   Drul_array<Real> maxtan;  
177   maxtan[LEFT]  = maxtan[RIGHT] = h/(b/2);
178   disturb[LEFT]  = disturb[RIGHT] =   Offset (b / 2, h);
179
180   for (int i = 1; i < encompass_.size () -1; i++)
181     {
182       Real y= encompass_[i][Y_AXIS];
183       if (y> 0)
184         {
185           Real x = encompass_[i][X_AXIS];
186           
187           Direction d = LEFT;
188           do
189             {
190               // 1 if d == LEFT
191               int k = (1 - d)/2;
192               Real tan = y /  ((1-k)* b - d * x);
193
194               if (tan > maxtan[d])
195                 {
196                   maxtan[d] = tan;
197                   disturb[d] = Offset (x,y);
198                 }
199             }
200           while (flip (&d)!=LEFT);
201         }
202     }
203
204   for (int i = 0; i < encompass_.size (); i++ )
205     h = h >? encompass_[i][Y_AXIS];
206
207   /*
208     The curve will always be under line between curve_.control_0 -> curve_.control_1, so
209     make it extra steep by slur_rc_factor
210   */
211
212
213   Drul_array<Real> angles;
214   Direction d = LEFT;
215   do
216     {
217       maxtan[d] *= -d * rc_factor_;
218       angles[d] = atan (maxtan[d]);
219     }
220   while (flip(&d) != LEFT);
221
222   Real rc3 = 0.0;
223
224   /* 
225     if we have two disturbing points, have line through those...
226     in order to get a sane line, make sure points are reasonably far apart
227     X distance must be reasonably(!) big (division)
228    */
229   if (abs (disturb[LEFT][X_AXIS] - disturb[RIGHT][X_AXIS]) > FUDGE)
230     rc3 = (disturb[RIGHT][Y_AXIS] - disturb[LEFT][Y_AXIS]) / (disturb[RIGHT][X_AXIS] - disturb[LEFT][X_AXIS]);
231
232   else
233     rc3 = tan ((angles[LEFT] - angles[RIGHT]) / 2);
234
235
236   // ugh: be less steep
237   rc3 /= 2*rc_factor_;
238   
239
240   Real c2 = -maxtan[RIGHT] * curve_.control_[3][X_AXIS];
241
242   // use highest because rc3 is damped.
243   Real maxy = disturb[LEFT][Y_AXIS] >? disturb[RIGHT][Y_AXIS];
244   Real c3 = disturb[LEFT][Y_AXIS] > disturb[RIGHT][Y_AXIS] ?
245     maxy - rc3 * disturb[LEFT][X_AXIS] :
246     maxy - rc3 * disturb[RIGHT][X_AXIS];
247
248   curve_.control_[1][X_AXIS] = c3 / (maxtan[LEFT] - rc3);
249   curve_.control_[1][Y_AXIS] = maxtan[LEFT] * curve_.control_[1][X_AXIS];
250   
251   curve_.control_[2][X_AXIS] = (c3 - c2) / (maxtan[RIGHT] - rc3);
252   curve_.control_[2][Y_AXIS] = maxtan[RIGHT] * curve_.control_[2][X_AXIS] + c2;
253
254
255   curve_.check_sanity();
256 }
257
258 /*
259   The maximum amount that the encompass points stick out above the bezier curve.
260  */
261 Real
262 Bezier_bow::fit_factor () const
263 {
264   Real x1 = encompass_[0][X_AXIS];
265   Real x2 = encompass_.top ()[X_AXIS];
266
267   Real factor = 1.0;
268   for (int i=1; i < encompass_.size ()-1; i++)
269     {
270       if (encompass_[i][X_AXIS] > x1 && encompass_[i][X_AXIS] < x2)
271         {
272          Real y = curve_.get_other_coordinate (X_AXIS, encompass_[i][X_AXIS]);
273          if (y>0)
274            {
275              Real f = encompass_[i][Y_AXIS] / y;
276              factor = factor >? f;
277            }
278         }
279     }
280
281
282   return factor;
283 }
284
285
286
287
288 void
289 Bezier_bow::to_canonic_form ()
290 {
291   origin_ = encompass_[0];
292   translate (encompass_,-origin_);
293
294   Offset delta = encompass_.top () - encompass_[0];
295   alpha_ = delta.arg ();
296
297   rotate (encompass_, -alpha_);
298   if (dir_ == DOWN)
299     {
300       flipy (encompass_);
301     }
302
303   while (encompass_.size () > 1 && encompass_[1][X_AXIS] <= 0.0)
304     {
305       programming_error ("Degenerate slur: infinite steepness reqd");
306       encompass_.del (1);
307     }
308
309   Real l = encompass_.top ()[X_AXIS];
310   while (encompass_.size () > 1 && encompass_.top (1)[X_AXIS] >= l)
311     {
312       programming_error ("Degenerate slur: infinite steepness reqd");
313       encompass_.del (encompass_.size ()-2);
314     }
315 }
316
317
318
319 /*
320  See Documentation/fonts.tex
321  */
322 void
323 Bezier_bow::calc_default ()
324 {
325   Real pi = M_PI;
326
327   Real alpha = height_limit_ * 2.0 / pi;
328   Real beta = pi * ratio_ / (2.0 * height_limit_);
329
330   Offset delta (encompass_.top ()[X_AXIS] 
331     - encompass_[0][X_AXIS], 0);
332
333   Real b = delta.length ();
334   Real indent = alpha * atan (beta * b);
335   Real height = indent;
336  
337   curve_.control_ [0] = Offset (0, 0);
338   curve_.control_ [1] = Offset (indent, height);
339   curve_.control_ [2] = Offset (b - indent, height);
340   curve_.control_ [3] = Offset (b, 0);
341 }
342
343
344