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