]> git.donarmstrong.com Git - lilypond.git/blob - lily/bezier-bow.cc
partial: 1.3.13.jcn
[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--1999 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 "paper-def.hh"
16 #include "debug.hh"
17 #include "main.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 (Paper_def* paper_l)
43 {
44   paper_l_ = paper_l;
45 }
46
47 void
48 Bezier_bow::blow_fit ()
49 {
50   Real x1 = encompass_[0][X_AXIS];
51   Real x2 = encompass_.top ()[X_AXIS];
52
53   Real factor = 1.0;
54   for (int i=1; i < encompass_.size ()-1; i++)
55     {
56       if (encompass_[i][X_AXIS] > x1 && encompass_[i][X_AXIS] < x2)
57         {
58          Real y = curve_.get_other_coordinate (X_AXIS, encompass_[i][X_AXIS]);
59          if (y>0)
60            {
61              Real f = encompass_[i][Y_AXIS] / y;
62              factor = factor >? f;
63            }
64         }
65     }
66
67   curve_.control_[1][Y_AXIS] *= factor;
68   curve_.control_[2][Y_AXIS] *= factor;  
69   return_.control_[1][Y_AXIS] *= factor;
70   return_.control_[2][Y_AXIS] *= factor;
71
72   curve_.check_sanity ();
73 }
74
75 Real
76 Bezier_bow::calc_f (Real height)
77 {
78   transform ();
79   calc_default (height);
80
81   Real dy = check_fit_f ();
82   calc_return (0, 0);
83
84   transform_back ();
85   return dy;
86 }
87
88 void
89 Bezier_bow::calc ()
90 {
91   transform ();
92   calc_controls ();
93   transform_back ();
94 }
95
96
97
98 /*
99   [TODO]
100     * see if it works
101     * document in Documentation/fonts.tex
102  */
103
104 /*
105   Clipping
106
107   This function tries to address two issues:
108     * the tangents of the slur should always point inwards 
109       in the actual slur, i.e.  *after rotating back*.
110
111     * slurs shouldn't be too high 
112       let's try : h <= 1.2 b && h <= 3 staffheight?
113
114   We could calculate the tangent of the bezier curve from
115   both ends going inward, and clip the slur at the point
116   where the tangent (after rotation) points up (or inward
117   with a certain maximum angle).
118   
119   However, we assume that real clipping is not the best
120   answer.  We expect that moving the outer control point up 
121   if the slur becomes too high will result in a nicer slur 
122   after recalculation.
123
124   Knowing that the tangent is the line through the first
125   two control points, we'll clip (move the outer control
126   point upwards) too if the tangent points outwards.
127  */
128
129 bool
130 Bezier_bow::calc_clipping ()
131 {
132   Real clip_height = paper_l_->get_var ("slur_clip_height");
133   Real clip_ratio = paper_l_->get_var ("slur_clip_ratio");
134   Real clip_angle = paper_l_->get_var ("slur_clip_angle");
135
136   Real b = curve_.control_[3][X_AXIS] - curve_.control_[0][X_AXIS];
137   Real clip_h = clip_ratio * b <? clip_height;
138   Real begin_h = curve_.control_[1][Y_AXIS] - curve_.control_[0][Y_AXIS];
139   Real end_h = curve_.control_[2][Y_AXIS] - curve_.control_[3][Y_AXIS];
140   Real begin_dy = 0 >? begin_h - clip_h;
141   Real end_dy = 0 >? end_h - clip_h;
142   
143   Real pi = M_PI;
144   Real begin_alpha = (curve_.control_[1] - curve_.control_[0]).arg () + dir_ * alpha_;
145   Real end_alpha = pi -  (curve_.control_[2] - curve_.control_[3]).arg () - dir_  * alpha_;
146
147   Real max_alpha = clip_angle / 90 * pi / 2;
148   if ((begin_dy < 0) && (end_dy < 0)
149     && (begin_alpha < max_alpha) && (end_alpha < max_alpha))
150     return false;
151
152   transform_back ();
153
154   bool again = true;
155
156   if ((begin_dy > 0) || (end_dy > 0))
157     {
158       Real dy = (begin_dy + end_dy) / 4;
159       dy *= cos (alpha_);
160       encompass_[0][Y_AXIS] += dir_ * dy;
161       encompass_.top ()[Y_AXIS] += dir_ * dy;
162     }
163   else
164     {
165       //ugh
166       Real c = 0.4;
167       if (begin_alpha >= max_alpha)
168         begin_dy = 0 >? c * begin_alpha / max_alpha * begin_h;
169       if (end_alpha >= max_alpha)
170         end_dy = 0 >? c * end_alpha / max_alpha * end_h;
171
172       encompass_[0][Y_AXIS] += dir_ * begin_dy;
173       encompass_.top ()[Y_AXIS] += dir_ * end_dy;
174
175       Offset delta = encompass_.top () - encompass_[0];
176       alpha_ = delta.arg ();
177     }
178
179   transform ();
180
181   return again;
182 }
183
184 void
185 Bezier_bow::calc_controls ()
186 {
187   for (int i = 0; i < 3; i++)
188     {
189       
190       if (i && !calc_clipping ())
191         return;
192
193       /*
194         why do we always recalc from 0?
195         shouldn't calc_f () be used (too), rather than blow_fit () (only)?
196        */
197       calc_default (0);
198       curve_.check_sanity ();
199       if (check_fit_f () > 0)
200         {
201           calc_tangent_controls ();
202           blow_fit ();
203         }
204       else
205         {
206           calc_return (0, 0);
207           return;
208         }
209     }
210 }
211
212 void
213 Bezier_bow::calc_return (Real begin_alpha, Real end_alpha)
214 {
215   Real thick = paper_l_->get_var ("slur_thickness");
216
217   return_.control_[0] = curve_.control_[3];
218   return_.control_[3] = curve_.control_[0];
219
220   return_.control_[1] = curve_.control_[2] - thick * complex_exp (Offset (0, 90 + end_alpha));
221   return_.control_[2] = curve_.control_[1] - thick * complex_exp (Offset (0, 90 - begin_alpha));  
222 }
223
224 /*
225 This function calculates 2 center control points, based on 
226   
227  See Documentation/fonts.tex
228  */
229 void
230 Bezier_bow::calc_tangent_controls ()
231 {
232   Real b = curve_.control_[3][X_AXIS];
233   Real h = curve_.control_[1][Y_AXIS];
234   
235
236   Drul_array<Offset> disturb;
237   Drul_array<Real> maxtan;  
238   maxtan[LEFT]  = maxtan[RIGHT] = h/(b/2);
239   disturb[LEFT]  = disturb[RIGHT] =   Offset (b / 2, h);
240
241   for (int i = 1; i < encompass_.size () -1; i++)
242     {
243       Real y= encompass_[i][Y_AXIS];
244       if (y> 0)
245         {
246           Real x = encompass_[i][X_AXIS];
247           
248           Direction d = LEFT;
249           do
250             {
251               // 1 if d == LEFT
252               int k = (1 - d)/2;
253               Real tan = y /  ((1-k)* b - d * x);
254
255               if (tan > maxtan[d])
256                 {
257                   maxtan[d] = tan;
258                   disturb[d] = Offset (x,y);
259                 }
260             }
261           while (flip (&d)!=LEFT);
262         }
263     }
264
265   for (int i = 0; i < encompass_.size (); i++ )
266     h = h >? encompass_[i][Y_AXIS];
267
268   /*
269     The curve will always be under line between curve_.control_0 -> curve_.control_1, so
270     make it extra steep by slur_rc_factor
271   */
272   Real rc_correct = paper_l_->get_var ("slur_rc_factor");
273   
274   Drul_array<Real> angles;
275   Direction d = LEFT;
276   do
277     {
278       maxtan[d] *= rc_correct;
279       angles[d] = atan (-d * maxtan[d]);
280     }
281   while (flip(&d) != LEFT);
282
283   Real rc3 = 0.0;
284
285   // if we have two disturbing points, have line through those...
286   if (disturb[LEFT][Y_AXIS] != disturb[RIGHT][Y_AXIS])
287     rc3 = (disturb[RIGHT][Y_AXIS] - disturb[LEFT][Y_AXIS]) / (disturb[RIGHT][X_AXIS] - disturb[LEFT][X_AXIS]);
288
289   else
290     rc3 = tan ((angles[RIGHT] - angles[LEFT]) / 2);
291
292
293   // ugh: be less steep
294   rc3 /= 2*rc_correct;
295   
296
297   Real c2 = -maxtan[RIGHT] * curve_.control_[3][X_AXIS];
298
299   // use highest because rc3 is damped.
300   Real maxy = disturb[LEFT][Y_AXIS] >? disturb[RIGHT][Y_AXIS];
301   Real c3 = disturb[LEFT][Y_AXIS] > disturb[RIGHT][Y_AXIS] ?
302     maxy - rc3 * disturb[LEFT][X_AXIS] :
303     maxy - rc3 * disturb[RIGHT][X_AXIS];
304
305   curve_.control_[1][X_AXIS] = c3 / (maxtan[LEFT] - rc3);
306   curve_.control_[1][Y_AXIS] = maxtan[LEFT] * curve_.control_[1][X_AXIS];
307   
308   curve_.control_[2][X_AXIS] = (c3 - c2) / (maxtan[RIGHT] - rc3);
309   curve_.control_[2][Y_AXIS] = maxtan[RIGHT] * curve_.control_[2][X_AXIS] + c2;
310
311
312   curve_.check_sanity();
313   
314   calc_return (angles[LEFT], angles[RIGHT]);
315 }
316
317 /*
318   The maximum amount that the encompass points stick out above the bezier curve.
319  */
320 Real
321 Bezier_bow::check_fit_f () const
322 {
323   Real dy = 0;
324   Real x1 = encompass_[0][X_AXIS];
325   Real x2 = encompass_.top ()[X_AXIS];
326   for (int i = 1; i < encompass_.size () - 1; i++)
327     {
328       Real x = encompass_[i][X_AXIS];
329       if (x1< x&& x < x2)
330         dy = dy >? (encompass_[i][Y_AXIS] - curve_.get_other_coordinate (X_AXIS, x));
331     }
332   return dy;
333 }
334
335
336 void
337 Bezier_bow::set (Array<Offset> points, Direction dir)
338 {
339   dir_ = dir;
340   encompass_ = points;
341 }
342
343 void
344 Bezier_bow::transform ()
345 {
346   origin_ = encompass_[0];
347   translate (encompass_,-origin_);
348
349   Offset delta = encompass_.top () - encompass_[0];
350   alpha_ = delta.arg ();
351
352   rotate (encompass_, -alpha_);
353
354   if (dir_ == DOWN)
355     flipy (encompass_);
356 }
357
358 void
359 Bezier_bow::transform_back ()
360 {
361   if (dir_ == DOWN)
362     {
363       curve_.flip (Y_AXIS);
364       return_.flip (Y_AXIS);
365       flipy (encompass_);
366     }
367
368   curve_.rotate (alpha_);
369   curve_.translate (origin_);
370  
371   return_.rotate (alpha_);
372   return_.translate (origin_);
373  
374   rotate (encompass_,alpha_);
375   translate (encompass_,origin_);
376 }
377
378 /*
379  See Documentation/fonts.tex
380  */
381 void
382 Bezier_bow::calc_default (Real h)
383 {
384   Real pi = M_PI;
385
386   Real height_limit = paper_l_->get_var ("slur_height_limit");
387   Real ratio = paper_l_->get_var ("slur_ratio");
388
389   Real alpha = height_limit * 2.0 / pi;
390   Real beta = pi * ratio / (2.0 * height_limit);
391
392   Offset delta (encompass_.top ()[X_AXIS] 
393     - encompass_[0][X_AXIS], 0);
394
395   Real b = delta.length ();
396   Real indent = alpha * atan (beta * b);
397   Real height = indent + h;
398  
399   curve_.control_ [0] = Offset (0, 0);
400   curve_.control_ [1] = Offset (indent, height);
401   curve_.control_ [2] = Offset (b - indent, height);
402   curve_.control_ [3] = Offset (b, 0);
403 }
404
405