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