]> git.donarmstrong.com Git - lilypond.git/blob - lily/beam.cc
release: 0.1.61
[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     * lowest beam of (UP) beam must never be lower than second staffline
15     * centre beam symbol
16     * less hairy code
17     * redo grouping 
18     * (future) knee: ([\stem 1; c8 \stem -1; c8]
19  */
20
21 #include <math.h>
22
23 #include "p-col.hh"
24 #include "varray.hh"
25 #include "proto.hh"
26 #include "dimen.hh"
27 #include "beam.hh"
28 #include "abbreviation-beam.hh"
29 #include "misc.hh"
30 #include "debug.hh"
31 #include "atom.hh"
32 #include "molecule.hh"
33 #include "leastsquares.hh"
34 #include "stem.hh"
35 #include "paper-def.hh"
36 #include "lookup.hh"
37 #include "grouping.hh"
38 #include "stem-info.hh"
39 #include "main.hh"  // experimental features
40
41
42 IMPLEMENT_IS_TYPE_B1 (Beam, Spanner);
43
44 Beam::Beam ()
45 {
46   slope_f_ = 0;
47   left_y_ = 0;
48   damping_i_ = 1;
49   quantisation_ = NORMAL;
50   multiple_i_ = 0;
51 }
52
53 void
54 Beam::add (Stem*s)
55 {
56   stems_.push (s);
57   s->add_dependency (this);
58   s->beam_l_ = this;
59
60   if (!spanned_drul_[LEFT])
61     set_bounds (LEFT,s);
62   else
63     set_bounds (RIGHT,s);
64 }
65
66 Molecule*
67 Beam::brew_molecule_p () const
68 {
69   Molecule *mol_p = new Molecule;
70
71   Real interline_f = paper ()->interline_f ();
72   Real internote_f = interline_f / 2;
73   Real staffline_f = paper ()->rule_thickness ();
74   Real beam_f = 0.48 * (interline_f - staffline_f);
75
76   Real x0 = stems_[0]->hpos_f ();
77   for (int j=0; j <stems_.size (); j++)
78     {
79       Stem *i = stems_[j];
80       Stem * prev = (j > 0)? stems_[j-1] : 0;
81       Stem * next = (j < stems_.size ()-1) ? stems_[j+1] :0;
82
83       Molecule sb = stem_beams (i, next, prev);
84       Real  x = i->hpos_f ()-x0;
85       sb.translate (Offset (x, (x * slope_f_ + left_y_) * internote_f));
86       mol_p->add (sb);
87     }
88   mol_p->translate_axis (x0 
89     - spanned_drul_[LEFT]->absolute_coordinate (X_AXIS), X_AXIS);
90
91   return mol_p;
92 }
93
94 Offset
95 Beam::center () const
96 {
97   Real w= (paper ()->note_width () + width ().length ())/2.0;
98   return Offset (w, (left_y_ + w* slope_f_)*paper ()->internote_f ());
99 }
100
101 void
102 Beam::do_pre_processing ()
103 {
104   if (!dir_)
105     set_default_dir ();
106 }
107
108 void
109 Beam::do_print () const
110 {
111 #ifndef NPRINT
112   DOUT << "slope_f_ " <<slope_f_ << "left ypos " << left_y_;
113   Spanner::do_print ();
114 #endif
115 }
116
117 void
118 Beam::do_post_processing ()
119 {
120   if (stems_.size () < 2)
121     {
122       warning (_ ("Beam with less than 2 stems"));
123       transparent_b_ = true;
124       return ;
125     }
126   solve_slope ();
127   set_stemlens ();
128 }
129
130 void
131 Beam::do_substitute_dependent (Score_elem*o,Score_elem*n)
132 {
133   if (o->is_type_b (Stem::static_name ()))
134       stems_.substitute ((Stem*)o->item (),  n? (Stem*) n->item ():0);
135 }
136
137 Interval
138 Beam::do_width () const
139 {
140   return Interval (stems_[0]->hpos_f (),
141                    stems_.top ()->hpos_f ());
142 }
143
144 void
145 Beam::set_default_dir ()
146 {
147   Drul_array<int> total;
148   total[UP]  = total[DOWN] = 0;
149   Drul_array<int> count; 
150   count[UP]  = count[DOWN] = 0;
151   Direction d = DOWN;
152   
153   for (int i=0; i <stems_.size (); i++)
154     do {
155       Stem *s = stems_[i];
156       int current = s->dir_ 
157         ? (1 + d * s->dir_)/2
158         : s->get_center_distance ((Direction)-d);
159
160       if (current)
161         {
162           total[d] += current;
163           count[d] ++;
164         }
165
166     } while (flip(&d) != DOWN);
167   
168    do {
169     if (!total[d])
170       count[d] = 1;
171   } while (flip(&d) != DOWN);
172   
173   /* 
174
175      [Ross] states that the majority of the notes dictates the
176      direction (and not the mean of "center distance")
177   */
178   dir_ = (total[UP] > total[DOWN]) ? UP : DOWN;
179
180   for (int i=0; i <stems_.size (); i++)
181     {
182       Stem *sl = stems_[i];
183       sl->dir_ = dir_;
184     }
185 }
186
187 /*
188   See Documentation/tex/fonts.doc
189  */
190 void
191 Beam::solve_slope ()
192 {
193   /*
194     should use minimum energy formulation (cf linespacing)
195   */
196
197   assert (multiple_i_);
198   Array<Stem_info> sinfo;
199   for (int j=0; j <stems_.size (); j++)
200     {
201       Stem *i = stems_[j];
202
203       i->mult_i_ = multiple_i_;
204       i->set_default_extents ();
205       if (i->invisible_b ())
206         continue;
207
208       Stem_info info (i);
209       sinfo.push (info);
210     }
211   if (! sinfo.size ())
212     slope_f_ = left_y_ = 0;
213   else if (sinfo.size () == 1)
214     {
215       slope_f_ = 0;
216       left_y_ = sinfo[0].idealy_f_;
217     }
218   else
219     {
220
221       Real leftx = sinfo[0].x_;
222       Least_squares l;
223       for (int i=0; i < sinfo.size (); i++)
224         {
225           sinfo[i].x_ -= leftx;
226           l.input.push (Offset (sinfo[i].x_, sinfo[i].idealy_f_));
227         }
228
229       l.minimise (slope_f_, left_y_);
230     }
231
232   Real dy = 0.0;
233   for (int i=0; i < sinfo.size (); i++)
234     {
235       Real y = sinfo[i].x_ * slope_f_ + left_y_;
236       Real my = sinfo[i].miny_f_;
237
238       if (my - y > dy)
239         dy = my -y;
240     }
241   left_y_ += dy;
242   left_y_ *= dir_;
243
244   slope_f_ *= dir_;
245
246   /*
247     This neat trick is by Werner Lemberg, damped = tanh (slope_f_) corresponds
248     with some tables in [Wanske]
249     */
250   if (damping_i_)
251     slope_f_ = 0.6 * tanh (slope_f_) / damping_i_;
252
253   quantise_dy ();
254
255   Real sl = slope_f_ * paper ()->internote_f ();
256   paper ()->lookup_l ()->beam (sl, 20 PT, 1 PT);
257   slope_f_ = sl / paper ()->internote_f ();
258 }
259
260 void
261 Beam::quantise_dy ()
262 {
263   /*
264     [Ross] (simplification of)
265     Try to set slope_f_ complying with y-span of:
266       - zero
267       - beam_f / 2 + staffline_f / 2
268       - beam_f + staffline_f
269     + n * interline
270     */
271
272   if (quantisation_ <= NONE)
273     return;
274
275   Real interline_f = paper ()->interline_f ();
276   Real internote_f = interline_f / 2;
277   Real staffline_f = paper ()->rule_thickness ();
278   Real beam_f = 0.48 * (interline_f - staffline_f);
279
280   Real dx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
281
282   // dim(y) = internote; so slope = (y/internote)/x
283   Real dy_f = dx_f * abs (slope_f_ * internote_f);
284   
285   Real quanty_f = 0.0;
286
287   /* UGR.   ICE in 2.8.1; bugreport filed. */
288   Array<Real> allowed_fraction (3);
289   allowed_fraction[0] = 0;
290   allowed_fraction[1] = (beam_f / 2 + staffline_f / 2);
291   allowed_fraction[2] = (beam_f + staffline_f);
292
293
294     Interval iv = quantise_iv (allowed_fraction, interline_f, dy_f);
295     quanty_f = (dy_f - iv.min () <= iv.max () - dy_f)
296       ? iv.min ()
297       : iv.max ();
298
299
300   slope_f_ = (quanty_f / dx_f) / internote_f * sign (slope_f_);
301 }
302
303 static int test_pos = 0;
304
305
306 /*
307   
308   Prevent interference from stafflines and beams.  See Documentation/tex/fonts.doc
309   
310  */
311 void
312 Beam::quantise_left_y (bool extend_b)
313 {
314    /*
315     we only need to quantise the start of the beam as dy is quantised too
316    if extend_b then stems must *not* get shorter
317    */
318
319   if (quantisation_ <= NONE)
320     return;
321
322   /*
323     ----------------------------------------------------------
324                                                    ########
325                                         ########
326                              ########
327     --------------########------------------------------------
328        ########
329
330        hang       straddle   sit        inter      hang
331    */
332
333   Real interbeam_f = paper ()->interbeam_f ();
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 = 0.48 * (interline_f - staffline_f);
338   Real symbol_f = beam_f + interbeam_f * (multiple_i_ - 1);
339
340   Real straddle = 0;
341   Real sit = beam_f / 2 - staffline_f / 2;
342   Real inter = interline_f / 2;
343   Real hang = interline_f - beam_f / 2 + staffline_f / 2;
344
345   /*
346     Put all allowed positions into an array.
347     Whether a position is allowed or not depends on 
348     strictness of quantisation, multiplicity and direction.
349
350     For simplicity, we'll assume dir = UP and correct if 
351     dir = DOWN afterwards.
352    */
353
354   // dim(left_y_) = internote
355   Real dy_f = dir_ * left_y_ * internote_f;
356
357   Array<Real> allowed_position;
358   if (quantisation_ != TEST)
359     {
360       if (quantisation_ <= NORMAL) 
361         {
362           if ((multiple_i_ <= 2) || (abs (dy_f) >= staffline_f / 2))
363             allowed_position.push (straddle);
364           if ((multiple_i_ <= 1) || (abs (dy_f) >= staffline_f / 2))
365             allowed_position.push (sit);
366           allowed_position.push (hang);
367         }
368       else
369         // TODO: check and fix TRADITIONAL
370         {
371           if ((multiple_i_ <= 2) || (abs (dy_f) >= staffline_f / 2))
372             allowed_position.push (straddle);
373           if ((multiple_i_ <= 1) && (dy_f <= staffline_f / 2))
374             allowed_position.push (sit);
375           if (dy_f >= -staffline_f / 2)
376             allowed_position.push (hang);
377         }
378     }
379   else
380     {
381       if (test_pos == 0)
382         {
383         allowed_position.push (hang);
384         cout << "hang" << hang << endl;
385         }
386       else if (test_pos==1)
387         {
388         allowed_position.push (straddle);
389         cout << "straddle" << straddle << endl;
390         }
391       else if (test_pos==2)
392         {
393         allowed_position.push (sit);
394         cout << "sit" << sit << endl;
395         }
396       else if (test_pos==3)
397         {
398         allowed_position.push (inter);
399         cout << "inter" << inter << endl;
400         }
401     }
402
403 #if 0
404   // this currently never happens
405   Real q = (dy_f / interline_f - dy_i) * interline_f;
406   if ((quantisation_ < NORMAL) && (q < interline_f / 3 - beam_f / 2))
407     allowed_position.push (inter);
408 #endif
409
410   Interval iv = quantise_iv (allowed_position, interline_f, dy_f);
411
412   Real quanty_f = dy_f - iv.min () <= iv.max () - dy_f ? iv.min () : iv.max ();
413   if (extend_b)
414     quanty_f = iv.max ();
415
416   // dim(left_y_) = internote
417   left_y_ = dir_ * quanty_f / internote_f;
418 }
419
420 void
421 Beam::set_stemlens ()
422 {
423   Real interbeam_f = paper ()->interbeam_f ();
424   Real interline_f = paper ()->interline_f ();
425   Real internote_f = interline_f / 2;
426   Real staffline_f = paper ()->rule_thickness ();
427   Real beam_f = 0.48 * (interline_f - staffline_f);
428
429   /*
430     if we have more than three beams they must open-up
431     in order to not collide with staff lines
432    */
433   if (multiple_i_ > 3)
434     interbeam_f += 2.0 * staffline_f / 4;
435
436   Real x0 = stems_[0]->hpos_f ();
437   Real dy = 0;
438   // ugh, rounding problems! (enge floots)
439   Real epsilon = staffline_f / 8;
440   do
441     { 
442       left_y_ += dy * dir_;
443       quantise_left_y (dy);
444       dy = 0;
445       for (int i=0; i < stems_.size (); i++)
446         {
447           Stem *s = stems_[i];
448           if (s->transparent_b_)
449             continue;
450
451           Real x = s->hpos_f () - x0;
452           s->set_stemend (left_y_ + slope_f_ * x);
453           Real y = s->stem_end_f () * dir_;
454           Stem_info info (s);
455           if (y < info.miny_f_)
456             dy = dy >? info.miny_f_ - y;
457         }
458     } while (abs (dy) > epsilon);
459
460   // ugh asymmetric symbol ?
461   if (dir_ == UP)
462     left_y_ -= dir_ * staffline_f / 4;
463
464   if ((multiple_i_ >= 3) && (dir_ == UP))
465     left_y_ -= dir_ * staffline_f / 4;
466
467   test_pos++;
468   test_pos %= 4;
469 }
470
471 /*
472  FIXME
473  ugh.  this is broken and should be rewritten.
474   - [c8. c32 c32]
475  */
476 void
477 Beam::set_grouping (Rhythmic_grouping def, Rhythmic_grouping cur)
478 {
479   def.OK ();
480   cur.OK ();
481   assert (cur.children.size () == stems_.size ());
482
483   cur.split (def);
484
485   Array<int> b;
486   {
487     Array<int> flags;
488     for (int j=0; j <stems_.size (); j++)
489       {
490         Stem *s = stems_[j];
491
492         int f = s->flag_i_ - 2;
493         assert (f>0);
494         flags.push (f);
495       }
496     int fi =0;
497     b= cur.generate_beams (flags, fi);
498     b.insert (0,0);
499     b.push (0);
500     assert (stems_.size () == b.size ()/2);
501   }
502
503   for (int j=0, i=0; i < b.size () && j <stems_.size (); i+= 2, j++)
504     {
505       Stem *s = stems_[j];
506       s->beams_left_i_ = b[i];
507       s->beams_right_i_ = b[i+1];
508       multiple_i_ = multiple_i_ >? (b[i] >? b[i+1]);
509     }
510 }
511
512 /*
513   beams to go with one stem.
514   */
515 Molecule
516 Beam::stem_beams (Stem *here, Stem *next, Stem *prev) const
517 {
518   assert (!next || next->hpos_f () > here->hpos_f ());
519   assert (!prev || prev->hpos_f () < here->hpos_f ());
520
521   Real staffline_f = paper ()->rule_thickness ();
522   Real interbeam_f = paper ()->interbeam_f ();
523   Real internote_f =paper ()->internote_f (); 
524   Real interline_f = 2 * internote_f;
525   Real beamheight_f = 0.48 * (interline_f - staffline_f);
526
527   if (multiple_i_ > 3)
528     interbeam_f += 2.0 * staffline_f / 4;
529   Real dy = interbeam_f;
530   Real stemdx = staffline_f;
531   Real sl = slope_f_* internote_f;
532   paper ()->lookup_l ()->beam (sl, 20 PT, 1 PT);
533
534   Molecule leftbeams;
535   Molecule rightbeams;
536
537   /* half beams extending to the left. */
538   if (prev)
539     {
540       int lhalfs= lhalfs = here->beams_left_i_ - prev->beams_right_i_ ;
541       int lwholebeams= here->beams_left_i_ <? prev->beams_right_i_ ;
542       Real w = (here->hpos_f () - prev->hpos_f ())/4 <? paper ()->note_width ();;
543       Atom a;
544       if (lhalfs)               // generates warnings if not
545         a =  paper ()->lookup_l ()->beam (sl, w, beamheight_f);
546       a.translate (Offset (-w, -w * sl));
547       for (int j = 0; j  < lhalfs; j++)
548         {
549           Atom b (a);
550           b.translate_axis (-dir_ * dy * (lwholebeams+j), Y_AXIS);
551           leftbeams.add (b);
552         }
553     }
554
555   if (next)
556     {
557       int rhalfs = here->beams_right_i_ - next->beams_left_i_;
558       int rwholebeams = here->beams_right_i_ <? next->beams_left_i_;
559
560       Real w = next->hpos_f () - here->hpos_f ();
561       Atom a = paper ()->lookup_l ()->beam (sl, w + stemdx, beamheight_f);
562       a.translate_axis( - stemdx/2, X_AXIS);
563       int j = 0;
564       Real gap_f = 0;
565       if (here->beam_gap_i_)
566         {
567           int nogap = rwholebeams - here->beam_gap_i_;
568           for (; j  < nogap; j++)
569             {
570               Atom b (a);
571               b.translate_axis (-dir_ * dy * j, Y_AXIS);
572               rightbeams.add (b);
573             }
574           // TODO: notehead widths differ for different types
575           gap_f = paper ()->note_width () / 2;
576           w -= 2 * gap_f;
577           a = paper ()->lookup_l ()->beam (sl, w + stemdx, beamheight_f);
578         }
579
580       for (; j  < rwholebeams; j++)
581         {
582           Atom b (a);
583           b.translate (Offset (gap_f, -dir_ * dy * j));
584           rightbeams.add (b);
585         }
586
587       w = w/4 <? paper ()->note_width ();
588       if (rhalfs)
589         a = paper ()->lookup_l ()->beam (sl, w, beamheight_f);
590
591       for (; j  < rwholebeams + rhalfs; j++)
592         {
593           Atom b (a);
594           b.translate_axis (-dir_ * dy * j, Y_AXIS);
595           rightbeams.add (b);
596         }
597
598     }
599   leftbeams.add (rightbeams);
600
601   /*
602     Does beam quanting think  of the asymetry of beams? 
603     Refpoint is on bottom of symbol. (FIXTHAT) --hwn.
604    */
605   return leftbeams;
606 }
607