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