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