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