]> git.donarmstrong.com Git - lilypond.git/blob - lily/beam.cc
6e2e3b4641e778d6b0cc4f3e685c1f99f313c31b
[lilypond.git] / lily / beam.cc
1 /*
2   beam.cc -- implement Beam
3
4   source file of the GNU LilyPond music typesetter
5
6   (c)  1997--1998, 1998 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7     Jan Nieuwenhuizen <janneke@gnu.org>
8
9 */
10
11
12 /*
13   [TODO]
14     * centre beam symbol
15     * less hairy code
16     * redo grouping 
17  */
18
19 #include <math.h>
20
21 #include "p-col.hh"
22 #include "array.hh"
23 #include "proto.hh"
24 #include "dimensions.hh"
25 #include "beam.hh"
26 #include "abbreviation-beam.hh"
27 #include "misc.hh"
28 #include "debug.hh"
29 #include "atom.hh"
30 #include "molecule.hh"
31 #include "leastsquares.hh"
32 #include "stem.hh"
33 #include "paper-def.hh"
34 #include "lookup.hh"
35 #include "grouping.hh"
36
37 Beam::Beam ()
38 {
39   slope_f_ = 0;
40   left_y_ = 0;
41   damping_i_ = 1;
42   quantisation_ = NORMAL;
43   multiple_i_ = 0;
44 }
45
46 void
47 Beam::add_stem (Stem*s)
48 {
49   stems_.push (s);
50   s->add_dependency (this);
51   s->beam_l_ = this;
52
53   if (!spanned_drul_[LEFT])
54     set_bounds (LEFT,s);
55   else
56     set_bounds (RIGHT,s);
57 }
58
59 Molecule*
60 Beam::do_brew_molecule_p () const
61 {
62   Molecule *mol_p = new Molecule;
63   Real internote_f = paper ()->internote_f ();
64
65   Real x0 = stems_[0]->hpos_f ();
66   for (int j=0; j <stems_.size (); j++)
67     {
68       Stem *i = stems_[j];
69       Stem * prev = (j > 0)? stems_[j-1] : 0;
70       Stem * next = (j < stems_.size ()-1) ? stems_[j+1] :0;
71
72       Molecule sb = stem_beams (i, next, prev);
73       Real  x = i->hpos_f ()-x0;
74       sb.translate (Offset (x, (x * slope_f_ + left_y_) * internote_f));
75       mol_p->add_molecule (sb);
76     }
77   mol_p->translate_axis (x0 
78     - spanned_drul_[LEFT]->absolute_coordinate (X_AXIS), X_AXIS);
79
80   return mol_p;
81 }
82
83 Offset
84 Beam::center () const
85 {
86   Real w= (paper ()->note_width () + extent (X_AXIS).length ())/2.0;
87   return Offset (w, (left_y_ + w* slope_f_)*paper ()->internote_f ());
88 }
89
90 void
91 Beam::do_pre_processing ()
92 {
93   if (!dir_)
94     set_default_dir ();
95 }
96
97 void
98 Beam::do_print () const
99 {
100 #ifndef NPRINT
101   DOUT << "slope_f_ " << slope_f_ << "left ypos " << left_y_;
102   Spanner::do_print ();
103 #endif
104 }
105
106 void
107 Beam::do_post_processing ()
108 {
109   if (stems_.size () < 2)
110     {
111       warning (_ ("beam with less than two stems"));
112       transparent_b_ = true;
113       return ;
114     }
115   calculate_slope ();
116   set_stemlens ();
117 }
118
119 void
120 Beam::do_substitute_dependent (Score_element*o,Score_element*n)
121 {
122   if (Stem * os = dynamic_cast<Stem*> (o))
123     stems_.substitute (os,
124                        dynamic_cast<Stem *> (n));
125 }
126
127 Interval
128 Beam::do_width () const
129 {
130   return Interval (stems_[0]->hpos_f (),
131                    stems_.top ()->hpos_f ());
132 }
133
134 void
135 Beam::set_default_dir ()
136 {
137   Drul_array<int> total;
138   total[UP]  = total[DOWN] = 0;
139   Drul_array<int> count; 
140   count[UP]  = count[DOWN] = 0;
141   Direction d = DOWN;
142   
143   for (int i=0; i <stems_.size (); i++)
144     do {
145       Stem *s = stems_[i];
146       int current = s->dir_ 
147         ? (1 + d * s->dir_)/2
148         : s->get_center_distance ((Direction)-d);
149
150       if (current)
151         {
152           total[d] += current;
153           count[d] ++;
154         }
155
156     } while (flip(&d) != DOWN);
157   
158   /* 
159      [Ross] states that the majority of the notes dictates the
160      direction (and not the mean of "center distance")
161
162      But is that because it really looks better, or because he
163      wants to provide some real simple hands-on rules.
164      
165      We have our doubts, so we simply provide all sensible alternatives.
166   */
167
168   Dir_algorithm a = (Dir_algorithm)rint(paper ()->get_var ("beam_dir_algorithm"));
169   switch (a)
170     {
171     case MAJORITY:
172       dir_ = (count[UP] > count[DOWN]) ? UP : DOWN;
173       break;
174     case MEAN:
175       // mean centre distance
176       dir_ = (total[UP] > total[DOWN]) ? UP : DOWN;
177       break;
178     default:
179     case MEDIAN:
180       // median centre distance
181       if (!count[UP])
182         dir_ = DOWN;
183       else if (!count[DOWN])
184         dir_ = UP;
185       else
186         dir_ = (total[UP] / count[UP] > total[DOWN] / count[DOWN]) ? UP : DOWN;
187       break;
188     }
189
190   for (int i=0; i <stems_.size (); i++)
191     {
192       Stem *s = stems_[i];
193       s->beam_dir_ = dir_;
194       if (!s->dir_forced_b_)
195         s->dir_ = dir_;
196     }
197 }
198
199 /*
200   See Documentation/tex/fonts.doc
201  */
202
203 void
204 Beam::solve_slope ()
205 {
206   /*
207     should use minimum energy formulation (cf linespacing)
208   */
209   assert (sinfo_.size () > 1);
210   DOUT << "Beam::solve_slope: \n";
211
212   Least_squares l;
213   for (int i=0; i < sinfo_.size (); i++)
214     {
215       l.input.push (Offset (sinfo_[i].x_, sinfo_[i].idealy_f_));
216     }
217   l.minimise (slope_f_, left_y_);
218 }
219
220 Real
221 Beam::check_stemlengths_f (bool set_b)
222 {
223   Real interbeam_f = paper ()->interbeam_f (multiple_i_);
224   Real internote_f = paper ()->internote_f (); 
225   Real beam_f = paper ()->beam_thickness_f ();
226   Real staffline_f = paper ()->rule_thickness ();
227   Real epsilon_f = staffline_f / 8;
228   Real dy_f = 0.0;
229   for (int i=0; i < sinfo_.size (); i++)
230     {
231       Real y = sinfo_[i].x_ * slope_f_ + left_y_;
232       // correct for knee
233       if (dir_ != sinfo_[i].dir_)
234         y -= dir_ * (beam_f / 2
235                       + (sinfo_[i].mult_i_ - 1) * interbeam_f) / internote_f;
236
237       if (set_b)
238         sinfo_[i].stem_l_->set_stemend (y);
239         
240       y *= dir_;
241       if (y > sinfo_[i].maxy_f_)
242         dy_f = dy_f <? sinfo_[i].maxy_f_ - y;
243       if (y < sinfo_[i].miny_f_)
244         { 
245           // when all too short, normal stems win..
246           if (dy_f < -epsilon_f)
247             warning (_ ("weird beam shift, check your knees"));
248           dy_f = dy_f >? sinfo_[i].miny_f_ - y;
249         }
250     }
251   return dy_f;
252 }
253
254 void
255 Beam::set_steminfo ()
256 {
257   assert (multiple_i_);
258   int total_count_i = 0;
259   int forced_count_i = 0;
260   for (int i=0; i < stems_.size (); i++)
261     {
262       Stem *s = stems_[i];
263       s->mult_i_ = multiple_i_;
264       s->set_default_extents ();
265       if (s->invisible_b ())
266         continue;
267       if (((int)s->chord_start_f ()) && (s->dir_ != s->get_default_dir ()))
268         forced_count_i++;
269       total_count_i++;
270     }
271
272   Real internote_f = paper ()->internote_f ();
273   int stem_max = (int)rint(paper ()->get_var ("stem_max"));
274   Real shorten_f = paper ()->get_var (String ("forced_stem_shorten"
275                                               + to_str (multiple_i_ <? stem_max)))
276     / internote_f;
277     
278   Real leftx = 0;
279   for (int i=0; i < stems_.size (); i++)
280     {
281       Stem *s = stems_[i];
282       if (s->invisible_b ())
283         continue;
284
285       Stem_info info (s);
286       if (leftx == 0)
287         leftx = info.x_;
288       info.x_ -= leftx;
289       if (info.dir_ == dir_)
290         {
291           if (forced_count_i == total_count_i)
292             info.idealy_f_ -= shorten_f;
293           else if (forced_count_i > total_count_i / 2)
294             info.idealy_f_ -= shorten_f / 2;
295         }
296       sinfo_.push (info);
297     }
298 }
299
300 void
301 Beam::calculate_slope ()
302 {
303   set_steminfo ();
304   if (!sinfo_.size ())
305     slope_f_ = left_y_ = 0;
306   else if (sinfo_[0].idealy_f_ == sinfo_.top ().idealy_f_)
307     {
308       slope_f_ = 0;
309       left_y_ = sinfo_[0].idealy_f_;
310       left_y_ *= dir_;
311     }
312   else
313     {
314       solve_slope ();
315       Real solved_slope_f = slope_f_;
316
317       /*
318         steep slope running against lengthened stem is suspect
319       */
320       Real dx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
321
322       // urg, these y internote-y-dimensions
323       Real internote_f = paper ()->internote_f ();
324       Real lengthened = paper ()->get_var ("beam_lengthened") / internote_f;
325       Real steep = paper ()->get_var ("beam_steep_slope") / internote_f;
326       if (((left_y_ - sinfo_[0].idealy_f_ > lengthened)
327            && (slope_f_ > steep))
328           || ((left_y_ + slope_f_ * dx_f - sinfo_.top ().idealy_f_ > lengthened)
329               && (slope_f_ < -steep)))
330         {
331           slope_f_ = 0;
332         }
333
334       /*
335         This neat trick is by Werner Lemberg,
336         damped = tanh (slope_f_)
337         corresponds with some tables in [Wanske]
338       */
339       if (damping_i_)
340         slope_f_ = 0.6 * tanh (slope_f_) / damping_i_;
341       
342       quantise_dy ();
343
344       Real damped_slope_dy_f = (solved_slope_f - slope_f_) * dx_f / 2;
345       left_y_ += damped_slope_dy_f;
346
347       left_y_ *= dir_;
348       slope_f_ *= dir_;
349     }
350 }
351
352 void
353 Beam::quantise_dy ()
354 {
355   /*
356     [Ross] (simplification of)
357     Try to set slope_f_ complying with y-span of:
358       - zero
359       - beam_f / 2 + staffline_f / 2
360       - beam_f + staffline_f
361     + n * interline
362     */
363
364   if (quantisation_ <= NONE)
365     return;
366
367   Real interline_f = paper ()->interline_f ();
368   Real internote_f = interline_f / 2;
369   Real staffline_f = paper ()->rule_thickness ();
370   Real beam_f = paper ()->beam_thickness_f ();
371
372   Real dx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
373
374   // dim(y) = internote; so slope = (y/internote)/x
375   Real dy_f = dx_f * abs (slope_f_ * internote_f);
376   
377   Real quanty_f = 0.0;
378
379   /* UGR.   ICE in 2.8.1; bugreport filed. */
380   Array<Real> allowed_fraction (3);
381   allowed_fraction[0] = 0;
382   allowed_fraction[1] = (beam_f / 2 + staffline_f / 2);
383   allowed_fraction[2] = (beam_f + staffline_f);
384
385
386   Interval iv = quantise_iv (allowed_fraction, interline_f, dy_f);
387   quanty_f = (dy_f - iv.min () <= iv.max () - dy_f)
388     ? iv.min ()
389     : iv.max ();
390
391
392   slope_f_ = (quanty_f / dx_f) / internote_f * sign (slope_f_);
393 }
394
395 static int test_pos = 0;
396
397
398 /*
399   
400   Prevent interference from stafflines and beams.  See Documentation/tex/fonts.doc
401   
402  */
403 void
404 Beam::quantise_left_y (bool extend_b)
405 {
406    /*
407     we only need to quantise the start of the beam as dy is quantised too
408    if extend_b then stems must *not* get shorter
409    */
410
411   if (quantisation_ <= NONE)
412     return;
413
414   /*
415     ----------------------------------------------------------
416                                                    ########
417                                         ########
418                              ########
419     --------------########------------------------------------
420        ########
421
422        hang       straddle   sit        inter      hang
423    */
424
425   Real interline_f = paper ()->interline_f ();
426   Real internote_f = paper ()->internote_f ();
427   Real staffline_f = paper ()->rule_thickness ();
428   Real beam_f = paper ()->beam_thickness_f ();
429
430   /*
431     [TODO]
432     it would be nice to have all allowed positions in a runtime matrix:
433     (multiplicity, minimum_beam_dy, maximum_beam_dy)
434    */
435
436   Real straddle = 0;
437   Real sit = beam_f / 2 - staffline_f / 2;
438   Real inter = interline_f / 2;
439   Real hang = interline_f - beam_f / 2 + staffline_f / 2;
440
441   /*
442     Put all allowed positions into an array.
443     Whether a position is allowed or not depends on 
444     strictness of quantisation, multiplicity and direction.
445
446     For simplicity, we'll assume dir = UP and correct if 
447     dir = DOWN afterwards.
448    */
449
450   // dim(left_y_) = internote
451   Real dy_f = dir_ * left_y_ * internote_f;
452
453   Real beamdx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
454   Real beamdy_f = beamdx_f * slope_f_ * internote_f;
455
456   Array<Real> allowed_position;
457   if (quantisation_ != TEST)
458     {
459       if (quantisation_ <= NORMAL) 
460         {
461           if ((multiple_i_ <= 2) || (abs (beamdy_f) >= staffline_f / 2))
462             allowed_position.push (straddle);
463           if ((multiple_i_ <= 1) || (abs (beamdy_f) >= staffline_f / 2))
464             allowed_position.push (sit);
465           allowed_position.push (hang);
466         }
467       else
468         // TODO: check and fix TRADITIONAL
469         {
470           if ((multiple_i_ <= 2) || (abs (beamdy_f) >= staffline_f / 2))
471             allowed_position.push (straddle);
472           if ((multiple_i_ <= 1) && (beamdy_f <= staffline_f / 2))
473             allowed_position.push (sit);
474           if (beamdy_f >= -staffline_f / 2)
475             allowed_position.push (hang);
476         }
477     }
478   else
479     {
480       if (test_pos == 0)
481         {
482         allowed_position.push (hang);
483         cout << "hang" << hang << "\n";
484         }
485       else if (test_pos==1)
486         {
487         allowed_position.push (straddle);
488         cout << "straddle" << straddle << endl;
489         }
490       else if (test_pos==2)
491         {
492         allowed_position.push (sit);
493         cout << "sit" << sit << endl;
494         }
495       else if (test_pos==3)
496         {
497         allowed_position.push (inter);
498         cout << "inter" << inter << endl;
499         }
500     }
501
502   Interval iv = quantise_iv (allowed_position, interline_f, dy_f);
503
504   Real quanty_f = dy_f - iv.min () <= iv.max () - dy_f ? iv.min () : iv.max ();
505   if (extend_b)
506     quanty_f = iv.max ();
507
508   // dim(left_y_) = internote
509   left_y_ = dir_ * quanty_f / internote_f;
510 }
511
512 void
513 Beam::set_stemlens ()
514 {
515   Real staffline_f = paper ()->rule_thickness ();
516   // enge floots
517   Real epsilon_f = staffline_f / 8;
518
519   DOUT << "Beam::set_stemlens: \n";
520   Real dy_f = check_stemlengths_f (false);
521   for (int i = 0; i < 2; i++)
522     { 
523       left_y_ += dy_f * dir_;
524       quantise_left_y (dy_f);
525       dy_f = check_stemlengths_f (true);
526       if (abs (dy_f) <= epsilon_f)
527         {
528           DOUT << "Beam::set_stemlens: " << i << " iterations\n";
529           break;
530         }
531     }
532
533   test_pos++;
534   test_pos %= 4;
535 }
536
537 /*
538  FIXME
539  ugh.  this is broken and should be rewritten.
540   - [c8. c32 c32]
541  */
542 void
543 Beam::set_grouping (Rhythmic_grouping def, Rhythmic_grouping cur)
544 {
545   def.OK ();
546   cur.OK ();
547   assert (cur.children.size () == stems_.size ());
548
549   cur.split (def);
550
551   Array<int> b;
552   {
553     Array<int> flags;
554     for (int j=0; j <stems_.size (); j++)
555       {
556         Stem *s = stems_[j];
557
558         int f = s->flag_i_ - 2;
559         assert (f>0);
560         flags.push (f);
561       }
562     int fi =0;
563     b= cur.generate_beams (flags, fi);
564     b.insert (0,0);
565     b.push (0);
566     assert (stems_.size () == b.size ()/2);
567   }
568
569   for (int j=0, i=0; i < b.size () && j <stems_.size (); j++)
570     {
571       Stem *s = stems_[j];
572       Direction d = LEFT;
573       do {
574         if (s->beams_i_drul_[d] < 0)
575           s->beams_i_drul_[d] = b[i];
576
577         multiple_i_ = multiple_i_ >? s->beams_i_drul_[d];
578         i++;
579       } while ((flip (&d)) != LEFT);
580     }
581 }
582
583 /*
584   beams to go with one stem.
585   */
586 Molecule
587 Beam::stem_beams (Stem *here, Stem *next, Stem *prev) const
588 {
589   assert (!next || next->hpos_f () > here->hpos_f ());
590   assert (!prev || prev->hpos_f () < here->hpos_f ());
591
592   Real staffline_f = paper ()->rule_thickness ();
593   Real interbeam_f = paper ()->interbeam_f (multiple_i_);
594   Real internote_f = paper ()->internote_f (); 
595   Real beam_f = paper ()->beam_thickness_f ();
596
597   Real dy = interbeam_f;
598   Real stemdx = staffline_f;
599   Real sl = slope_f_* internote_f;
600   lookup_l ()->beam (sl, 20 PT, 1 PT);
601
602   Molecule leftbeams;
603   Molecule rightbeams;
604
605   // UGH
606   Real nw_f = paper ()->note_width () * 0.8;
607
608   /* half beams extending to the left. */
609   if (prev)
610     {
611       int lhalfs= lhalfs = here->beams_i_drul_[LEFT] - prev->beams_i_drul_[RIGHT] ;
612       int lwholebeams= here->beams_i_drul_[LEFT] <? prev->beams_i_drul_[RIGHT] ;
613       /*
614        Half beam should be one note-width, 
615        but let's make sure two half-beams never touch
616        */
617       Real w = here->hpos_f () - prev->hpos_f ();
618       w = w/2 <? nw_f;
619       Atom a;
620       if (lhalfs)               // generates warnings if not
621         a =  lookup_l ()->beam (sl, w, beam_f);
622       a.translate (Offset (-w, -w * sl));
623       for (int j = 0; j  < lhalfs; j++)
624         {
625           Atom b (a);
626           b.translate_axis (-dir_ * dy * (lwholebeams+j), Y_AXIS);
627           leftbeams.add_atom (b);
628         }
629     }
630
631   if (next)
632     {
633       int rhalfs = here->beams_i_drul_[RIGHT] - next->beams_i_drul_[LEFT];
634       int rwholebeams = here->beams_i_drul_[RIGHT] <? next->beams_i_drul_[LEFT];
635
636       Real w = next->hpos_f () - here->hpos_f ();
637       Atom a = lookup_l ()->beam (sl, w + stemdx, beam_f);
638       a.translate_axis( - stemdx/2, X_AXIS);
639       int j = 0;
640       Real gap_f = 0;
641       if (here->beam_gap_i_)
642         {
643           int nogap = rwholebeams - here->beam_gap_i_;
644           for (; j  < nogap; j++)
645             {
646               Atom b (a);
647               b.translate_axis (-dir_ * dy * j, Y_AXIS);
648               rightbeams.add_atom (b);
649             }
650           // TODO: notehead widths differ for different types
651           gap_f = nw_f / 2;
652           w -= 2 * gap_f;
653           a = lookup_l ()->beam (sl, w + stemdx, beam_f);
654         }
655
656       for (; j  < rwholebeams; j++)
657         {
658           Atom b (a);
659           b.translate (Offset (gap_f, -dir_ * dy * j));
660           rightbeams.add_atom (b);
661         }
662
663       w = w/2 <? nw_f;
664       if (rhalfs)
665         a = lookup_l ()->beam (sl, w, beam_f);
666
667       for (; j  < rwholebeams + rhalfs; j++)
668         {
669           Atom b (a);
670           b.translate_axis (-dir_ * dy * j, Y_AXIS);
671           rightbeams.add_atom (b);
672         }
673
674     }
675   leftbeams.add_molecule (rightbeams);
676
677   /*
678     Does beam quanting think  of the asymetry of beams? 
679     Refpoint is on bottom of symbol. (FIXTHAT) --hwn.
680    */
681   return leftbeams;
682 }
683