]> git.donarmstrong.com Git - lilypond.git/blob - lily/bezier.cc
release: 0.1.62
[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 <jan@digicash.com>
7 */
8
9 #include <math.h>
10 #include "bezier.hh"
11 #include "misc.hh"
12
13 #ifndef STANDALONE
14 #include "direction.hh"
15 #include "dimen.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   control_[1].y () += -h1 +h; 
172   control_[2].y () += -h1 +h; 
173   return_[1].y () += -h1 +h;
174   return_[2].y () += -h1 +h; 
175 }
176
177 void
178 Bezier_bow::calc_bezier ()
179 {
180   Real s = sqrt (control_[3].x () * control_[3].x () 
181     + control_[1].y () * control_[2].y ());
182 #ifndef STANDALONE
183   Real internote = paper_l_->internote_f ();
184 #else
185   Real internote = STAFFHEIGHT / 8;
186 #endif
187   int steps = (int)rint (s / internote);
188   Bezier::calc (steps);
189 }
190
191 Real
192 Bezier_bow::calc_f (Real height)
193 {
194   transform ();
195   calc_default (height);
196
197   calc_bezier ();
198
199   Real dy = check_fit_f ();
200   calc_return (0, 0);
201
202   transform_back ();
203   return dy;
204 }
205
206 void
207 Bezier_bow::calc ()
208 {
209   transform ();
210
211   calc_controls ();
212
213   transform_back ();
214 }
215
216 /*
217   [TODO]
218     * see if it works
219     * document in Documentation/fonts.tex
220  */
221
222 /*
223   Clipping
224
225   This function tries to address two issues:
226     * the tangents of the slur should always point inwards 
227       in the actual slur, i.e.  *after rotating back*.
228
229     * slurs shouldn't be too high 
230       let's try : h <= 1.2 b && h <= 3 staffheight?
231
232   We could calculate the tangent of the bezier curve from
233   both ends going inward, and clip the slur at the point
234   where the tangent (after rotation) points up (or inward
235   with a certain maximum angle).
236   
237   However, we assume that real clipping is not the best
238   answer.  We expect that moving the outer control point up 
239   if the slur becomes too high will result in a nicer slur 
240   after recalculation.
241
242   Knowing that the tangent is the line through the first
243   two control points, we'll clip (move the outer control
244   point upwards) too if the tangent points outwards.
245  */
246
247 bool
248 Bezier_bow::calc_clipping ()
249 {
250 #ifndef STANDALONE
251   Real clip_height = paper_l_->get_var ("slur_clip_height");
252   Real clip_ratio = paper_l_->get_var ("slur_clip_ratio");
253   Real clip_angle = paper_l_->get_var ("slur_clip_angle");
254 #else
255   Real staffsize_f = STAFFHEIGHT;
256   Real clip_height = 3.0 * staffsize_f;
257   Real clip_ratio = 1.2;
258   Real clip_angle = 100;
259 #endif
260
261   Real b = control_[3].x () - control_[0].x ();
262   Real clip_h = clip_ratio * b <? clip_height;
263   Real begin_h = control_[1].y () - control_[0].y ();
264   Real end_h = control_[2].y () - control_[3].y ();
265   Real begin_dy = 0 >? begin_h - clip_h;
266   Real end_dy = 0 >? end_h - clip_h;
267   
268   Real pi = M_PI;
269   Real begin_alpha = (control_[1] - control_[0]).arg () + dir_ * alpha_;
270   Real end_alpha = pi -  (control_[2] - control_[3]).arg () - dir_ * alpha_;
271
272   Real max_alpha = clip_angle / 90 * pi / 2;
273   if ((begin_dy < 0) && (end_dy < 0)
274     && (begin_alpha < max_alpha) && (end_alpha < max_alpha))
275     return false;
276
277   transform_back ();
278
279   bool again = true;
280
281   if ((begin_dy > 0) || (end_dy > 0))
282     {
283       Real dy = (begin_dy + end_dy) / 4;
284       dy *= cos (alpha_);
285       encompass_[0].y () += dir_ * dy;
286       encompass_[encompass_.size () - 1].y () += dir_ * dy;
287     }
288   else
289     {
290       //ugh
291       Real c = 0.4;
292       if (begin_alpha >= max_alpha)
293         begin_dy = 0 >? c * begin_alpha / max_alpha * begin_h;
294       if (end_alpha >= max_alpha)
295         end_dy = 0 >? c * end_alpha / max_alpha * end_h;
296
297       encompass_[0].y () += dir_ * begin_dy;
298       encompass_[encompass_.size () - 1].y () += dir_ * end_dy;
299
300       Offset delta = encompass_[encompass_.size () - 1] - encompass_[0];
301       alpha_ = delta.arg ();
302     }
303
304   transform ();
305
306   return again;
307 }
308
309 void
310 Bezier_bow::calc_controls ()
311 {
312   for (int i = 0; i < 3; i++)
313     {
314       if (i && !calc_clipping ())
315         return;
316
317       calc_default (0);
318       calc_bezier ();
319       
320       if (check_fit_bo ())
321         {
322           calc_return (0, 0);
323           return;
324         }
325       calc_tangent_controls ();
326
327       blow_fit ();
328       // ugh
329       blow_fit ();
330     }
331 }
332
333 void
334 Bezier_bow::calc_return (Real begin_alpha, Real end_alpha)
335 {
336 #ifndef STANDALONE
337   Real thick = paper_l_->get_var ("slur_thickness");
338 #else
339   Real thick = 1.8 * 0.4 PT;
340 #endif
341
342   return_[0] = control_[3];
343   return_[3] = control_[0];
344
345   return_[1] = control_[2] - thick * complex_exp (Offset (0, 90 + end_alpha));
346   return_[2] = control_[1] 
347     - thick * complex_exp (Offset (0, 90 - begin_alpha));  
348 }
349
350 /*
351  See Documentation/fonts.tex
352  */
353 void
354 Bezier_bow::calc_tangent_controls ()
355 {
356   Offset ijk_p (control_[3].x () / 2, control_[1].y ());
357   BEZIER_BOW_DOUT << "ijk: " << ijk_p.x () << ", " << ijk_p.y () << endl;
358
359   Real default_rc = ijk_p.y () / ijk_p.x ();
360
361   int begin_disturb = encompass_.largest_disturbing ();
362   Offset begin_p = begin_disturb ? Offset (encompass_[begin_disturb].x (), 
363     encompass_[begin_disturb].y ()) : ijk_p;
364   Real begin_rc = begin_p.y () / begin_p.x ();
365   if (default_rc > begin_rc)
366     {
367       begin_p = ijk_p;
368       begin_rc = default_rc;
369     }
370
371   Curve reversed;
372   reversed.set_size (encompass_.size ());
373   Real b = control_[3].x ();
374   for (int i = 0; i < encompass_.size (); i++ )
375     {
376       //       b     1  0
377       // r  =     -        *  c 
378       //       0     0 -1   
379       reversed[i].x () = b - encompass_[encompass_.size () - i - 1].x ();
380       reversed[i].y () = encompass_[encompass_.size () - i - 1].y ();
381     }
382
383   int end_disturb = reversed.largest_disturbing ();
384   end_disturb = end_disturb ? encompass_.size () - end_disturb - 1 : 0;
385   Offset end_p = end_disturb ? Offset (encompass_[end_disturb].x (), 
386     encompass_[end_disturb].y ()) : ijk_p;
387   Real end_rc = end_p.y () / (control_[3].x () - end_p.x ());
388   if (default_rc > end_rc)
389     {
390       end_p = ijk_p;
391       end_rc = default_rc;
392     }
393   BEZIER_BOW_DOUT << "begin " << begin_p.x () << ", " << begin_p.y () << endl;
394   BEZIER_BOW_DOUT << "end " << end_p.x () << ", " << end_p.y () << endl;
395
396   Real height =control_[1].y (); 
397   for (int i = 0; i < encompass_.size (); i++ )
398     height = height >? encompass_[i].y ();
399
400   // emperic computer science:
401   //   * tangents somewhat steeper than minimal line
402 #ifndef STANDALONE
403   Real internote = paper_l_->internote_f ();
404   Real rc_correct = paper_l_->get_var ("slur_rc_factor");
405 #else
406   Real internote = STAFFHEIGHT / 8;
407   Real rc_correct = 2.4;
408 #endif
409
410   begin_rc *= rc_correct;
411   end_rc *= rc_correct;
412   Real rc1 = begin_rc;
413   Real rc2 = -end_rc;
414   
415   Real begin_alpha = atan (begin_rc);
416   Real end_alpha = atan (-end_rc);
417   Real theta = (begin_alpha - end_alpha) / 2;
418
419   Real epsilon = internote / 5;
420
421   // if we have two disturbing points, have height line through those...
422   if (!((abs (begin_p.x () - end_p.x ()) < epsilon)
423     && (abs (begin_p.y () - end_p.y ()) < epsilon)))
424       theta = atan (end_p.y () - begin_p.y ()) / (end_p.x () - begin_p.x ());
425
426   Real rc3 = tan (theta);
427   // ugh: be less steep
428   rc3 /= 2*rc_correct;
429
430   Real c2 = -rc2 * control_[3].x ();
431   Real c3 = begin_p.y () > end_p.y () ? begin_p.y () 
432     - rc3 * begin_p.x () : end_p.y () - rc3 * end_p.x ();
433
434   BEZIER_BOW_DOUT << "y1 = " << rc1 << " x + 0" << endl;
435   BEZIER_BOW_DOUT << "y2 = " << rc2 << " x + " << c2 << endl;
436   BEZIER_BOW_DOUT << "y3 = " << rc3 << " x + " << c3 << endl;
437   control_[1].x () = c3 / (rc1 - rc3);
438   control_[1].y () = rc1 * control_[1].x ();
439   control_[2].x () = (c3 - c2) / (rc2 - rc3);
440   BEZIER_BOW_DOUT << "c2.x () = " << control_[2].x () << endl;
441   BEZIER_BOW_DOUT << "(c3 - c2) = " << (c3 - c2) << endl;
442   BEZIER_BOW_DOUT << "(rc2 - rc3) = " << (rc2 - rc3) << endl;
443   control_[2].y () = rc2 * control_[2].x () + c2;
444   BEZIER_BOW_DOUT << "c2.y ()" << control_[2].y () << endl;
445
446   calc_return (begin_alpha, end_alpha);
447 }
448
449 bool
450 Bezier_bow::check_fit_bo ()
451 {
452   for (int i = 1; i < encompass_.size () - 1; i++)
453     if ((encompass_[i].x () > encompass_[0].x ())
454       && (encompass_[i].x () < encompass_[encompass_.size () -1].x ()))
455       if (encompass_[i].y () > y (encompass_[i].x ()))
456         return false;
457   return true;
458 }
459
460 Real
461 Bezier_bow::check_fit_f ()
462 {
463   Real dy = 0;
464   for (int i = 1; i < encompass_.size () - 1; i++)
465     if ((encompass_[i].x () > encompass_[0].x ())
466       && (encompass_[i].x () < encompass_[encompass_.size () -1].x ()))
467       dy = dy >? (encompass_[i].y () - y (encompass_[i].x ()));
468   return dy;
469 }
470
471 void
472 Bezier_bow::set (Array<Offset> points, int dir)
473 {
474   dir_ = dir;
475   encompass_ = points;
476 }
477
478 void
479 Bezier_bow::transform ()
480 {
481   origin_ = encompass_[0];
482   encompass_.translate (-origin_);
483
484   Offset delta = encompass_[encompass_.size () - 1] - encompass_[0];
485   alpha_ = delta.arg ();
486
487   encompass_.rotate (-alpha_);
488
489   if (dir_ == DOWN)
490     encompass_.flipy ();
491 }
492
493 void
494 Bezier_bow::transform_back ()
495 {
496   if (dir_ == DOWN)
497     {
498       control_.flipy ();
499       return_.flipy ();
500       encompass_.flipy ();
501     }
502
503   control_.rotate (alpha_);
504   control_.translate (origin_);
505
506   return_.rotate (alpha_);
507   return_.translate (origin_);
508
509   encompass_.rotate (alpha_);
510   encompass_.translate (origin_);
511 }
512
513 /*
514  See Documentation/fonts.tex
515  */
516 void
517 Bezier_bow::calc_default (Real h)
518 {
519   Real pi = M_PI;
520 #ifndef STANDALONE
521   Real height_limit = paper_l_->get_var ("slur_height_limit");
522   Real ratio = paper_l_->get_var ("slur_ratio");
523 #else
524   Real staffsize_f = STAFFHEIGHT;
525   Real height_limit = staffsize_f;
526   Real ratio = 1.0/3.0;
527 #endif
528
529   Real alpha = height_limit * 2.0 / pi;
530   Real beta = pi * ratio / (2.0 * height_limit);
531
532   Offset delta (encompass_[encompass_.size () - 1].x () 
533     - encompass_[0].x (), 0);
534   Real b = delta.length ();
535   Real indent = alpha * atan (beta * b);
536   Real height = indent + h;
537  
538 #define RESIZE_ICE
539 #ifndef RESIZE_ICE
540   Array<Offset> control;
541   control.push (Offset (0, 0));
542   control.push (Offset (indent, height));
543   control.push (Offset (b - indent, height));
544   control.push (Offset (b, 0));
545 #else
546   Array<Offset> control (4);
547   control[0] = Offset (0, 0);
548   control[1] = Offset (indent, height);
549   control[2] = Offset (b - indent, height);
550   control[3] = Offset (b, 0);
551 #endif
552   Bezier::set (control);
553 }
554