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