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