]> git.donarmstrong.com Git - lilypond.git/blob - lily/beam.cc
release: 0.1.15
[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 "abbreviation-beam.hh"
22 #include "misc.hh"
23 #include "debug.hh"
24 #include "atom.hh"
25 #include "molecule.hh"
26 #include "leastsquares.hh"
27 #include "stem.hh"
28 #include "paper-def.hh"
29 #include "lookup.hh"
30 #include "grouping.hh"
31 #include "stem-info.hh"
32
33
34 IMPLEMENT_IS_TYPE_B1(Beam, Spanner);
35
36 Beam::Beam()
37 {
38   slope = 0;
39   left_pos = 0.0;
40 }
41
42 void
43 Beam::add (Stem*s)
44 {
45   stems.push (s);
46   s->add_dependency (this);
47   s->beam_l_ = this;
48
49   if (!spanned_drul_[LEFT])
50     set_bounds (LEFT,s);
51   else
52     set_bounds (RIGHT,s);
53 }
54
55 Molecule*
56 Beam::brew_molecule_p() const 
57 {
58   Molecule *mol_p = new Molecule;
59   // huh? inter-what
60   //    Real inter_f = paper()->interbeam_f ();
61   Real inter_f = paper()->internote_f ();
62   Real x0 = stems[0]->hpos_f();
63   for (int j=0; j <stems.size(); j++) 
64     {
65       Stem *i = stems[j];
66       Stem * prev = (j > 0)? stems[j-1] : 0;
67       Stem * next = (j < stems.size()-1) ? stems[j+1] :0;
68
69       Molecule sb = stem_beams (i, next, prev);
70       Real  x = i->hpos_f()-x0;
71       sb.translate (Offset (x, (x * slope  + left_pos)* inter_f));
72       mol_p->add (sb);
73     }
74   mol_p->translate (x0 - spanned_drul_[LEFT]->absolute_coordinate(X_AXIS), X_AXIS);
75   return mol_p;
76 }
77
78 Offset
79 Beam::center() const
80 {
81   Real w=(paper()->note_width () + width ().length ())/2.0;
82   return Offset (w, (left_pos + w* slope)*paper()->internote_f ());
83 }
84
85 void
86 Beam::do_pre_processing()
87 {
88   if (!dir_)
89     set_default_dir();
90 }
91
92 void
93 Beam::do_print() const
94 {
95 #ifndef NPRINT
96   DOUT << "slope " <<slope << "left ypos " << left_pos;
97   Spanner::do_print();
98 #endif
99 }
100
101 void
102 Beam::do_post_processing()
103 {
104   if (stems.size() < 2) 
105     {
106       warning ("Beam with less than 2 stems");
107       transparent_b_ = true;
108       return ;
109     }
110   solve_slope();    
111   set_stemlens();
112 }
113
114 void
115 Beam::do_substitute_dependent (Score_elem*o,Score_elem*n)
116 {
117   if (o->is_type_b (Stem::static_name())) 
118       stems.substitute ((Stem*)o->item(),  n?(Stem*) n->item ():0);
119 }
120
121 Interval
122 Beam::do_width() const
123 {
124   return Interval (stems[0]->hpos_f(),
125                    stems.top()->hpos_f ());
126 }
127
128 void
129 Beam::set_default_dir()
130 {
131   int up = 0, down = 0;
132   int up_count = 0, down_count = 0;
133
134   for (int i=0; i <stems.size(); i++) 
135     {
136       Stem *sl = stems[i];
137       int cur_down = sl->get_center_distance_from_top();
138       int cur_up = sl->get_center_distance_from_bottom();
139       if (cur_down) 
140         {
141           down += cur_down;
142           down_count++;
143         }
144       if (cur_up) 
145         {
146           up += cur_up;
147           up_count++;
148         }
149     }
150   if (!down)
151     down_count = 1;
152   if (!up)
153     up_count = 1;
154
155   // the following relation is equal to
156   //        up / up_count > down / down_count
157   dir_ = (up * down_count > down * up_count) ? UP : DOWN;
158
159   for (int i=0; i <stems.size(); i++) 
160     {
161       Stem *sl = stems[i];
162       sl->dir_ = dir_;
163     }
164 }
165
166 /*
167   should use minimum energy formulation (cf linespacing)
168
169   [todo]
170   the y of the (start) of the beam should be quantisized,
171   so that no stafflines appear just in between two beam-flags
172
173 */
174 void
175 Beam::solve_slope()
176 {
177   Array<Stem_info> sinfo;
178   for (int j=0; j <stems.size(); j++) 
179     {
180       Stem *i = stems[j];
181
182       i->set_default_extents();
183       if (i->invisible_b())
184         continue;
185         
186       Stem_info info (i);
187       sinfo.push (info);
188     }
189   if (! sinfo.size())
190     slope = left_pos = 0;
191   else if (sinfo.size() == 1) 
192     {
193       slope = 0;
194       left_pos = sinfo[0].idealy_f_;
195     }
196   else 
197     {
198         
199       Real leftx = sinfo[0].x;
200       Least_squares l;
201       for (int i=0; i < sinfo.size(); i++) 
202         {
203           sinfo[i].x -= leftx;
204           l.input.push (Offset (sinfo[i].x, sinfo[i].idealy_f_));
205         }
206
207       l.minimise (slope, left_pos);
208     }
209   
210   Real dy = 0.0;
211   for (int i=0; i < sinfo.size(); i++) 
212     {
213       Real y = sinfo[i].x * slope + left_pos;
214       Real my = sinfo[i].miny_f_;
215
216       if (my - y > dy)
217         dy = my -y;     
218     }
219   left_pos += dy;
220   left_pos *= dir_;    
221
222   slope *= dir_;
223
224   /*
225     This neat trick is by Werner Lemberg, damped = tanh (slope) corresponds
226     with some tables in [Wanske]
227     */
228   slope = 0.6 * tanh (slope);  
229
230   // ugh
231   Real sl = slope*paper()->internote_f ();
232   paper()->lookup_l ()->beam (sl, 20 PT);
233   slope = sl /paper()->internote_f ();
234 }
235
236 void
237 Beam::set_stemlens()
238 {
239   Real x0 = stems[0]->hpos_f();    
240   for (int j=0; j <stems.size(); j++) 
241     {
242       Stem *s = stems[j];
243
244       Real x =  s->hpos_f()-x0;
245       s->set_stemend (left_pos + slope * x);    
246     }
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 = 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 /*
285   beams to go with one stem.
286   */
287 Molecule
288 Beam::stem_beams (Stem *here, Stem *next, Stem *prev) const
289 {
290   assert (!next || next->hpos_f() > here->hpos_f ());
291   assert (!prev || prev->hpos_f() < here->hpos_f ());
292   //    Real dy=paper()->internote_f ()*2;
293   Real dy = paper()->interbeam_f ();
294   Real stemdx = paper()->rule_thickness ();
295   Real sl = slope*paper()->internote_f ();
296   paper()->lookup_l ()->beam (sl, 20 PT);
297
298   Molecule leftbeams;
299   Molecule rightbeams;
300
301   /* half beams extending to the left. */
302   if (prev) 
303     {
304       int lhalfs= lhalfs = here->beams_left_i_ - prev->beams_right_i_ ;
305       int lwholebeams= here->beams_left_i_ <? prev->beams_right_i_ ;
306       Real w = (here->hpos_f () - prev->hpos_f ())/4;
307       Atom a;
308       if (lhalfs)               // generates warnings if not
309         a =  paper()->lookup_l ()->beam (sl, w);
310       a.translate (Offset (-w, -w * sl));
311       for (int j = 0; j  < lhalfs; j++) 
312         {
313           Atom b (a);
314           b.translate (-dir_ * dy * (lwholebeams+j), Y_AXIS);
315           leftbeams.add (b);
316         }
317     }
318         
319   if (next)
320     {
321       int rhalfs = here->beams_right_i_ - next->beams_left_i_;
322       int rwholebeams = here->beams_right_i_ <? next->beams_left_i_; 
323
324       Real w = next->hpos_f() - here->hpos_f ();
325       Atom a = paper()->lookup_l ()->beam (sl, w + stemdx);
326
327       int j = 0;
328       Real gap_f = 0;
329       if (here->beam_gap_i_)
330         {
331           int nogap = rwholebeams - here->beam_gap_i_;
332           for (; j  < nogap; j++) 
333             {
334               Atom b (a);
335               b.translate (-dir_ * dy * j, Y_AXIS);
336               rightbeams.add (b); 
337             }
338           // TODO: notehead widths differ for different types
339           gap_f = paper()->note_width () / 2;
340           w -= 2 * gap_f;
341           a = paper()->lookup_l ()->beam (sl, w + stemdx);
342         }
343
344       for (; j  < rwholebeams; j++) 
345         {
346           Atom b (a);
347           b.translate (Offset (gap_f, -dir_ * dy * j));
348           rightbeams.add (b); 
349         }
350
351       w /= 4;
352       if (rhalfs)
353         a = paper()->lookup_l ()->beam (sl, w);
354         
355       for (; j  < rwholebeams + rhalfs; j++) 
356         {
357           Atom b (a);
358           b.translate (-dir_ * dy * j, Y_AXIS);
359           rightbeams.add (b); 
360         }
361         
362     }
363   leftbeams.add (rightbeams);
364   return leftbeams;
365 }