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