]> git.donarmstrong.com Git - lilypond.git/blob - lily/beam.cc
release: 1.1.51
[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   int stem_max = (int)rint(paper_l ()->get_var ("stem_max"));
324   Real shorten_f = paper_l ()->get_var (String ("forced_stem_shorten"
325                                               + to_str (multiple_i_ <? stem_max)));
326     
327   Real leftx = 0;
328   for (int i=0; i < stems_.size (); i++)
329     {
330       Stem *s = stems_[i];
331       if (s->invisible_b ())
332         continue;
333
334       Stem_info info (s, multiple_i_);
335       if (leftx == 0)
336         leftx = info.x_;
337       info.x_ -= leftx;
338       if (info.dir_ == dir_)
339         {
340           if (forced_count_i == total_count_i)
341             info.idealy_f_ -= shorten_f;
342           else if (forced_count_i > total_count_i / 2)
343             info.idealy_f_ -= shorten_f / 2;
344         }
345       sinfo_.push (info);
346     }
347 }
348
349 void
350 Beam::calculate_slope ()
351 {
352   set_steminfo ();
353   if (!sinfo_.size ())
354     slope_f_ = left_y_ = 0;
355   else if (sinfo_[0].idealy_f_ == sinfo_.top ().idealy_f_)
356     {
357       slope_f_ = 0;
358       left_y_ = sinfo_[0].idealy_f_;
359       left_y_ *= dir_;
360     }
361   else
362     {
363       solve_slope ();
364       Real solved_slope_f = slope_f_;
365
366       /*
367         steep slope running against lengthened stem is suspect
368       */
369       Real dx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
370
371       // urg, these y internote-y-dimensions
372       Real internote_f = stems_[0]->staff_line_leading_f ()/2;
373
374       Real lengthened = paper_l ()->get_var ("beam_lengthened") / internote_f;
375       Real steep = paper_l ()->get_var ("beam_steep_slope") / internote_f;
376       if (((left_y_ - sinfo_[0].idealy_f_ > lengthened)
377            && (slope_f_ > steep))
378           || ((left_y_ + slope_f_ * dx_f - sinfo_.top ().idealy_f_ > lengthened)
379               && (slope_f_ < -steep)))
380         {
381           slope_f_ = 0;
382         }
383
384       /*
385         This neat trick is by Werner Lemberg,
386         damped = tanh (slope_f_)
387         corresponds with some tables in [Wanske]
388       */
389       SCM damp = remove_elt_property (damping_scm_sym);
390       int damping = 1;          // ugh.
391       if (damp!= SCM_BOOL_F)
392         damping = gh_int2scm (SCM_CDR(damp));
393
394       if (damping)
395         slope_f_ = 0.6 * tanh (slope_f_) / damping;
396       
397       quantise_dy ();
398
399       Real damped_slope_dy_f = (solved_slope_f - slope_f_) * dx_f / 2;
400       left_y_ += damped_slope_dy_f;
401
402       left_y_ *= dir_;
403       slope_f_ *= dir_;
404     }
405 }
406
407 void
408 Beam::quantise_dy ()
409 {
410   /*
411     [Ross] (simplification of)
412     Try to set slope_f_ complying with y-span of:
413       - zero
414       - beam_f / 2 + staffline_f / 2
415       - beam_f + staffline_f
416     + n * interline
417     */
418
419   if (quantisation_ <= NONE)
420     return;
421
422   Real interline_f = stems_[0]->staff_line_leading_f ();
423   Real internote_f = interline_f / 2;
424   Real staffline_f = paper_l ()->rule_thickness ();
425   Real beam_f = paper_l ()->beam_thickness_f ();
426
427   Real dx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
428
429   // dim(y) = internote; so slope = (y/internote)/x
430   Real dy_f = dx_f * abs (slope_f_ * internote_f);
431   
432   Real quanty_f = 0.0;
433
434   /* UGR.   ICE in 2.8.1; bugreport filed. */
435   Array<Real> allowed_fraction (3);
436   allowed_fraction[0] = 0;
437   allowed_fraction[1] = (beam_f / 2 + staffline_f / 2);
438   allowed_fraction[2] = (beam_f + staffline_f);
439
440
441   Interval iv = quantise_iv (allowed_fraction, interline_f, dy_f);
442   quanty_f = (dy_f - iv[SMALLER] <= iv[BIGGER] - dy_f)
443     ? iv[SMALLER]
444     : iv[BIGGER];
445
446
447   slope_f_ = (quanty_f / dx_f) / internote_f * sign (slope_f_);
448 }
449
450 static int test_pos = 0;
451
452
453 /*
454   
455   Prevent interference from stafflines and beams.  See Documentation/tex/fonts.doc
456   
457  */
458 void
459 Beam::quantise_left_y (bool extend_b)
460 {
461    /*
462     we only need to quantise the start of the beam as dy is quantised too
463    if extend_b then stems must *not* get shorter
464    */
465
466   if (quantisation_ <= NONE)
467     return;
468
469   /*
470     ----------------------------------------------------------
471                                                    ########
472                                         ########
473                              ########
474     --------------########------------------------------------
475        ########
476
477        hang       straddle   sit        inter      hang
478    */
479
480   Real space = stems_[0]->staff_line_leading_f ();
481   Real internote_f = space /2;
482   Real staffline_f = paper_l ()->rule_thickness ();
483   Real beam_f = paper_l ()->beam_thickness_f ();
484
485   /*
486     [TODO]
487     it would be nice to have all allowed positions in a runtime matrix:
488     (multiplicity, minimum_beam_dy, maximum_beam_dy)
489    */
490
491   Real straddle = 0;
492   Real sit = beam_f / 2 - staffline_f / 2;
493   Real inter = space / 2;
494   Real hang = space - beam_f / 2 + staffline_f / 2;
495
496   /*
497     Put all allowed positions into an array.
498     Whether a position is allowed or not depends on 
499     strictness of quantisation, multiplicity and direction.
500
501     For simplicity, we'll assume dir = UP and correct if 
502     dir = DOWN afterwards.
503    */
504
505   // dim(left_y_) = internote
506   Real dy_f = dir_ * left_y_ * internote_f;
507
508   Real beamdx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
509   Real beamdy_f = beamdx_f * slope_f_ * internote_f;
510
511   Array<Real> allowed_position;
512   if (quantisation_ != TEST)
513     {
514       if (quantisation_ <= NORMAL) 
515         {
516           if ((multiple_i_ <= 2) || (abs (beamdy_f) >= staffline_f / 2))
517             allowed_position.push (straddle);
518           if ((multiple_i_ <= 1) || (abs (beamdy_f) >= staffline_f / 2))
519             allowed_position.push (sit);
520           allowed_position.push (hang);
521         }
522       else
523         // TODO: check and fix TRADITIONAL
524         {
525           if ((multiple_i_ <= 2) || (abs (beamdy_f) >= staffline_f / 2))
526             allowed_position.push (straddle);
527           if ((multiple_i_ <= 1) && (beamdy_f <= staffline_f / 2))
528             allowed_position.push (sit);
529           if (beamdy_f >= -staffline_f / 2)
530             allowed_position.push (hang);
531         }
532     }
533   else
534     {
535       if (test_pos == 0)
536         {
537         allowed_position.push (hang);
538         cout << "hang" << hang << "\n";
539         }
540       else if (test_pos==1)
541         {
542         allowed_position.push (straddle);
543         cout << "straddle" << straddle << endl;
544         }
545       else if (test_pos==2)
546         {
547         allowed_position.push (sit);
548         cout << "sit" << sit << endl;
549         }
550       else if (test_pos==3)
551         {
552         allowed_position.push (inter);
553         cout << "inter" << inter << endl;
554         }
555     }
556
557   Interval iv = quantise_iv (allowed_position, space, dy_f);
558
559   Real quanty_f = dy_f - iv[SMALLER] <= iv[BIGGER] - dy_f ? iv[SMALLER] : iv[BIGGER];
560   if (extend_b)
561     quanty_f = iv[BIGGER];
562
563   // dim(left_y_) = internote
564   left_y_ = dir_ * quanty_f / internote_f;
565 }
566
567 void
568 Beam::set_stemlens ()
569 {
570   Real staffline_f = paper_l ()->rule_thickness ();
571   // enge floots
572   Real epsilon_f = staffline_f / 8;
573
574   Real dy_f = check_stemlengths_f (false);
575   for (int i = 0; i < 2; i++)
576     { 
577       left_y_ += dy_f * dir_;
578       quantise_left_y (dy_f);
579       dy_f = check_stemlengths_f (true);
580       if (abs (dy_f) <= epsilon_f)
581         {
582           break;
583         }
584     }
585
586   test_pos++;
587   test_pos %= 4;
588 }
589
590 /*
591  FIXME
592  ugh.  this is broken and should be rewritten.
593   - [c8. c32 c32]
594  */
595 void
596 Beam::set_grouping (Rhythmic_grouping def, Rhythmic_grouping cur)
597 {
598   def.OK ();
599   cur.OK ();
600   assert (cur.children.size () == stems_.size ());
601
602   cur.split (def);
603
604   Array<int> b;
605   {
606     Array<int> flags;
607     for (int j=0; j <stems_.size (); j++)
608       {
609         Stem *s = stems_[j];
610
611         int f = s->flag_i_ - 2;
612         assert (f>0);
613         flags.push (f);
614       }
615     int fi =0;
616     b= cur.generate_beams (flags, fi);
617     b.insert (0,0);
618     b.push (0);
619     assert (stems_.size () == b.size ()/2);
620   }
621
622   for (int j=0, i=0; i < b.size () && j <stems_.size (); j++)
623     {
624       Stem *s = stems_[j];
625       Direction d = LEFT;
626       do {
627         if (s->beams_i_drul_[d] < 0)
628           s->beams_i_drul_[d] = b[i];
629
630         multiple_i_ = multiple_i_ >? s->beams_i_drul_[d];
631         i++;
632       } while ((flip (&d)) != LEFT);
633     }
634 }
635
636 /*
637   beams to go with one stem.
638   */
639 Molecule
640 Beam::stem_beams (Stem *here, Stem *next, Stem *prev) const
641 {
642   assert (!next || next->hpos_f () > here->hpos_f ());
643   assert (!prev || prev->hpos_f () < here->hpos_f ());
644
645   Real staffline_f = paper_l ()->rule_thickness ();
646   Real interbeam_f = paper_l ()->interbeam_f (multiple_i_);
647
648   Real internote_f = here->staff_line_leading_f ()/2;
649   Real beam_f = paper_l ()->beam_thickness_f ();
650
651   Real dy = interbeam_f;
652   Real stemdx = staffline_f;
653   Real sl = slope_f_* internote_f;
654   lookup_l ()->beam (sl, 20 PT, 1 PT);
655
656   Molecule leftbeams;
657   Molecule rightbeams;
658
659   // UGH
660   Real nw_f = paper_l ()->note_width () * 0.8;
661
662   /* half beams extending to the left. */
663   if (prev)
664     {
665       int lhalfs= lhalfs = here->beams_i_drul_[LEFT] - prev->beams_i_drul_[RIGHT] ;
666       int lwholebeams= here->beams_i_drul_[LEFT] <? prev->beams_i_drul_[RIGHT] ;
667       /*
668        Half beam should be one note-width, 
669        but let's make sure two half-beams never touch
670        */
671       Real w = here->hpos_f () - prev->hpos_f ();
672       w = w/2 <? nw_f;
673       Molecule a;
674       if (lhalfs)               // generates warnings if not
675         a =  lookup_l ()->beam (sl, w, beam_f);
676       a.translate (Offset (-w, -w * sl));
677       for (int j = 0; j  < lhalfs; j++)
678         {
679           Molecule b (a);
680           b.translate_axis (-dir_ * dy * (lwholebeams+j), Y_AXIS);
681           leftbeams.add_molecule (b);
682         }
683     }
684
685   if (next)
686     {
687       int rhalfs = here->beams_i_drul_[RIGHT] - next->beams_i_drul_[LEFT];
688       int rwholebeams = here->beams_i_drul_[RIGHT] <? next->beams_i_drul_[LEFT];
689
690       Real w = next->hpos_f () - here->hpos_f ();
691       Molecule a = lookup_l ()->beam (sl, w + stemdx, beam_f);
692       a.translate_axis( - stemdx/2, X_AXIS);
693       int j = 0;
694       Real gap_f = 0;
695
696       SCM gap = get_elt_property (beam_gap_scm_sym);
697       if (gap != SCM_BOOL_F)
698         {
699           int gap_i = gh_scm2int (gap);
700           int nogap = rwholebeams - gap_i;
701           
702           for (; j  < nogap; j++)
703             {
704               Molecule b (a);
705               b.translate_axis (-dir_ * dy * j, Y_AXIS);
706               rightbeams.add_molecule (b);
707             }
708           // TODO: notehead widths differ for different types
709           gap_f = nw_f / 2;
710           w -= 2 * gap_f;
711           a = lookup_l ()->beam (sl, w + stemdx, beam_f);
712         }
713
714       for (; j  < rwholebeams; j++)
715         {
716           Molecule b (a);
717           b.translate (Offset (gap_f, -dir_ * dy * j));
718           rightbeams.add_molecule (b);
719         }
720
721       w = w/2 <? nw_f;
722       if (rhalfs)
723         a = lookup_l ()->beam (sl, w, beam_f);
724
725       for (; j  < rwholebeams + rhalfs; j++)
726         {
727           Molecule b (a);
728           b.translate_axis (-dir_ * dy * j, Y_AXIS);
729           rightbeams.add_molecule (b);
730         }
731
732     }
733   leftbeams.add_molecule (rightbeams);
734
735   /*
736     Does beam quanting think  of the asymetry of beams? 
737     Refpoint is on bottom of symbol. (FIXTHAT) --hwn.
738    */
739   return leftbeams;
740 }
741