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