]> git.donarmstrong.com Git - lilypond.git/blob - lily/beam.cc
release: 1.1.39
[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 centre distance
207       beamdir = (total[UP] > total[DOWN]) ? UP : DOWN;
208       break;
209     default:
210     case MEDIAN:
211       // median centre 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   Real internote_f = stems_[0]->staff_line_leading_f ()/2;
324   int stem_max = (int)rint(paper_l ()->get_var ("stem_max"));
325   Real shorten_f = paper_l ()->get_var (String ("forced_stem_shorten"
326                                               + to_str (multiple_i_ <? stem_max)))
327     / internote_f;
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   assert (!next || next->hpos_f () > here->hpos_f ());
645   assert (!prev || prev->hpos_f () < here->hpos_f ());
646
647   Real staffline_f = paper_l ()->rule_thickness ();
648   Real interbeam_f = paper_l ()->interbeam_f (multiple_i_);
649
650   Real internote_f = here->staff_line_leading_f ()/2;
651   Real beam_f = paper_l ()->beam_thickness_f ();
652
653   Real dy = interbeam_f;
654   Real stemdx = staffline_f;
655   Real sl = slope_f_* internote_f;
656   lookup_l ()->beam (sl, 20 PT, 1 PT);
657
658   Molecule leftbeams;
659   Molecule rightbeams;
660
661   // UGH
662   Real nw_f = paper_l ()->note_width () * 0.8;
663
664   /* half beams extending to the left. */
665   if (prev)
666     {
667       int lhalfs= lhalfs = here->beams_i_drul_[LEFT] - prev->beams_i_drul_[RIGHT] ;
668       int lwholebeams= here->beams_i_drul_[LEFT] <? prev->beams_i_drul_[RIGHT] ;
669       /*
670        Half beam should be one note-width, 
671        but let's make sure two half-beams never touch
672        */
673       Real w = here->hpos_f () - prev->hpos_f ();
674       w = w/2 <? nw_f;
675       Molecule a;
676       if (lhalfs)               // generates warnings if not
677         a =  lookup_l ()->beam (sl, w, beam_f);
678       a.translate (Offset (-w, -w * sl));
679       for (int j = 0; j  < lhalfs; j++)
680         {
681           Molecule b (a);
682           b.translate_axis (-dir_ * dy * (lwholebeams+j), Y_AXIS);
683           leftbeams.add_molecule (b);
684         }
685     }
686
687   if (next)
688     {
689       int rhalfs = here->beams_i_drul_[RIGHT] - next->beams_i_drul_[LEFT];
690       int rwholebeams = here->beams_i_drul_[RIGHT] <? next->beams_i_drul_[LEFT];
691
692       Real w = next->hpos_f () - here->hpos_f ();
693       Molecule a = lookup_l ()->beam (sl, w + stemdx, beam_f);
694       a.translate_axis( - stemdx/2, X_AXIS);
695       int j = 0;
696       Real gap_f = 0;
697
698       SCM gap = get_elt_property (beam_gap_scm_sym);
699       if (gap != SCM_BOOL_F)
700         {
701           int gap_i = gh_scm2int (gap);
702           int nogap = rwholebeams - gap_i;
703           
704           for (; j  < nogap; j++)
705             {
706               Molecule b (a);
707               b.translate_axis (-dir_ * dy * j, Y_AXIS);
708               rightbeams.add_molecule (b);
709             }
710           // TODO: notehead widths differ for different types
711           gap_f = nw_f / 2;
712           w -= 2 * gap_f;
713           a = lookup_l ()->beam (sl, w + stemdx, beam_f);
714         }
715
716       for (; j  < rwholebeams; j++)
717         {
718           Molecule b (a);
719           b.translate (Offset (gap_f, -dir_ * dy * j));
720           rightbeams.add_molecule (b);
721         }
722
723       w = w/2 <? nw_f;
724       if (rhalfs)
725         a = lookup_l ()->beam (sl, w, beam_f);
726
727       for (; j  < rwholebeams + rhalfs; j++)
728         {
729           Molecule b (a);
730           b.translate_axis (-dir_ * dy * j, Y_AXIS);
731           rightbeams.add_molecule (b);
732         }
733
734     }
735   leftbeams.add_molecule (rightbeams);
736
737   /*
738     Does beam quanting think  of the asymetry of beams? 
739     Refpoint is on bottom of symbol. (FIXTHAT) --hwn.
740    */
741   return leftbeams;
742 }
743