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