]> git.donarmstrong.com Git - lilypond.git/blob - lily/beam.cc
release: 0.1.62
[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     * (future) knee: ([\stem 1; c8 \stem -1; c8]
18  */
19
20 #include <math.h>
21
22 #include "p-col.hh"
23 #include "varray.hh"
24 #include "proto.hh"
25 #include "dimen.hh"
26 #include "beam.hh"
27 #include "abbreviation-beam.hh"
28 #include "misc.hh"
29 #include "debug.hh"
30 #include "atom.hh"
31 #include "molecule.hh"
32 #include "leastsquares.hh"
33 #include "stem.hh"
34 #include "paper-def.hh"
35 #include "lookup.hh"
36 #include "grouping.hh"
37 #include "stem-info.hh"
38 #include "main.hh"  // experimental features
39
40
41 IMPLEMENT_IS_TYPE_B1 (Beam, Spanner);
42
43 Beam::Beam ()
44 {
45   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
171      [Ross] states that the majority of the notes dictates the
172      direction (and not the mean of "center distance")
173   */
174   dir_ = (total[UP] > total[DOWN]) ? UP : DOWN;
175
176   for (int i=0; i <stems_.size (); i++)
177     {
178       Stem *sl = stems_[i];
179       sl->dir_ = dir_;
180     }
181 }
182
183 /*
184   See Documentation/tex/fonts.doc
185  */
186 void
187 Beam::solve_slope ()
188 {
189   /*
190     should use minimum energy formulation (cf linespacing)
191   */
192
193   assert (multiple_i_);
194   Array<Stem_info> sinfo;
195   for (int j=0; j <stems_.size (); j++)
196     {
197       Stem *i = stems_[j];
198
199       i->mult_i_ = multiple_i_;
200       i->set_default_extents ();
201       if (i->invisible_b ())
202         continue;
203
204       Stem_info info (i);
205       sinfo.push (info);
206     }
207   if (! sinfo.size ())
208     slope_f_ = left_y_ = 0;
209   else if (sinfo.size () == 1)
210     {
211       slope_f_ = 0;
212       left_y_ = sinfo[0].idealy_f_;
213     }
214   else
215     {
216
217       Real leftx = sinfo[0].x_;
218       Least_squares l;
219       for (int i=0; i < sinfo.size (); i++)
220         {
221           sinfo[i].x_ -= leftx;
222           l.input.push (Offset (sinfo[i].x_, sinfo[i].idealy_f_));
223         }
224
225       l.minimise (slope_f_, left_y_);
226     }
227
228   Real dy = 0.0;
229   for (int i=0; i < sinfo.size (); i++)
230     {
231       Real y = sinfo[i].x_ * slope_f_ + left_y_;
232       Real my = sinfo[i].miny_f_;
233
234       if (my - y > dy)
235         dy = my -y;
236     }
237   left_y_ += dy;
238   left_y_ *= dir_;
239
240   slope_f_ *= dir_;
241
242   /*
243     This neat trick is by Werner Lemberg, damped = tanh (slope_f_) corresponds
244     with some tables in [Wanske]
245     */
246   if (damping_i_)
247     slope_f_ = 0.6 * tanh (slope_f_) / damping_i_;
248
249   quantise_dy ();
250
251   Real sl = slope_f_ * paper ()->internote_f ();
252   paper ()->lookup_l ()->beam (sl, 20 PT, 1 PT);
253   slope_f_ = sl / paper ()->internote_f ();
254 }
255
256 void
257 Beam::quantise_dy ()
258 {
259   /*
260     [Ross] (simplification of)
261     Try to set slope_f_ complying with y-span of:
262       - zero
263       - beam_f / 2 + staffline_f / 2
264       - beam_f + staffline_f
265     + n * interline
266     */
267
268   if (quantisation_ <= NONE)
269     return;
270
271   Real interline_f = paper ()->interline_f ();
272   Real internote_f = interline_f / 2;
273   Real staffline_f = paper ()->rule_thickness ();
274   Real beam_f = paper ()->beam_thickness_f ();
275
276   Real dx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
277
278   // dim(y) = internote; so slope = (y/internote)/x
279   Real dy_f = dx_f * abs (slope_f_ * internote_f);
280   
281   Real quanty_f = 0.0;
282
283   /* UGR.   ICE in 2.8.1; bugreport filed. */
284   Array<Real> allowed_fraction (3);
285   allowed_fraction[0] = 0;
286   allowed_fraction[1] = (beam_f / 2 + staffline_f / 2);
287   allowed_fraction[2] = (beam_f + staffline_f);
288
289
290     Interval iv = quantise_iv (allowed_fraction, interline_f, dy_f);
291     quanty_f = (dy_f - iv.min () <= iv.max () - dy_f)
292       ? iv.min ()
293       : iv.max ();
294
295
296   slope_f_ = (quanty_f / dx_f) / internote_f * sign (slope_f_);
297 }
298
299 static int test_pos = 0;
300
301
302 /*
303   
304   Prevent interference from stafflines and beams.  See Documentation/tex/fonts.doc
305   
306  */
307 void
308 Beam::quantise_left_y (bool extend_b)
309 {
310    /*
311     we only need to quantise the start of the beam as dy is quantised too
312    if extend_b then stems must *not* get shorter
313    */
314
315   if (quantisation_ <= NONE)
316     return;
317
318   /*
319     ----------------------------------------------------------
320                                                    ########
321                                         ########
322                              ########
323     --------------########------------------------------------
324        ########
325
326        hang       straddle   sit        inter      hang
327    */
328
329   Real interline_f = paper ()->interline_f ();
330   Real internote_f = paper ()->internote_f ();
331   Real staffline_f = paper ()->rule_thickness ();
332   Real beam_f = paper ()->beam_thickness_f ();
333
334   /*
335     [TODO]
336     it would be nice to have all allowed positions in a runtime matrix:
337     (multiplicity, minimum_beam_dy, maximum_beam_dy)
338    */
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   Real beamdx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
358   Real beamdy_f = beamdx_f * slope_f_ * internote_f;
359
360   Array<Real> allowed_position;
361   if (quantisation_ != TEST)
362     {
363       if (quantisation_ <= NORMAL) 
364         {
365           if ((multiple_i_ <= 2) || (abs (beamdy_f) >= staffline_f / 2))
366             allowed_position.push (straddle);
367           if ((multiple_i_ <= 1) || (abs (beamdy_f) >= staffline_f / 2))
368             allowed_position.push (sit);
369           allowed_position.push (hang);
370         }
371       else
372         // TODO: check and fix TRADITIONAL
373         {
374           if ((multiple_i_ <= 2) || (abs (beamdy_f) >= staffline_f / 2))
375             allowed_position.push (straddle);
376           if ((multiple_i_ <= 1) && (beamdy_f <= staffline_f / 2))
377             allowed_position.push (sit);
378           if (beamdy_f >= -staffline_f / 2)
379             allowed_position.push (hang);
380         }
381     }
382   else
383     {
384       if (test_pos == 0)
385         {
386         allowed_position.push (hang);
387         cout << "hang" << hang << endl;
388         }
389       else if (test_pos==1)
390         {
391         allowed_position.push (straddle);
392         cout << "straddle" << straddle << endl;
393         }
394       else if (test_pos==2)
395         {
396         allowed_position.push (sit);
397         cout << "sit" << sit << endl;
398         }
399       else if (test_pos==3)
400         {
401         allowed_position.push (inter);
402         cout << "inter" << inter << endl;
403         }
404     }
405
406 #if 0
407   // this currently never happens
408   Real q = (dy_f / interline_f - dy_i) * interline_f;
409   if ((quantisation_ < NORMAL) && (q < interline_f / 3 - beam_f / 2))
410     allowed_position.push (inter);
411 #endif
412
413   Interval iv = quantise_iv (allowed_position, interline_f, dy_f);
414
415   Real quanty_f = dy_f - iv.min () <= iv.max () - dy_f ? iv.min () : iv.max ();
416   if (extend_b)
417     quanty_f = iv.max ();
418
419   // dim(left_y_) = internote
420   left_y_ = dir_ * quanty_f / internote_f;
421 }
422
423 void
424 Beam::set_stemlens ()
425 {
426   Real staffline_f = paper ()->rule_thickness ();
427
428   Real x0 = stems_[0]->hpos_f ();
429   Real dy = 0;
430   // ugh, rounding problems! (enge floots)
431   Real epsilon = staffline_f / 8;
432   do
433     { 
434       left_y_ += dy * dir_;
435       quantise_left_y (dy);
436       dy = 0;
437       for (int i=0; i < stems_.size (); i++)
438         {
439           Stem *s = stems_[i];
440           if (s->transparent_b_)
441             continue;
442
443           Real x = s->hpos_f () - x0;
444           s->set_stemend (left_y_ + slope_f_ * x);
445           Real y = s->stem_end_f () * dir_;
446           Stem_info info (s);
447           if (y < info.miny_f_)
448             dy = dy >? info.miny_f_ - y;
449         }
450     } while (abs (dy) > epsilon);
451
452   test_pos++;
453   test_pos %= 4;
454 }
455
456 /*
457  FIXME
458  ugh.  this is broken and should be rewritten.
459   - [c8. c32 c32]
460  */
461 void
462 Beam::set_grouping (Rhythmic_grouping def, Rhythmic_grouping cur)
463 {
464   def.OK ();
465   cur.OK ();
466   assert (cur.children.size () == stems_.size ());
467
468   cur.split (def);
469
470   Array<int> b;
471   {
472     Array<int> flags;
473     for (int j=0; j <stems_.size (); j++)
474       {
475         Stem *s = stems_[j];
476
477         int f = s->flag_i_ - 2;
478         assert (f>0);
479         flags.push (f);
480       }
481     int fi =0;
482     b= cur.generate_beams (flags, fi);
483     b.insert (0,0);
484     b.push (0);
485     assert (stems_.size () == b.size ()/2);
486   }
487
488   for (int j=0, i=0; i < b.size () && j <stems_.size (); i+= 2, j++)
489     {
490       Stem *s = stems_[j];
491       s->beams_left_i_ = b[i];
492       s->beams_right_i_ = b[i+1];
493       multiple_i_ = multiple_i_ >? (b[i] >? b[i+1]);
494     }
495 }
496
497 /*
498   beams to go with one stem.
499   */
500 Molecule
501 Beam::stem_beams (Stem *here, Stem *next, Stem *prev) const
502 {
503   assert (!next || next->hpos_f () > here->hpos_f ());
504   assert (!prev || prev->hpos_f () < here->hpos_f ());
505
506   Real staffline_f = paper ()->rule_thickness ();
507   Real interbeam_f = paper ()->interbeam_f (multiple_i_);
508   Real internote_f = paper ()->internote_f (); 
509   Real beam_f = paper ()->beam_thickness_f ();
510
511   Real dy = interbeam_f;
512   Real stemdx = staffline_f;
513   Real sl = slope_f_* internote_f;
514   paper ()->lookup_l ()->beam (sl, 20 PT, 1 PT);
515
516   Molecule leftbeams;
517   Molecule rightbeams;
518
519   /* half beams extending to the left. */
520   if (prev)
521     {
522       int lhalfs= lhalfs = here->beams_left_i_ - prev->beams_right_i_ ;
523       int lwholebeams= here->beams_left_i_ <? prev->beams_right_i_ ;
524       Real w = (here->hpos_f () - prev->hpos_f ())/4 <? paper ()->note_width ();;
525       Atom a;
526       if (lhalfs)               // generates warnings if not
527         a =  paper ()->lookup_l ()->beam (sl, w, beam_f);
528       a.translate (Offset (-w, -w * sl));
529       for (int j = 0; j  < lhalfs; j++)
530         {
531           Atom b (a);
532           b.translate_axis (-dir_ * dy * (lwholebeams+j), Y_AXIS);
533           leftbeams.add (b);
534         }
535     }
536
537   if (next)
538     {
539       int rhalfs = here->beams_right_i_ - next->beams_left_i_;
540       int rwholebeams = here->beams_right_i_ <? next->beams_left_i_;
541
542       Real w = next->hpos_f () - here->hpos_f ();
543       Atom a = paper ()->lookup_l ()->beam (sl, w + stemdx, beam_f);
544       a.translate_axis( - stemdx/2, X_AXIS);
545       int j = 0;
546       Real gap_f = 0;
547       if (here->beam_gap_i_)
548         {
549           int nogap = rwholebeams - here->beam_gap_i_;
550           for (; j  < nogap; j++)
551             {
552               Atom b (a);
553               b.translate_axis (-dir_ * dy * j, Y_AXIS);
554               rightbeams.add (b);
555             }
556           // TODO: notehead widths differ for different types
557           gap_f = paper ()->note_width () / 2;
558           w -= 2 * gap_f;
559           a = paper ()->lookup_l ()->beam (sl, w + stemdx, beam_f);
560         }
561
562       for (; j  < rwholebeams; j++)
563         {
564           Atom b (a);
565           b.translate (Offset (gap_f, -dir_ * dy * j));
566           rightbeams.add (b);
567         }
568
569       w = w/4 <? paper ()->note_width ();
570       if (rhalfs)
571         a = paper ()->lookup_l ()->beam (sl, w, beam_f);
572
573       for (; j  < rwholebeams + rhalfs; j++)
574         {
575           Atom b (a);
576           b.translate_axis (-dir_ * dy * j, Y_AXIS);
577           rightbeams.add (b);
578         }
579
580     }
581   leftbeams.add (rightbeams);
582
583   /*
584     Does beam quanting think  of the asymetry of beams? 
585     Refpoint is on bottom of symbol. (FIXTHAT) --hwn.
586    */
587   return leftbeams;
588 }
589