]> git.donarmstrong.com Git - lilypond.git/blob - lily/bezier.cc
release: 1.0.1
[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 Jan Nieuwenhuizen <janneke@gnu.org>
7 */
8
9 #include <math.h>
10 #include "bezier.hh"
11 #include "misc.hh"
12
13 #ifndef STANDALONE
14 #include "direction.hh"
15 #include "dimension.hh"
16 #include "paper-def.hh"
17 #include "debug.hh"
18 #include "main.hh"
19 #define BEZIER_BOW_DOUT if (check_debug && !monitor->silent_b ("Bezier_bow")) cout
20 #else
21 #define BEZIER_BOW_DOUT cerr
22 #endif
23
24 void
25 Curve::flipy ()
26 {
27   for (int i = 0; i < size (); i++)
28     (*this)[i].mirror (Y_AXIS);
29 }
30
31 int
32 Curve::largest_disturbing ()
33 {
34   Real alpha = 0;
35   int j = 0;
36   for (int i = 1; i < size (); i++)
37     {
38       if ((*this)[i].y () > 0)
39         {
40           Real phi = (*this)[i].y () / (*this)[i].x ();
41           if (phi > alpha)
42             {
43               alpha = phi;
44               j = i;
45             }
46         }
47     }
48   return j;
49 }
50
51 void
52 Curve::rotate (Real phi)
53 {
54   Offset rot (complex_exp (Offset (0, phi)));
55   for (int i = 0; i < size (); i++)
56     (*this)[i] = complex_multiply (rot, (*this)[i]);
57 }
58
59 void
60 Curve::translate (Offset o)
61 {
62   for (int i = 0; i < size (); i++)
63     (*this)[i] += o;
64 }
65
66 Bezier::Bezier ()
67 {
68   control_.set_size (4);
69 }
70
71 void
72 Bezier::calc (int steps)
73 {       
74   steps = steps >? 10;
75   curve_.set_size (steps);
76   Real dt = 1.0 / curve_.size ();
77   Offset c = 3.0 * (control_[1] - control_[0]);
78   Offset b = 3.0 * (control_[2] - control_[1]) - c;
79   Offset a = control_[3] - (control_[0] + c + b);
80   Real t = 0.0;
81   for (int i = 0; i < curve_.size (); i++ )
82     {    
83       curve_[i] = ((a * t + b) * t + c) * t + control_[0];
84       t += dt;
85     }
86 }
87
88 void
89 Bezier::set (Array<Offset> points)
90 {       
91   assert (points.size () == 4);
92   control_ = points;
93 }
94
95 Real
96 Bezier::y (Real x)
97 {
98   // ugh
99   // bounds func should be templatised to take array of offsets too?
100   Array<Real> positions;
101   for (int i = 0; i < curve_.size (); i++)
102     positions.push (curve_[i].x ());
103
104   Slice slice = get_bounds_slice (positions, x);
105   // ugh
106   Offset z1 = curve_[0 >? slice.max () - 1];
107   Offset z2 = curve_[1 >? slice.max ()];
108   Real multiplier = (x - z2.x ()) / (z1.x () - z2.x ());
109   Real y = z1.y () * multiplier + (1.0 - multiplier) * z2.y();
110
111   return y;
112 }
113
114
115 Bezier_bow::Bezier_bow (Paper_def* paper_l)
116 {
117   paper_l_ = paper_l;
118   return_.set_size (4);
119 }
120
121 void
122 Bezier_bow::blow_fit ()
123 {
124   Real dy1 = check_fit_f ();
125   if (!dy1)
126     return;
127
128   // be careful not to take too big step
129   Real f = 0.3;
130   Real h1 = dy1 * f;
131   control_[1].y () += h1; 
132   control_[2].y () += h1; 
133   return_[1].y () += h1; 
134   return_[2].y () += h1; 
135
136   calc_bezier ();
137   Real dy2 = check_fit_f ();
138   if (!dy2)
139     return;
140
141 #ifndef STANDALONE
142   Real internote_f = paper_l_->internote_f ();
143 #else
144   Real internote_f = STAFFHEIGHT / 8;
145 #endif
146
147   Real epsilon = internote_f / 4;
148   if (abs (dy2 - dy1) < epsilon)
149     return;
150   
151   /*
152     Assume 
153       dy = B (h) 
154     with 
155       B (h) = a * h + b;
156
157     Then we get for h : B (h) = 0
158
159      B(0)  = dy1 = a * 0 + b   =>   b = dy1
160      B(h1) = dy2 = a * h1 + b  =>   a * f * dy1 + b = dy2
161
162          =>
163
164      a * dy1 / 2 + dy1 = dy2  =>  a = (dy2 - dy1) / (f * dy1)
165    */
166
167   Real a = (dy2 - dy1) / (f * dy1);
168   Real b = dy1;
169   Real h = -b / a;
170
171   if (sign (h) != sign (h1))
172     return;
173
174   if (sign (h) != sign (h1))
175     return;
176
177   control_[1].y () += -h1 +h; 
178   control_[2].y () += -h1 +h; 
179   return_[1].y () += -h1 +h;
180   return_[2].y () += -h1 +h; 
181 }
182
183 void
184 Bezier_bow::calc_bezier ()
185 {
186   Real s = sqrt (control_[3].x () * control_[3].x () 
187     + control_[1].y () * control_[2].y ());
188 #ifndef STANDALONE
189   Real internote = paper_l_->internote_f ();
190 #else
191   Real internote = STAFFHEIGHT / 8;
192 #endif
193   int steps = (int)rint (s / internote);
194   Bezier::calc (steps);
195 }
196
197 Real
198 Bezier_bow::calc_f (Real height)
199 {
200   transform ();
201   calc_default (height);
202
203   calc_bezier ();
204
205   Real dy = check_fit_f ();
206   calc_return (0, 0);
207
208   transform_back ();
209   return dy;
210 }
211
212 void
213 Bezier_bow::calc ()
214 {
215   transform ();
216
217   calc_controls ();
218
219   transform_back ();
220 }
221
222 /*
223   [TODO]
224     * see if it works
225     * document in Documentation/fonts.tex
226  */
227
228 /*
229   Clipping
230
231   This function tries to address two issues:
232     * the tangents of the slur should always point inwards 
233       in the actual slur, i.e.  *after rotating back*.
234
235     * slurs shouldn't be too high 
236       let's try : h <= 1.2 b && h <= 3 staffheight?
237
238   We could calculate the tangent of the bezier curve from
239   both ends going inward, and clip the slur at the point
240   where the tangent (after rotation) points up (or inward
241   with a certain maximum angle).
242   
243   However, we assume that real clipping is not the best
244   answer.  We expect that moving the outer control point up 
245   if the slur becomes too high will result in a nicer slur 
246   after recalculation.
247
248   Knowing that the tangent is the line through the first
249   two control points, we'll clip (move the outer control
250   point upwards) too if the tangent points outwards.
251  */
252
253 bool
254 Bezier_bow::calc_clipping ()
255 {
256 #ifndef STANDALONE
257   Real clip_height = paper_l_->get_var ("slur_clip_height");
258   Real clip_ratio = paper_l_->get_var ("slur_clip_ratio");
259   Real clip_angle = paper_l_->get_var ("slur_clip_angle");
260 #else
261   Real staffsize_f = STAFFHEIGHT;
262   Real clip_height = 3.0 * staffsize_f;
263   Real clip_ratio = 1.2;
264   Real clip_angle = 100;
265 #endif
266
267   Real b = control_[3].x () - control_[0].x ();
268   Real clip_h = clip_ratio * b <? clip_height;
269   Real begin_h = control_[1].y () - control_[0].y ();
270   Real end_h = control_[2].y () - control_[3].y ();
271   Real begin_dy = 0 >? begin_h - clip_h;
272   Real end_dy = 0 >? end_h - clip_h;
273   
274   Real pi = M_PI;
275   Real begin_alpha = (control_[1] - control_[0]).arg () + dir_ * alpha_;
276   Real end_alpha = pi -  (control_[2] - control_[3]).arg () - dir_ * alpha_;
277
278   Real max_alpha = clip_angle / 90 * pi / 2;
279   if ((begin_dy < 0) && (end_dy < 0)
280     && (begin_alpha < max_alpha) && (end_alpha < max_alpha))
281     return false;
282
283   transform_back ();
284
285   bool again = true;
286
287   if ((begin_dy > 0) || (end_dy > 0))
288     {
289       Real dy = (begin_dy + end_dy) / 4;
290       dy *= cos (alpha_);
291       encompass_[0].y () += dir_ * dy;
292       encompass_[encompass_.size () - 1].y () += dir_ * dy;
293     }
294   else
295     {
296       //ugh
297       Real c = 0.4;
298       if (begin_alpha >= max_alpha)
299         begin_dy = 0 >? c * begin_alpha / max_alpha * begin_h;
300       if (end_alpha >= max_alpha)
301         end_dy = 0 >? c * end_alpha / max_alpha * end_h;
302
303       encompass_[0].y () += dir_ * begin_dy;
304       encompass_[encompass_.size () - 1].y () += dir_ * end_dy;
305
306       Offset delta = encompass_[encompass_.size () - 1] - encompass_[0];
307       alpha_ = delta.arg ();
308     }
309
310   transform ();
311
312   return again;
313 }
314
315 void
316 Bezier_bow::calc_controls ()
317 {
318   for (int i = 0; i < 3; i++)
319     {
320       if (i && !calc_clipping ())
321         return;
322
323       calc_default (0);
324       calc_bezier ();
325       
326       if (check_fit_bo ())
327         {
328           calc_return (0, 0);
329           return;
330         }
331       calc_tangent_controls ();
332
333       blow_fit ();
334       // ugh
335       blow_fit ();
336     }
337 }
338
339 void
340 Bezier_bow::calc_return (Real begin_alpha, Real end_alpha)
341 {
342 #ifndef STANDALONE
343   Real thick = paper_l_->get_var ("slur_thickness");
344 #else
345   Real thick = 1.8 * 0.4 PT;
346 #endif
347
348   return_[0] = control_[3];
349   return_[3] = control_[0];
350
351   return_[1] = control_[2] - thick * complex_exp (Offset (0, 90 + end_alpha));
352   return_[2] = control_[1] 
353     - thick * complex_exp (Offset (0, 90 - begin_alpha));  
354 }
355
356 /*
357  See Documentation/fonts.tex
358  */
359 void
360 Bezier_bow::calc_tangent_controls ()
361 {
362   Offset ijk_p (control_[3].x () / 2, control_[1].y ());
363   BEZIER_BOW_DOUT << "ijk: " << ijk_p.x () << ", " << ijk_p.y () << endl;
364
365   Real default_rc = ijk_p.y () / ijk_p.x ();
366
367   int begin_disturb = encompass_.largest_disturbing ();
368   Offset begin_p = begin_disturb ? Offset (encompass_[begin_disturb].x (), 
369     encompass_[begin_disturb].y ()) : ijk_p;
370   Real begin_rc = begin_p.y () / begin_p.x ();
371   if (default_rc > begin_rc)
372     {
373       begin_p = ijk_p;
374       begin_rc = default_rc;
375     }
376
377   Curve reversed;
378   reversed.set_size (encompass_.size ());
379   Real b = control_[3].x ();
380   for (int i = 0; i < encompass_.size (); i++ )
381     {
382       //       b     1  0
383       // r  =     -        *  c 
384       //       0     0 -1   
385       reversed[i].x () = b - encompass_[encompass_.size () - i - 1].x ();
386       reversed[i].y () = encompass_[encompass_.size () - i - 1].y ();
387     }
388
389   int end_disturb = reversed.largest_disturbing ();
390   end_disturb = end_disturb ? encompass_.size () - end_disturb - 1 : 0;
391   Offset end_p = end_disturb ? Offset (encompass_[end_disturb].x (), 
392     encompass_[end_disturb].y ()) : ijk_p;
393   Real end_rc = end_p.y () / (control_[3].x () - end_p.x ());
394   if (default_rc > end_rc)
395     {
396       end_p = ijk_p;
397       end_rc = default_rc;
398     }
399   BEZIER_BOW_DOUT << "begin " << begin_p.x () << ", " << begin_p.y () << endl;
400   BEZIER_BOW_DOUT << "end " << end_p.x () << ", " << end_p.y () << endl;
401
402   Real height =control_[1].y (); 
403   for (int i = 0; i < encompass_.size (); i++ )
404     height = height >? encompass_[i].y ();
405
406   // emperic computer science:
407   //   * tangents somewhat steeper than minimal line
408 #ifndef STANDALONE
409   Real internote = paper_l_->internote_f ();
410   Real rc_correct = paper_l_->get_var ("slur_rc_factor");
411 #else
412   Real internote = STAFFHEIGHT / 8;
413   Real rc_correct = 2.4;
414 #endif
415
416   begin_rc *= rc_correct;
417   end_rc *= rc_correct;
418   Real rc1 = begin_rc;
419   Real rc2 = -end_rc;
420   
421   Real begin_alpha = atan (begin_rc);
422   Real end_alpha = atan (-end_rc);
423   Real theta = (begin_alpha - end_alpha) / 2;
424
425   Real epsilon = internote / 5;
426
427   // if we have two disturbing points, have height line through those...
428   if (!((abs (begin_p.x () - end_p.x ()) < epsilon)
429     && (abs (begin_p.y () - end_p.y ()) < epsilon)))
430       theta = atan (end_p.y () - begin_p.y ()) / (end_p.x () - begin_p.x ());
431
432   Real rc3 = tan (theta);
433   // ugh: be less steep
434   rc3 /= 2*rc_correct;
435
436   Real c2 = -rc2 * control_[3].x ();
437   Real c3 = begin_p.y () > end_p.y () ? begin_p.y () 
438     - rc3 * begin_p.x () : end_p.y () - rc3 * end_p.x ();
439
440   BEZIER_BOW_DOUT << "y1 = " << rc1 << " x + 0" << endl;
441   BEZIER_BOW_DOUT << "y2 = " << rc2 << " x + " << c2 << endl;
442   BEZIER_BOW_DOUT << "y3 = " << rc3 << " x + " << c3 << endl;
443   control_[1].x () = c3 / (rc1 - rc3);
444   control_[1].y () = rc1 * control_[1].x ();
445   control_[2].x () = (c3 - c2) / (rc2 - rc3);
446   BEZIER_BOW_DOUT << "c2.x () = " << control_[2].x () << endl;
447   BEZIER_BOW_DOUT << "(c3 - c2) = " << (c3 - c2) << endl;
448   BEZIER_BOW_DOUT << "(rc2 - rc3) = " << (rc2 - rc3) << endl;
449   control_[2].y () = rc2 * control_[2].x () + c2;
450   BEZIER_BOW_DOUT << "c2.y ()" << control_[2].y () << endl;
451
452   calc_return (begin_alpha, end_alpha);
453 }
454
455 bool
456 Bezier_bow::check_fit_bo ()
457 {
458   for (int i = 1; i < encompass_.size () - 1; i++)
459     if ((encompass_[i].x () > encompass_[0].x ())
460       && (encompass_[i].x () < encompass_[encompass_.size () -1].x ()))
461       if (encompass_[i].y () > y (encompass_[i].x ()))
462         return false;
463   return true;
464 }
465
466 Real
467 Bezier_bow::check_fit_f ()
468 {
469   Real dy = 0;
470   for (int i = 1; i < encompass_.size () - 1; i++)
471     if ((encompass_[i].x () > encompass_[0].x ())
472       && (encompass_[i].x () < encompass_[encompass_.size () -1].x ()))
473       dy = dy >? (encompass_[i].y () - y (encompass_[i].x ()));
474   return dy;
475 }
476
477 void
478 Bezier_bow::set (Array<Offset> points, int dir)
479 {
480   dir_ = dir;
481   encompass_ = points;
482 }
483
484 void
485 Bezier_bow::transform ()
486 {
487   origin_ = encompass_[0];
488   encompass_.translate (-origin_);
489
490   Offset delta = encompass_[encompass_.size () - 1] - encompass_[0];
491   alpha_ = delta.arg ();
492
493   encompass_.rotate (-alpha_);
494
495   if (dir_ == DOWN)
496     encompass_.flipy ();
497 }
498
499 void
500 Bezier_bow::transform_back ()
501 {
502   if (dir_ == DOWN)
503     {
504       control_.flipy ();
505       return_.flipy ();
506       encompass_.flipy ();
507     }
508
509   control_.rotate (alpha_);
510   control_.translate (origin_);
511
512   return_.rotate (alpha_);
513   return_.translate (origin_);
514
515   encompass_.rotate (alpha_);
516   encompass_.translate (origin_);
517 }
518
519 /*
520  See Documentation/fonts.tex
521  */
522 void
523 Bezier_bow::calc_default (Real h)
524 {
525   Real pi = M_PI;
526 #ifndef STANDALONE
527   Real height_limit = paper_l_->get_var ("slur_height_limit");
528   Real ratio = paper_l_->get_var ("slur_ratio");
529 #else
530   Real staffsize_f = STAFFHEIGHT;
531   Real height_limit = staffsize_f;
532   Real ratio = 1.0/3.0;
533 #endif
534
535   Real alpha = height_limit * 2.0 / pi;
536   Real beta = pi * ratio / (2.0 * height_limit);
537
538   Offset delta (encompass_[encompass_.size () - 1].x () 
539     - encompass_[0].x (), 0);
540   Real b = delta.length ();
541   Real indent = alpha * atan (beta * b);
542   Real height = indent + h;
543  
544 #define RESIZE_ICE
545 #ifndef RESIZE_ICE
546   Array<Offset> control;
547   control.push (Offset (0, 0));
548   control.push (Offset (indent, height));
549   control.push (Offset (b - indent, height));
550   control.push (Offset (b, 0));
551 #else
552   Array<Offset> control (4);
553   control[0] = Offset (0, 0);
554   control[1] = Offset (indent, height);
555   control[2] = Offset (b - indent, height);
556   control[3] = Offset (b, 0);
557 #endif
558   Bezier::set (control);
559 }
560