]> git.donarmstrong.com Git - lilypond.git/blob - lily/beam.cc
patch::: 1.1.18.jcn4: vixje
[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.minimise (slope_f_, left_y_);
238
239   solved_slope_f_ = dir_ * slope_f_;
240
241   /*
242     This neat trick is by Werner Lemberg, damped = tanh (slope_f_) corresponds
243     with some tables in [Wanske]
244     */
245   if (damping_i_)
246     slope_f_ = 0.6 * tanh (slope_f_) / damping_i_;
247
248   /* 
249     [TODO]
250     think
251
252     dropping lq for stemlengths solves [d d d] [d g d] "bug..."
253
254     but may be a bit too crude, and result in lots of 
255     too high beams...
256
257     perhaps only if slope = 0 ?
258     */
259
260   if (abs (slope_f_) < epsilon_f)
261     left_y_ = (sinfo[0].idealy_f_ + sinfo.top ().idealy_f_) / 2;
262   else
263     /* 
264       symmetrical, but results often in having stemlength = minimal 
265
266     left_y_ = sinfo[0].dir_ == dir_ ? sinfo[0].miny_f_ : sinfo[0].maxy_f_;
267
268       what about
269     */
270     {
271       Real dx = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
272       if (sinfo[0].dir_ == sinfo.top ().dir_)
273         left_y_ = sinfo[0].idealy_f_ >? sinfo.top ().idealy_f_ - slope_f_ * dx; 
274       // knee
275       else
276         left_y_ = sinfo[0].idealy_f_;
277     }
278 }
279
280 Real
281 Beam::check_stemlengths_f (Array<Stem_info>& sinfo)
282 {
283   /*
284    find shortest stem and adjust left_y accordingly
285    */
286   Real dy = 0.0;
287   for (int i=0; i < sinfo.size (); i++)
288     {
289       Real y = sinfo[i].x_ * slope_f_ + left_y_;
290       Real my = sinfo[i].miny_f_;
291
292       if (my - y > dy)
293         dy = my -y;
294     }
295   return dy;
296 }
297
298 void
299 Beam::calculate_slope ()
300 {
301   Real interline_f = paper ()->interline_f ();
302   Real staffline_f = paper ()->rule_thickness ();
303   Real epsilon_f = staffline_f / 8;
304
305   assert (multiple_i_);
306   Array<Stem_info> sinfo;
307   for (int i=0; i < stems_.size (); i++)
308     {
309       Stem *s = stems_[i];
310
311       s->mult_i_ = multiple_i_;
312       s->set_default_extents ();
313       if (s->invisible_b ())
314         continue;
315
316       Stem_info info (s);
317       sinfo.push (info);
318     }
319
320   if (! sinfo.size ())
321     slope_f_ = left_y_ = 0;
322   else if (sinfo.size () == 1)
323     {
324       slope_f_ = 0;
325       left_y_ = sinfo[0].idealy_f_;
326     }
327   else
328     {
329       Real y;
330       Real s;
331       for (int i = 0; i < 3; i++)
332         {
333           y = left_y_;
334           solve_slope (sinfo);
335           Real dy = check_stemlengths_f (sinfo);
336           left_y_ += dy;
337
338           // only consider recalculation if long stem adjustments
339           if (!i && (left_y_ - sinfo[0].idealy_f_ < 0.5 * interline_f))
340             break;
341         
342           if (!i)
343             s = slope_f_;
344           // never allow slope to tilt the other way
345           else if (sign (slope_f_) != sign (s))
346             {
347               left_y_ = 0;
348               slope_f_ = 0;
349               Real dy = check_stemlengths_f (sinfo);
350               left_y_ += dy;
351               break;
352             }
353           // or become steeper
354           else if (abs (slope_f_) > abs (s))
355             {
356               slope_f_ = s;
357               Real dy = check_stemlengths_f (sinfo);
358               left_y_ += dy;
359               break;
360             }
361           if (abs (dy) < epsilon_f)
362             break;
363         }
364     }
365
366   left_y_ *= dir_;
367   slope_f_ *= dir_;
368
369   quantise_dy ();
370 }
371
372 void
373 Beam::quantise_dy ()
374 {
375   /*
376     [Ross] (simplification of)
377     Try to set slope_f_ complying with y-span of:
378       - zero
379       - beam_f / 2 + staffline_f / 2
380       - beam_f + staffline_f
381     + n * interline
382     */
383
384   if (quantisation_ <= NONE)
385     return;
386
387   Real interline_f = paper ()->interline_f ();
388   Real internote_f = interline_f / 2;
389   Real staffline_f = paper ()->rule_thickness ();
390   Real beam_f = paper ()->beam_thickness_f ();
391
392   Real dx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
393
394   // dim(y) = internote; so slope = (y/internote)/x
395   Real dy_f = dx_f * abs (slope_f_ * internote_f);
396   
397   Real quanty_f = 0.0;
398
399   /* UGR.   ICE in 2.8.1; bugreport filed. */
400   Array<Real> allowed_fraction (3);
401   allowed_fraction[0] = 0;
402   allowed_fraction[1] = (beam_f / 2 + staffline_f / 2);
403   allowed_fraction[2] = (beam_f + staffline_f);
404
405
406   Interval iv = quantise_iv (allowed_fraction, interline_f, dy_f);
407   quanty_f = (dy_f - iv.min () <= iv.max () - dy_f)
408     ? iv.min ()
409     : iv.max ();
410
411
412   slope_f_ = (quanty_f / dx_f) / internote_f * sign (slope_f_);
413 }
414
415 static int test_pos = 0;
416
417
418 /*
419   
420   Prevent interference from stafflines and beams.  See Documentation/tex/fonts.doc
421   
422  */
423 void
424 Beam::quantise_left_y (bool extend_b)
425 {
426    /*
427     we only need to quantise the start of the beam as dy is quantised too
428    if extend_b then stems must *not* get shorter
429    */
430
431   if (quantisation_ <= NONE)
432     return;
433
434   /*
435     ----------------------------------------------------------
436                                                    ########
437                                         ########
438                              ########
439     --------------########------------------------------------
440        ########
441
442        hang       straddle   sit        inter      hang
443    */
444
445   Real interline_f = paper ()->interline_f ();
446   Real internote_f = paper ()->internote_f ();
447   Real staffline_f = paper ()->rule_thickness ();
448   Real beam_f = paper ()->beam_thickness_f ();
449
450   /*
451     [TODO]
452     it would be nice to have all allowed positions in a runtime matrix:
453     (multiplicity, minimum_beam_dy, maximum_beam_dy)
454    */
455
456   Real straddle = 0;
457   Real sit = beam_f / 2 - staffline_f / 2;
458   Real inter = interline_f / 2;
459   Real hang = interline_f - beam_f / 2 + staffline_f / 2;
460
461   /*
462     Put all allowed positions into an array.
463     Whether a position is allowed or not depends on 
464     strictness of quantisation, multiplicity and direction.
465
466     For simplicity, we'll assume dir = UP and correct if 
467     dir = DOWN afterwards.
468    */
469
470   // dim(left_y_) = internote
471   Real dy_f = dir_ * left_y_ * internote_f;
472
473   Real beamdx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
474   Real beamdy_f = beamdx_f * slope_f_ * internote_f;
475
476   Array<Real> allowed_position;
477   if (quantisation_ != TEST)
478     {
479       if (quantisation_ <= NORMAL) 
480         {
481           if ((multiple_i_ <= 2) || (abs (beamdy_f) >= staffline_f / 2))
482             allowed_position.push (straddle);
483           if ((multiple_i_ <= 1) || (abs (beamdy_f) >= staffline_f / 2))
484             allowed_position.push (sit);
485           allowed_position.push (hang);
486         }
487       else
488         // TODO: check and fix TRADITIONAL
489         {
490           if ((multiple_i_ <= 2) || (abs (beamdy_f) >= staffline_f / 2))
491             allowed_position.push (straddle);
492           if ((multiple_i_ <= 1) && (beamdy_f <= staffline_f / 2))
493             allowed_position.push (sit);
494           if (beamdy_f >= -staffline_f / 2)
495             allowed_position.push (hang);
496         }
497     }
498   else
499     {
500       if (test_pos == 0)
501         {
502         allowed_position.push (hang);
503         cout << "hang" << hang << endl;
504         }
505       else if (test_pos==1)
506         {
507         allowed_position.push (straddle);
508         cout << "straddle" << straddle << endl;
509         }
510       else if (test_pos==2)
511         {
512         allowed_position.push (sit);
513         cout << "sit" << sit << endl;
514         }
515       else if (test_pos==3)
516         {
517         allowed_position.push (inter);
518         cout << "inter" << inter << endl;
519         }
520     }
521
522 #if 0
523   // this currently never happens
524   Real q = (dy_f / interline_f - dy_i) * interline_f;
525   if ((quantisation_ < NORMAL) && (q < interline_f / 3 - beam_f / 2))
526     allowed_position.push (inter);
527 #endif
528
529   Interval iv = quantise_iv (allowed_position, interline_f, dy_f);
530
531   Real quanty_f = dy_f - iv.min () <= iv.max () - dy_f ? iv.min () : iv.max ();
532   if (extend_b)
533     quanty_f = iv.max ();
534
535   // dim(left_y_) = internote
536   left_y_ = dir_ * quanty_f / internote_f;
537 }
538
539 void
540 Beam::set_stemlens ()
541 {
542   Real staffline_f = paper ()->rule_thickness ();
543   Real interbeam_f = paper ()->interbeam_f (multiple_i_);
544   Real internote_f = paper ()->internote_f (); 
545   Real beam_f = paper ()->beam_thickness_f ();
546
547   // enge floots
548   Real epsilon_f = staffline_f / 8;
549
550   /* 
551
552    Damped and quantised slopes, esp. in monotone scales such as
553
554       [c d e f g a b c]
555
556    will soon produce the minimal stem-length for one of the extreme 
557    stems, which is wrong (and ugly).  The minimum stemlength should
558    be kept rather small, in order to handle extreme beaming, such as
559
560       [c c' 'c]  %assuming no knee
561       
562    correctly.
563    To avoid these short stems for normal cases, we'll correct for
564    the loss in slope, if necessary.
565
566    [TODO]
567    ugh, another hack.  who's next?
568    Writing this all down, i realise (at last) that the Right Thing to
569    do is to assign uglyness to slope and stem-lengths and then minimise
570    the total uglyness of a beam.
571    Steep slopes are ugly, shortened stems are ugly, lengthened stems
572    are ugly.
573    How to do this?
574    
575    */
576
577   Real dx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
578   Real damp_correct_f = paper ()->get_var ("beam_slope_damp_correct_factor");
579   Real damped_slope_dy_f = (solved_slope_f_ - slope_f_) * dx_f
580     * sign (slope_f_);
581   damped_slope_dy_f *= damp_correct_f;
582   if (damped_slope_dy_f <= epsilon_f)
583     damped_slope_dy_f = 0;
584
585   DOUT << "Beam::set_stemlens: \n";
586   Real x0 = stems_[0]->hpos_f ();
587   Real dy_f = 0;
588   // urg
589   for (int jj = 0; jj < 10; jj++)
590     { 
591       left_y_ += dy_f * dir_;
592       quantise_left_y (dy_f);
593       dy_f = 0;
594       for (int i=0; i < stems_.size (); i++)
595         {
596           Stem *s = stems_[i];
597           if (s->transparent_b_)
598             continue;
599
600           Real x = s->hpos_f () - x0;
601           // urg move this to stem-info
602           Real sy = left_y_ + slope_f_ * x;
603           if (dir_ != s->dir_)
604             sy -= dir_ * (beam_f / 2
605               + (s->mult_i_ - 1) * interbeam_f) / internote_f;
606           s->set_stemend (sy);
607           Real y = s->stem_end_f () * dir_;
608           Stem_info info (s);
609           if (y > info.maxy_f_)
610             dy_f = dy_f <? info.maxy_f_ - y;
611           if (y < info.miny_f_)
612             { 
613               // when all too short, normal stems win..
614               if (dy_f < -epsilon_f)
615                 warning (_ ("weird beam shift, check your knees"));
616               dy_f = dy_f >? info.miny_f_ - y;
617             }
618         }
619       if (damped_slope_dy_f && (dy_f >= 0))
620         dy_f += damped_slope_dy_f;
621       damped_slope_dy_f = 0;
622       if (abs (dy_f) <= epsilon_f)
623         {
624           DOUT << "Beam::set_stemlens: " << jj << " iterations\n";
625           break;
626         }
627     }
628
629   test_pos++;
630   test_pos %= 4;
631 }
632
633 /*
634  FIXME
635  ugh.  this is broken and should be rewritten.
636   - [c8. c32 c32]
637  */
638 void
639 Beam::set_grouping (Rhythmic_grouping def, Rhythmic_grouping cur)
640 {
641   def.OK ();
642   cur.OK ();
643   assert (cur.children.size () == stems_.size ());
644
645   cur.split (def);
646
647   Array<int> b;
648   {
649     Array<int> flags;
650     for (int j=0; j <stems_.size (); j++)
651       {
652         Stem *s = stems_[j];
653
654         int f = s->flag_i_ - 2;
655         assert (f>0);
656         flags.push (f);
657       }
658     int fi =0;
659     b= cur.generate_beams (flags, fi);
660     b.insert (0,0);
661     b.push (0);
662     assert (stems_.size () == b.size ()/2);
663   }
664
665   for (int j=0, i=0; i < b.size () && j <stems_.size (); j++)
666     {
667       Stem *s = stems_[j];
668       Direction d = LEFT;
669       do {
670         if (s->beams_i_drul_[d] < 0)
671           s->beams_i_drul_[d] = b[i];
672
673         multiple_i_ = multiple_i_ >? s->beams_i_drul_[d];
674         i++;
675       } while ((flip (&d)) != LEFT);
676     }
677 }
678
679 /*
680   beams to go with one stem.
681   */
682 Molecule
683 Beam::stem_beams (Stem *here, Stem *next, Stem *prev) const
684 {
685   assert (!next || next->hpos_f () > here->hpos_f ());
686   assert (!prev || prev->hpos_f () < here->hpos_f ());
687
688   Real staffline_f = paper ()->rule_thickness ();
689   Real interbeam_f = paper ()->interbeam_f (multiple_i_);
690   Real internote_f = paper ()->internote_f (); 
691   Real beam_f = paper ()->beam_thickness_f ();
692
693   Real dy = interbeam_f;
694   Real stemdx = staffline_f;
695   Real sl = slope_f_* internote_f;
696   lookup_l ()->beam (sl, 20 PT, 1 PT);
697
698   Molecule leftbeams;
699   Molecule rightbeams;
700
701   // UGH
702   Real nw_f = paper ()->note_width () * 0.8;
703
704   /* half beams extending to the left. */
705   if (prev)
706     {
707       int lhalfs= lhalfs = here->beams_i_drul_[LEFT] - prev->beams_i_drul_[RIGHT] ;
708       int lwholebeams= here->beams_i_drul_[LEFT] <? prev->beams_i_drul_[RIGHT] ;
709       /*
710        Half beam should be one note-width, 
711        but let's make sure two half-beams never touch
712        */
713       Real w = here->hpos_f () - prev->hpos_f ();
714       w = w/2 <? nw_f;
715       Atom a;
716       if (lhalfs)               // generates warnings if not
717         a =  lookup_l ()->beam (sl, w, beam_f);
718       a.translate (Offset (-w, -w * sl));
719       for (int j = 0; j  < lhalfs; j++)
720         {
721           Atom b (a);
722           b.translate_axis (-dir_ * dy * (lwholebeams+j), Y_AXIS);
723           leftbeams.add_atom (b);
724         }
725     }
726
727   if (next)
728     {
729       int rhalfs = here->beams_i_drul_[RIGHT] - next->beams_i_drul_[LEFT];
730       int rwholebeams = here->beams_i_drul_[RIGHT] <? next->beams_i_drul_[LEFT];
731
732       Real w = next->hpos_f () - here->hpos_f ();
733       Atom a = lookup_l ()->beam (sl, w + stemdx, beam_f);
734       a.translate_axis( - stemdx/2, X_AXIS);
735       int j = 0;
736       Real gap_f = 0;
737       if (here->beam_gap_i_)
738         {
739           int nogap = rwholebeams - here->beam_gap_i_;
740           for (; j  < nogap; j++)
741             {
742               Atom b (a);
743               b.translate_axis (-dir_ * dy * j, Y_AXIS);
744               rightbeams.add_atom (b);
745             }
746           // TODO: notehead widths differ for different types
747           gap_f = nw_f / 2;
748           w -= 2 * gap_f;
749           a = lookup_l ()->beam (sl, w + stemdx, beam_f);
750         }
751
752       for (; j  < rwholebeams; j++)
753         {
754           Atom b (a);
755           b.translate (Offset (gap_f, -dir_ * dy * j));
756           rightbeams.add_atom (b);
757         }
758
759       w = w/2 <? nw_f;
760       if (rhalfs)
761         a = lookup_l ()->beam (sl, w, beam_f);
762
763       for (; j  < rwholebeams + rhalfs; j++)
764         {
765           Atom b (a);
766           b.translate_axis (-dir_ * dy * j, Y_AXIS);
767           rightbeams.add_atom (b);
768         }
769
770     }
771   leftbeams.add_molecule (rightbeams);
772
773   /*
774     Does beam quanting think  of the asymetry of beams? 
775     Refpoint is on bottom of symbol. (FIXTHAT) --hwn.
776    */
777   return leftbeams;
778 }
779