]> git.donarmstrong.com Git - lilypond.git/blob - lily/beam.cc
release: 0.1.11
[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_;
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_ = s->dir_;
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_ * 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_ * s->stem_end_f();
81
82   miny_f_ = dir_ * 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   if (!spanned_drul_[LEFT])
114     set_bounds(LEFT,s);
115   set_bounds(RIGHT,s);
116 }
117
118 void
119 Beam::set_default_dir()
120 {
121   int up = 0, down = 0;
122   int up_count = 0, down_count = 0;
123
124   for (int i=0; i <stems.size(); i++) 
125     {
126       Stem *sl = stems[i];
127       int cur_down = sl->get_center_distance_from_top();
128       int cur_up = sl->get_center_distance_from_bottom();
129       if (cur_down) 
130         {
131           down += cur_down;
132           down_count++;
133         }
134       if (cur_up) 
135         {
136           up += cur_up;
137           up_count++;
138         }
139     }
140   if (!down)
141     down_count = 1;
142   if (!up)
143     up_count = 1;
144
145   // the following relation is equal to
146   //        up / up_count > down / down_count
147   dir_ = (up * down_count > down * up_count) ? UP : DOWN;
148
149   for (int i=0; i <stems.size(); i++) 
150     {
151       Stem *sl = stems[i];
152       sl->dir_ = dir_;
153     }
154 }
155
156 /*
157   should use minimum energy formulation (cf linespacing)
158
159   [todo]
160   the y of the (start) of the beam should be quantisized,
161   so that no stafflines appear just in between two beam-flags
162
163 */
164 void
165 Beam::solve_slope()
166 {
167   Array<Stem_info> sinfo;
168   for (int j=0; j <stems.size(); j++) 
169     {
170       Stem *i = stems[j];
171
172       i->set_default_extents();
173       if (i->invisible_b())
174         continue;
175         
176       Stem_info info (i);
177       sinfo.push (info);
178     }
179   if (! sinfo.size())
180     slope = left_pos = 0;
181   else if (sinfo.size() == 1) 
182     {
183       slope = 0;
184       left_pos = sinfo[0].idealy_f_;
185     }
186   else 
187     {
188         
189       Real leftx = sinfo[0].x;
190       Least_squares l;
191       for (int i=0; i < sinfo.size(); i++) 
192         {
193           sinfo[i].x -= leftx;
194           l.input.push (Offset (sinfo[i].x, sinfo[i].idealy_f_));
195         }
196
197       l.minimise (slope, left_pos);
198     }
199   
200   Real dy = 0.0;
201   for (int i=0; i < sinfo.size(); i++) 
202     {
203       Real y = sinfo[i].x * slope + left_pos;
204       Real my = sinfo[i].miny_f_;
205
206       if (my - y > dy)
207         dy = my -y;     
208     }
209   left_pos += dy;
210   left_pos *= dir_;    
211
212   slope *= dir_;
213
214   /*
215     This neat trick is by Werner Lemberg, damped = tanh (slope) corresponds
216     with some tables in [Wanske]
217     */
218   slope = 0.6 * tanh (slope);  
219
220   // ugh
221   Real sl = slope*paper()->internote_f ();
222   paper()->lookup_l ()->beam (sl, 20 PT);
223   slope = sl /paper()->internote_f ();
224 }
225
226 void
227 Beam::set_stemlens()
228 {
229   Real x0 = stems[0]->hpos_f();    
230   for (int j=0; j <stems.size(); j++) 
231     {
232       Stem *s = stems[j];
233
234       Real x =  s->hpos_f()-x0;
235       s->set_stemend (left_pos + slope * x);    
236     }
237 }
238
239
240 void
241 Beam::do_post_processing()
242 {
243   if (stems.size() < 2) 
244     {
245       warning ("Beam with less than 2 stems");
246       transparent_b_ = true;
247       return ;
248     }
249   solve_slope();    
250   set_stemlens();
251 }
252
253 void
254 Beam::set_grouping (Rhythmic_grouping def, Rhythmic_grouping cur)
255 {
256   def.OK();
257   cur.OK();
258   assert (cur.children.size() == stems.size ());
259   
260   cur.split (def);
261
262   Array<int> b;
263   {
264     Array<int> flags;
265     for (int j=0; j <stems.size(); j++) 
266       {
267         Stem *s = stems[j];
268
269         int f = intlog2(abs (s->flag_i_))-2;
270         assert (f>0);
271         flags.push (f);
272       }
273     int fi =0;
274     b= cur.generate_beams (flags, fi);
275     b.insert (0,0);
276     b.push (0);
277     assert (stems.size() == b.size ()/2);
278   }
279
280   for (int j=0, i=0; i < b.size() && j <stems.size (); i+= 2, j++) 
281     {
282       Stem *s = stems[j];
283       s->beams_left_i_ = b[i];
284       s->beams_right_i_ = b[i+1];
285     }
286 }
287
288 void
289 Beam::do_pre_processing()
290 {
291   if (!dir_)
292     set_default_dir();
293
294 }
295
296
297 Interval
298 Beam::do_width() const
299 {
300   return Interval (stems[0]->hpos_f(),
301                    stems.top()->hpos_f ());
302 }
303
304 /*
305   beams to go with one stem.
306   */
307 Molecule
308 Beam::stem_beams (Stem *here, Stem *next, Stem *prev) const
309 {
310   assert (!next || next->hpos_f() > here->hpos_f ());
311   assert (!prev || prev->hpos_f() < here->hpos_f ());
312   //    Real dy=paper()->internote_f ()*2;
313   Real dy = paper()->interbeam_f ();
314   Real stemdx = paper()->rule_thickness ();
315   Real sl = slope*paper()->internote_f ();
316   paper()->lookup_l ()->beam (sl, 20 PT);
317
318   Molecule leftbeams;
319   Molecule rightbeams;
320
321   /* half beams extending to the left. */
322   if (prev) 
323     {
324       int lhalfs= lhalfs = here->beams_left_i_ - prev->beams_right_i_ ;
325       int lwholebeams= here->beams_left_i_ <? prev->beams_right_i_ ;
326       Real w = (here->hpos_f() - prev->hpos_f ())/4;
327       Symbol dummy;
328       Atom a (dummy);
329       if (lhalfs)               // generates warnings if not
330         a =  paper()->lookup_l ()->beam (sl, w);
331       a.translate (Offset (-w, -w * sl));
332       for (int j = 0; j  < lhalfs; j++) 
333         {
334           Atom b (a);
335           b.translate (-dir_ * dy * (lwholebeams+j), Y_AXIS);
336           leftbeams.add (b);
337         }
338     }
339         
340   if (next)
341     {
342       int rhalfs = here->beams_right_i_ - next->beams_left_i_;
343       int rwholebeams = here->beams_right_i_ <? next->beams_left_i_; 
344
345       Real w = next->hpos_f() - here->hpos_f ();
346       Atom a = paper()->lookup_l ()->beam (sl, w + stemdx);
347         
348       int j = 0;
349       for (; j  < rwholebeams; j++) 
350         {
351           Atom b (a);
352           b.translate (-dir_ * dy * j, Y_AXIS);
353           rightbeams.add (b); 
354         }
355
356       w /= 4;
357       if (rhalfs)
358         a = paper()->lookup_l ()->beam (sl, w);
359         
360       for (; j  < rwholebeams + rhalfs; j++) 
361         {
362           Atom b (a);
363           b.translate (-dir_ * dy * j, Y_AXIS);
364           rightbeams.add (b); 
365         }
366         
367     }
368   leftbeams.add (rightbeams);
369   return leftbeams;
370 }
371
372
373 Molecule*
374 Beam::brew_molecule_p() const 
375 {
376  
377   Molecule *mol_p = new Molecule;
378   // huh? inter-what
379   //    Real inter_f = paper()->interbeam_f ();
380   Real inter_f = paper()->internote_f ();
381   Real x0 = stems[0]->hpos_f();
382   for (int j=0; j <stems.size(); j++) 
383     {
384       Stem *i = stems[j];
385       Stem * prev = (j > 0)? stems[j-1] : 0;
386       Stem * next = (j < stems.size()-1) ? stems[j+1] :0;
387
388       Molecule sb = stem_beams (i, next, prev);
389       Real  x = i->hpos_f()-x0;
390       sb.translate (Offset (x, (x * slope  + left_pos)* inter_f));
391       mol_p->add (sb);
392     }
393   mol_p->translate (x0 - spanned_drul_[LEFT]->absolute_coordinate(X_AXIS), X_AXIS);
394   return mol_p;
395 }
396
397
398 IMPLEMENT_IS_TYPE_B1(Beam, Spanner);
399
400 void
401 Beam::do_print() const
402 {
403 #ifndef NPRINT
404   DOUT << "slope " <<slope << "left ypos " << left_pos;
405   Spanner::do_print();
406 #endif
407 }
408
409 void
410 Beam::do_substitute_dependent (Score_elem*o,Score_elem*n)
411 {
412   if (o->is_type_b (Stem::static_name())) 
413     {
414       stems.substitute ((Stem*)o->item(),  n?(Stem*) n->item ():0);
415     }
416 }