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