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