]> git.donarmstrong.com Git - lilypond.git/blob - lily/beam.cc
70a1ce88d0ee69d130d2ead7fa137d8d33d6e1dc
[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 Han-Wen Nienhuys <hanwen@stack.nl>
7
8   TODO
9
10   Less hairy code.  knee: ([\stem 1; c8 \stem -1; c8]
11   
12 */
13
14 #include <math.h>
15
16 #include "p-col.hh"
17 #include "varray.hh"
18 #include "proto.hh"
19 #include "dimen.hh"
20 #include "beam.hh"
21 #include "misc.hh"
22 #include "debug.hh"
23 #include "symbol.hh"
24 #include "molecule.hh"
25 #include "leastsquares.hh"
26 #include "stem.hh"
27 #include "paper-def.hh"
28 #include "lookup.hh"
29 #include "grouping.hh"
30
31
32
33 struct Stem_info {
34   Real x;
35   int dir_i_;
36   Real idealy_f_;
37   Real miny_f_;
38   int beams_i_;
39
40   Stem_info(){}
41   Stem_info (Stem const *);
42 };
43
44 Stem_info::Stem_info (Stem const *s)
45 {
46   x = s->hpos_f();
47   dir_i_ = s->dir_i_;
48   beams_i_ = intlog2( s->flag_i_) - 2;
49
50   /*
51    [todo] 
52        * get algorithm
53          * runtime
54
55    Breitkopf + H\"artel:
56        miny_f_ = interline + #beams * interbeam
57          ideal8 = 2 * interline + interbeam
58          ideal16,32,64,128 = 1.5 * interline + #beams * interbeam
59
60    * B\"arenreiter:
61        miny_f_ = interline + #beams * interbeam
62          ideal8,16 = 2 interline + #beams * interbeam
63          ideal32,64,128 = 1.5 interline + #beams * interbeam
64        
65    */
66
67   Real notehead_y = s->paper()->interline_f ();
68   // huh? why do i need the / 2
69 //    Real interbeam_f = s->paper()->interbeam_f ();
70   Real interbeam_f = s->paper()->interbeam_f () / 2;
71          
72   /* well eh, huh?
73   idealy_f_  = dir_i_ * s->stem_start_f() + beams_i_ * interbeam_f; 
74   if ( beams_i_ < 3)
75         idealy_f_ += 2 * interline_f;
76   else
77         idealy_f_ += 1.5 * interline_f;
78   */
79
80   idealy_f_  = dir_i_ * s->stem_end_f();
81
82   miny_f_ = dir_i_ * s->stem_start_f() + notehead_y + beams_i_ * interbeam_f;
83
84   idealy_f_ =  miny_f_ >? idealy_f_;
85 //    assert (miny_f_ <= idealy_f_);
86 }
87
88
89 /* *************** */
90
91
92 Offset
93 Beam::center()const
94 {
95   Real w=(paper()->note_width () + width ().length ())/2.0;
96   return Offset (w, (left_pos + w* slope)*paper()->internote_f ());
97 }
98
99
100 Beam::Beam()
101 {
102   slope = 0;
103   left_pos = 0.0;
104 }
105
106 void
107 Beam::add (Stem*s)
108 {
109   stems.push (s);
110   s->add_dependency (this);
111   s->print_flag_b_ = false;
112 }
113
114 void
115 Beam::set_default_dir()
116 {
117   int up = 0, down = 0;
118   int up_count = 0, down_count = 0;
119
120   for (int i=0; i <stems.size(); i++) 
121     {
122         Stem *sl = stems[i];
123         int cur_down = sl->get_center_distance_from_top();
124         int cur_up = sl->get_center_distance_from_bottom();
125         if (cur_down) 
126           {
127             down += cur_down;
128             down_count++;
129           }
130         if (cur_up) 
131           {
132             up += cur_up;
133             up_count++;
134           }
135     }
136   if (!down)
137         down_count = 1;
138   if (!up)
139         up_count = 1;
140
141   // the following relation is equal to
142   //        up / up_count > down / down_count
143   dir_i_ = (up * down_count > down * up_count) ? 1 : -1;
144
145    for (int i=0; i <stems.size(); i++) 
146    {
147         Stem *sl = stems[i];
148         sl->dir_i_ = dir_i_;
149    }
150 }
151
152 /*
153   should use minimum energy formulation (cf linespacing)
154
155   [todo]
156   the y of the (start) of the beam should be quantisized,
157   so that no stafflines appear just in between two beam-flags
158
159 */
160 void
161 Beam::solve_slope()
162 {
163   Array<Stem_info> sinfo;
164   for (int j=0; j <stems.size(); j++) 
165     {
166         Stem *i = stems[j];
167
168         i->set_default_extents();
169         if (i->invisible_b())
170             continue;
171         
172         Stem_info info (i);
173         sinfo.push (info);
174     }
175   if (! sinfo.size())
176         slope = left_pos = 0;
177   else if (sinfo.size() == 1) 
178     {
179         slope = 0;
180         left_pos = sinfo[0].idealy_f_;
181     }
182   else 
183     {
184         
185         Real leftx = sinfo[0].x;
186         Least_squares l;
187         for (int i=0; i < sinfo.size(); i++) 
188           {
189             sinfo[i].x -= leftx;
190             l.input.push (Offset (sinfo[i].x, sinfo[i].idealy_f_));
191           }
192
193         l.minimise (slope, left_pos);
194     }
195   
196   Real dy = 0.0;
197   for (int i=0; i < sinfo.size(); i++) 
198     {
199         Real y = sinfo[i].x * slope + left_pos;
200         Real my = sinfo[i].miny_f_;
201
202         if (my - y > dy)
203             dy = my -y; 
204     }
205   left_pos += dy;
206   left_pos *= dir_i_;    
207
208   slope *= dir_i_;
209
210   /*
211     This neat trick is by Werner Lemberg, damped = tanh (slope) corresponds
212     with some tables in [Wanske]
213    */
214   slope = 0.6 * tanh (slope);  
215
216                                 // ugh
217   Real sl = slope*paper()->internote_f ();
218   paper()->lookup_l ()->beam (sl, 20 PT);
219   slope = sl /paper()->internote_f ();
220 }
221
222 void
223 Beam::set_stemlens()
224 {
225   Real x0 = stems[0]->hpos_f();    
226   for (int j=0; j <stems.size(); j++) 
227     {
228         Stem *s = stems[j];
229
230         Real x =  s->hpos_f()-x0;
231         s->set_stemend (left_pos + slope * x);  
232     }
233 }
234
235
236 void
237 Beam::do_post_processing()
238 {
239   if ( stems.size() < 2) 
240     {
241         warning ("Beam with less than 2 stems");
242         transparent_b_ = true;
243         return ;
244     }
245   solve_slope();    
246   set_stemlens();
247 }
248
249 void
250 Beam::set_grouping (Rhythmic_grouping def, Rhythmic_grouping cur)
251 {
252   def.OK();
253   cur.OK();
254   assert (cur.children.size() == stems.size ());
255   
256   cur.split (def);
257
258   Array<int> b;
259   {
260         Array<int> flags;
261         for (int j=0; j <stems.size(); j++) 
262           {
263             Stem *s = stems[j];
264
265             int f = intlog2(abs (s->flag_i_))-2;
266             assert (f>0);
267             flags.push (f);
268           }
269         int fi =0;
270         b= cur.generate_beams (flags, fi);
271         b.insert (0,0);
272         b.push (0);
273         assert (stems.size() == b.size ()/2);
274     }
275
276   for (int j=0, i=0; i < b.size() && j <stems.size (); i+= 2, j++) 
277     {
278         Stem *s = stems[j];
279         s->beams_left_i_ = b[i];
280         s->beams_right_i_ = b[i+1];
281     }
282 }
283
284 void
285 Beam::do_pre_processing()
286 {
287   if (!dir_i_)
288         set_default_dir();
289
290 }
291
292
293 Interval
294 Beam::do_width() const
295 {
296   return Interval (stems[0]->hpos_f(),
297                      stems.top()->hpos_f ());
298 }
299
300 /*
301   beams to go with one stem.
302   */
303 Molecule
304 Beam::stem_beams (Stem *here, Stem *next, Stem *prev)const
305 {
306   assert (!next || next->hpos_f() > here->hpos_f () );
307   assert (!prev || prev->hpos_f() < here->hpos_f () );
308 //    Real dy=paper()->internote_f ()*2;
309   Real dy = paper()->interbeam_f ();
310   Real stemdx = paper()->rule_thickness ();
311   Real sl = slope*paper()->internote_f ();
312   paper()->lookup_l ()->beam (sl, 20 PT);
313
314   Molecule leftbeams;
315   Molecule rightbeams;
316
317   /* half beams extending to the left. */
318   if (prev) 
319     {
320         int lhalfs= lhalfs = here->beams_left_i_ - prev->beams_right_i_ ;
321         int lwholebeams= here->beams_left_i_ <? prev->beams_right_i_ ;
322         Real w = (here->hpos_f() - prev->hpos_f ())/4;
323         Symbol dummy;
324         Atom a (dummy);
325         if (lhalfs)             // generates warnings if not
326             a =  paper()->lookup_l ()->beam (sl, w);
327         a.translate (Offset (-w, -w * sl));
328         for (int j = 0; j  < lhalfs; j++) 
329           {
330             Atom b (a);
331             b.translate (-dir_i_ * dy * (lwholebeams+j), Y_AXIS);
332             leftbeams.add (b);
333           }
334     }
335         
336   if (next)
337     {
338         int rhalfs = here->beams_right_i_ - next->beams_left_i_;
339         int rwholebeams = here->beams_right_i_ <? next->beams_left_i_; 
340
341         Real w = next->hpos_f() - here->hpos_f ();
342         Atom a = paper()->lookup_l ()->beam (sl, w + stemdx);
343         
344         int j = 0;
345         for (; j  < rwholebeams; j++) 
346           {
347             Atom b (a);
348             b.translate (-dir_i_ * dy * j, Y_AXIS);
349             rightbeams.add (b); 
350           }
351
352         w /= 4;
353         if (rhalfs)
354             a = paper()->lookup_l ()->beam (sl, w);
355         
356         for (; j  < rwholebeams + rhalfs; j++) 
357           {
358             Atom b (a);
359             b.translate (-dir_i_ * dy * j, Y_AXIS);
360             rightbeams.add (b); 
361           }
362         
363     }
364   leftbeams.add (rightbeams);
365   return leftbeams;
366 }
367
368
369 Molecule*
370 Beam::brew_molecule_p() const 
371 {
372  
373   Molecule *mol_p = new Molecule;
374   // huh? inter-what
375 //    Real inter_f = paper()->interbeam_f ();
376   Real inter_f = paper()->internote_f ();
377   Real x0 = stems[0]->hpos_f();
378   for (int j=0; j <stems.size(); j++) 
379     {
380         Stem *i = stems[j];
381         Stem * prev = (j > 0)? stems[j-1] : 0;
382         Stem * next = (j < stems.size()-1) ? stems[j+1] :0;
383
384         Molecule sb = stem_beams (i, next, prev);
385         Real  x = i->hpos_f()-x0;
386         sb.translate (Offset (x, (x * slope  + left_pos)* inter_f));
387         mol_p->add (sb);
388     }
389   mol_p->translate (x0 - left_col_l_->hpos_f_, X_AXIS);
390   return mol_p;
391 }
392
393
394 IMPLEMENT_IS_TYPE_B1(Beam, Spanner);
395
396 void
397 Beam::do_print()const
398 {
399 #ifndef NPRINT
400   DOUT << "slope " <<slope << "left ypos " << left_pos;
401   Spanner::do_print();
402 #endif
403 }
404
405 void
406 Beam::do_substitute_dependent (Score_elem*o,Score_elem*n)
407 {
408   if (o->is_type_b (Stem::static_name())) 
409     {
410         stems.substitute ((Stem*)o->item(),  n?(Stem*) n->item ():0);
411     }
412 }