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