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