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