]> git.donarmstrong.com Git - lilypond.git/blob - lily/beam.cc
55256fdd5b78321bf93ed65398adcb24e3a353d9
[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           if (sinfo[0].dir_ == sinfo.top ().dir_)
281             left_y_ = sinfo[0].idealy_f_ >? sinfo.top ().idealy_f_ - slope_f_ * dx; 
282           // knee
283           else
284             left_y_ = sinfo[0].idealy_f_;
285         }
286     }
287
288   // uh?
289   Real dy = 0.0;
290   for (int i=0; i < sinfo.size (); i++)
291     {
292       Real y = sinfo[i].x_ * slope_f_ + left_y_;
293       Real my = sinfo[i].miny_f_;
294
295       if (my - y > dy)
296         dy = my -y;
297     }
298   left_y_ += dy;
299   left_y_ *= dir_;
300   slope_f_ *= dir_;
301
302   quantise_dy ();
303 }
304
305 void
306 Beam::quantise_dy ()
307 {
308   /*
309     [Ross] (simplification of)
310     Try to set slope_f_ complying with y-span of:
311       - zero
312       - beam_f / 2 + staffline_f / 2
313       - beam_f + staffline_f
314     + n * interline
315     */
316
317   if (quantisation_ <= NONE)
318     return;
319
320   Real interline_f = paper ()->interline_f ();
321   Real internote_f = interline_f / 2;
322   Real staffline_f = paper ()->rule_thickness ();
323   Real beam_f = paper ()->beam_thickness_f ();
324
325   Real dx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
326
327   // dim(y) = internote; so slope = (y/internote)/x
328   Real dy_f = dx_f * abs (slope_f_ * internote_f);
329   
330   Real quanty_f = 0.0;
331
332   /* UGR.   ICE in 2.8.1; bugreport filed. */
333   Array<Real> allowed_fraction (3);
334   allowed_fraction[0] = 0;
335   allowed_fraction[1] = (beam_f / 2 + staffline_f / 2);
336   allowed_fraction[2] = (beam_f + staffline_f);
337
338
339   Interval iv = quantise_iv (allowed_fraction, interline_f, dy_f);
340   quanty_f = (dy_f - iv.min () <= iv.max () - dy_f)
341     ? iv.min ()
342     : iv.max ();
343
344
345   slope_f_ = (quanty_f / dx_f) / internote_f * sign (slope_f_);
346 }
347
348 static int test_pos = 0;
349
350
351 /*
352   
353   Prevent interference from stafflines and beams.  See Documentation/tex/fonts.doc
354   
355  */
356 void
357 Beam::quantise_left_y (bool extend_b)
358 {
359    /*
360     we only need to quantise the start of the beam as dy is quantised too
361    if extend_b then stems must *not* get shorter
362    */
363
364   if (quantisation_ <= NONE)
365     return;
366
367   /*
368     ----------------------------------------------------------
369                                                    ########
370                                         ########
371                              ########
372     --------------########------------------------------------
373        ########
374
375        hang       straddle   sit        inter      hang
376    */
377
378   Real interline_f = paper ()->interline_f ();
379   Real internote_f = paper ()->internote_f ();
380   Real staffline_f = paper ()->rule_thickness ();
381   Real beam_f = paper ()->beam_thickness_f ();
382
383   /*
384     [TODO]
385     it would be nice to have all allowed positions in a runtime matrix:
386     (multiplicity, minimum_beam_dy, maximum_beam_dy)
387    */
388
389   Real straddle = 0;
390   Real sit = beam_f / 2 - staffline_f / 2;
391   Real inter = interline_f / 2;
392   Real hang = interline_f - beam_f / 2 + staffline_f / 2;
393
394   /*
395     Put all allowed positions into an array.
396     Whether a position is allowed or not depends on 
397     strictness of quantisation, multiplicity and direction.
398
399     For simplicity, we'll assume dir = UP and correct if 
400     dir = DOWN afterwards.
401    */
402
403   // dim(left_y_) = internote
404   Real dy_f = dir_ * left_y_ * internote_f;
405
406   Real beamdx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
407   Real beamdy_f = beamdx_f * slope_f_ * internote_f;
408
409   Array<Real> allowed_position;
410   if (quantisation_ != TEST)
411     {
412       if (quantisation_ <= NORMAL) 
413         {
414           if ((multiple_i_ <= 2) || (abs (beamdy_f) >= staffline_f / 2))
415             allowed_position.push (straddle);
416           if ((multiple_i_ <= 1) || (abs (beamdy_f) >= staffline_f / 2))
417             allowed_position.push (sit);
418           allowed_position.push (hang);
419         }
420       else
421         // TODO: check and fix TRADITIONAL
422         {
423           if ((multiple_i_ <= 2) || (abs (beamdy_f) >= staffline_f / 2))
424             allowed_position.push (straddle);
425           if ((multiple_i_ <= 1) && (beamdy_f <= staffline_f / 2))
426             allowed_position.push (sit);
427           if (beamdy_f >= -staffline_f / 2)
428             allowed_position.push (hang);
429         }
430     }
431   else
432     {
433       if (test_pos == 0)
434         {
435         allowed_position.push (hang);
436         cout << "hang" << hang << endl;
437         }
438       else if (test_pos==1)
439         {
440         allowed_position.push (straddle);
441         cout << "straddle" << straddle << endl;
442         }
443       else if (test_pos==2)
444         {
445         allowed_position.push (sit);
446         cout << "sit" << sit << endl;
447         }
448       else if (test_pos==3)
449         {
450         allowed_position.push (inter);
451         cout << "inter" << inter << endl;
452         }
453     }
454
455 #if 0
456   // this currently never happens
457   Real q = (dy_f / interline_f - dy_i) * interline_f;
458   if ((quantisation_ < NORMAL) && (q < interline_f / 3 - beam_f / 2))
459     allowed_position.push (inter);
460 #endif
461
462   Interval iv = quantise_iv (allowed_position, interline_f, dy_f);
463
464   Real quanty_f = dy_f - iv.min () <= iv.max () - dy_f ? iv.min () : iv.max ();
465   if (extend_b)
466     quanty_f = iv.max ();
467
468   // dim(left_y_) = internote
469   left_y_ = dir_ * quanty_f / internote_f;
470 }
471
472 void
473 Beam::set_stemlens ()
474 {
475   Real staffline_f = paper ()->rule_thickness ();
476   Real interbeam_f = paper ()->interbeam_f (multiple_i_);
477   Real internote_f = paper ()->internote_f (); 
478   Real beam_f = paper ()->beam_thickness_f ();
479
480   // enge floots
481   Real epsilon_f = staffline_f / 8;
482
483   /* 
484
485    Damped and quantised slopes, esp. in monotone scales such as
486
487       [c d e f g a b c]
488
489    will soon produce the minimal stem-length for one of the extreme 
490    stems, which is wrong (and ugly).  The minimum stemlength should
491    be kept rather small, in order to handle extreme beaming, such as
492
493       [c c' 'c]  %assuming no knee
494       
495    correctly.
496    To avoid these short stems for normal cases, we'll correct for
497    the loss in slope, if necessary.
498
499    [TODO]
500    ugh, another hack.  who's next?
501    Writing this all down, i realise (at last) that the Right Thing to
502    do is to assign uglyness to slope and stem-lengths and then minimise
503    the total uglyness of a beam.
504    Steep slopes are ugly, shortened stems are ugly, lengthened stems
505    are ugly.
506    How to do this?
507    
508    */
509
510   Real dx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
511   Real damp_correct_f = paper ()->get_var ("beam_slope_damp_correct_factor");
512   Real damped_slope_dy_f = (solved_slope_f_ - slope_f_) * dx_f
513     * sign (slope_f_);
514   damped_slope_dy_f *= damp_correct_f;
515   if (damped_slope_dy_f <= epsilon_f)
516     damped_slope_dy_f = 0;
517
518   DOUT << "Beam::set_stemlens: \n";
519   Real x0 = stems_[0]->hpos_f ();
520   Real dy_f = 0;
521   // urg
522   for (int jj = 0; jj < 10; jj++)
523     { 
524       left_y_ += dy_f * dir_;
525       quantise_left_y (dy_f);
526       dy_f = 0;
527       for (int i=0; i < stems_.size (); i++)
528         {
529           Stem *s = stems_[i];
530           if (s->transparent_b_)
531             continue;
532
533           Real x = s->hpos_f () - x0;
534           // urg move this to stem-info
535           Real sy = left_y_ + slope_f_ * x;
536           if (dir_ != s->dir_)
537             sy -= dir_ * (beam_f / 2
538               + (s->mult_i_ - 1) * interbeam_f) / internote_f;
539           s->set_stemend (sy);
540           Real y = s->stem_end_f () * dir_;
541           Stem_info info (s);
542           if (y > info.maxy_f_)
543             dy_f = dy_f <? info.maxy_f_ - y;
544           if (y < info.miny_f_)
545             { 
546               // when all too short, normal stems win..
547               if (dy_f < -epsilon_f)
548                 warning ( _("Weird beam shift, check your knees."));
549               dy_f = dy_f >? info.miny_f_ - y;
550             }
551         }
552       if (damped_slope_dy_f && (dy_f >= 0))
553         dy_f += damped_slope_dy_f;
554       damped_slope_dy_f = 0;
555       if (abs (dy_f) <= epsilon_f)
556         {
557           DOUT << "Beam::set_stemlens: " << jj << " iterations\n";
558           break;
559         }
560     }
561
562   test_pos++;
563   test_pos %= 4;
564 }
565
566 /*
567  FIXME
568  ugh.  this is broken and should be rewritten.
569   - [c8. c32 c32]
570  */
571 void
572 Beam::set_grouping (Rhythmic_grouping def, Rhythmic_grouping cur)
573 {
574   def.OK ();
575   cur.OK ();
576   assert (cur.children.size () == stems_.size ());
577
578   cur.split (def);
579
580   Array<int> b;
581   {
582     Array<int> flags;
583     for (int j=0; j <stems_.size (); j++)
584       {
585         Stem *s = stems_[j];
586
587         int f = s->flag_i_ - 2;
588         assert (f>0);
589         flags.push (f);
590       }
591     int fi =0;
592     b= cur.generate_beams (flags, fi);
593     b.insert (0,0);
594     b.push (0);
595     assert (stems_.size () == b.size ()/2);
596   }
597
598   for (int j=0, i=0; i < b.size () && j <stems_.size (); i+= 2, j++)
599     {
600       Stem *s = stems_[j];
601       s->beams_left_i_ = b[i];
602       s->beams_right_i_ = b[i+1];
603       multiple_i_ = multiple_i_ >? (b[i] >? b[i+1]);
604     }
605 }
606
607 /*
608   beams to go with one stem.
609   */
610 Molecule
611 Beam::stem_beams (Stem *here, Stem *next, Stem *prev) const
612 {
613   assert (!next || next->hpos_f () > here->hpos_f ());
614   assert (!prev || prev->hpos_f () < here->hpos_f ());
615
616   Real staffline_f = paper ()->rule_thickness ();
617   Real interbeam_f = paper ()->interbeam_f (multiple_i_);
618   Real internote_f = paper ()->internote_f (); 
619   Real beam_f = paper ()->beam_thickness_f ();
620
621   Real dy = interbeam_f;
622   Real stemdx = staffline_f;
623   Real sl = slope_f_* internote_f;
624   paper ()->lookup_l ()->beam (sl, 20 PT, 1 PT);
625
626   Molecule leftbeams;
627   Molecule rightbeams;
628
629   /* half beams extending to the left. */
630   if (prev)
631     {
632       int lhalfs= lhalfs = here->beams_left_i_ - prev->beams_right_i_ ;
633       int lwholebeams= here->beams_left_i_ <? prev->beams_right_i_ ;
634       Real w = (here->hpos_f () - prev->hpos_f ())/4 <? paper ()->note_width ();;
635       Atom a;
636       if (lhalfs)               // generates warnings if not
637         a =  paper ()->lookup_l ()->beam (sl, w, beam_f);
638       a.translate (Offset (-w, -w * sl));
639       for (int j = 0; j  < lhalfs; j++)
640         {
641           Atom b (a);
642           b.translate_axis (-dir_ * dy * (lwholebeams+j), Y_AXIS);
643           leftbeams.add (b);
644         }
645     }
646
647   if (next)
648     {
649       int rhalfs = here->beams_right_i_ - next->beams_left_i_;
650       int rwholebeams = here->beams_right_i_ <? next->beams_left_i_;
651
652       Real w = next->hpos_f () - here->hpos_f ();
653       Atom a = paper ()->lookup_l ()->beam (sl, w + stemdx, beam_f);
654       a.translate_axis( - stemdx/2, X_AXIS);
655       int j = 0;
656       Real gap_f = 0;
657       if (here->beam_gap_i_)
658         {
659           int nogap = rwholebeams - here->beam_gap_i_;
660           for (; j  < nogap; j++)
661             {
662               Atom b (a);
663               b.translate_axis (-dir_ * dy * j, Y_AXIS);
664               rightbeams.add (b);
665             }
666           // TODO: notehead widths differ for different types
667           gap_f = paper ()->note_width () / 2;
668           w -= 2 * gap_f;
669           a = paper ()->lookup_l ()->beam (sl, w + stemdx, beam_f);
670         }
671
672       for (; j  < rwholebeams; j++)
673         {
674           Atom b (a);
675           b.translate (Offset (gap_f, -dir_ * dy * j));
676           rightbeams.add (b);
677         }
678
679       w = w/4 <? paper ()->note_width ();
680       if (rhalfs)
681         a = paper ()->lookup_l ()->beam (sl, w, beam_f);
682
683       for (; j  < rwholebeams + rhalfs; j++)
684         {
685           Atom b (a);
686           b.translate_axis (-dir_ * dy * j, Y_AXIS);
687           rightbeams.add (b);
688         }
689
690     }
691   leftbeams.add (rightbeams);
692
693   /*
694     Does beam quanting think  of the asymetry of beams? 
695     Refpoint is on bottom of symbol. (FIXTHAT) --hwn.
696    */
697   return leftbeams;
698 }
699