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