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