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