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