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