]> git.donarmstrong.com Git - lilypond.git/blob - lily/beam.cc
release: 0.1.25
[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_axis (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   Drul_array<int> total;
132   total[UP]  = total[DOWN] = 0;
133   Drul_array<int> count; 
134   count[UP]  = count[DOWN] = 0;
135   Direction d = DOWN;
136   
137   for (int i=0; i <stems.size(); i++)
138     do {
139       Stem *s = stems[i];
140       int current = s->dir_ 
141         ? (1 + d * s->dir_)/2
142         : s->get_center_distance(Direction(-d));
143
144       if (current)
145         {
146           total[d] += current;
147           count[d] ++;
148         }
149
150     } while ((d *= -1) != DOWN);
151   
152    do {
153     if (!total[d])
154       count[d] = 1;
155   } while ((d *= -1) != DOWN);
156   
157   /* the following relation is equal to
158           up / up_count > down / down_count
159           */
160   dir_ = (total[UP] * count[DOWN] > total[DOWN] * count[UP]) ? UP : DOWN;
161
162   for (int i=0; i <stems.size(); i++)
163     {
164       Stem *sl = stems[i];
165       sl->dir_ = dir_;
166     }
167 }
168
169 /*
170   should use minimum energy formulation (cf linespacing)
171
172   [todo]
173   the y of the (start) of the beam should be quantisized,
174   so that no stafflines appear just in between two beam-flags
175
176 */
177 void
178 Beam::solve_slope()
179 {
180   Array<Stem_info> sinfo;
181   for (int j=0; j <stems.size(); j++)
182     {
183       Stem *i = stems[j];
184
185       i->set_default_extents();
186       if (i->invisible_b())
187         continue;
188
189       Stem_info info (i);
190       sinfo.push (info);
191     }
192   if (! sinfo.size())
193     slope = left_pos = 0;
194   else if (sinfo.size() == 1)
195     {
196       slope = 0;
197       left_pos = sinfo[0].idealy_f_;
198     }
199   else
200     {
201
202       Real leftx = sinfo[0].x;
203       Least_squares l;
204       for (int i=0; i < sinfo.size(); i++)
205         {
206           sinfo[i].x -= leftx;
207           l.input.push (Offset (sinfo[i].x, sinfo[i].idealy_f_));
208         }
209
210       l.minimise (slope, left_pos);
211     }
212
213   Real dy = 0.0;
214   for (int i=0; i < sinfo.size(); i++)
215     {
216       Real y = sinfo[i].x * slope + left_pos;
217       Real my = sinfo[i].miny_f_;
218
219       if (my - y > dy)
220         dy = my -y;
221     }
222   left_pos += dy;
223   left_pos *= dir_;
224
225   slope *= dir_;
226
227   /*
228     This neat trick is by Werner Lemberg, damped = tanh (slope) corresponds
229     with some tables in [Wanske]
230     */
231   slope = 0.6 * tanh (slope);
232
233   // ugh
234   Real sl = slope*paper()->internote_f ();
235   paper()->lookup_l ()->beam (sl, 20 PT);
236   slope = sl /paper()->internote_f ();
237 }
238
239 void
240 Beam::set_stemlens()
241 {
242   Real x0 = stems[0]->hpos_f();
243   for (int j=0; j <stems.size(); j++)
244     {
245       Stem *s = stems[j];
246
247       Real x =  s->hpos_f()-x0;
248       s->set_stemend (left_pos + slope * x);
249     }
250 }
251
252 void
253 Beam::set_grouping (Rhythmic_grouping def, Rhythmic_grouping cur)
254 {
255   def.OK();
256   cur.OK();
257   assert (cur.children.size() == stems.size ());
258
259   cur.split (def);
260
261   Array<int> b;
262   {
263     Array<int> flags;
264     for (int j=0; j <stems.size(); j++)
265       {
266         Stem *s = stems[j];
267
268         int f = s->flag_i_ - 2;
269         assert (f>0);
270         flags.push (f);
271       }
272     int fi =0;
273     b= cur.generate_beams (flags, fi);
274     b.insert (0,0);
275     b.push (0);
276     assert (stems.size() == b.size ()/2);
277   }
278
279   for (int j=0, i=0; i < b.size() && j <stems.size (); i+= 2, j++)
280     {
281       Stem *s = stems[j];
282       s->beams_left_i_ = b[i];
283       s->beams_right_i_ = b[i+1];
284     }
285 }
286
287 /*
288   beams to go with one stem.
289   */
290 Molecule
291 Beam::stem_beams (Stem *here, Stem *next, Stem *prev) const
292 {
293   assert (!next || next->hpos_f() > here->hpos_f ());
294   assert (!prev || prev->hpos_f() < here->hpos_f ());
295   //    Real dy=paper()->internote_f ()*2;
296   Real dy = paper()->interbeam_f ();
297   Real stemdx = paper()->rule_thickness ();
298   Real sl = slope*paper()->internote_f ();
299   paper()->lookup_l ()->beam (sl, 20 PT);
300
301   Molecule leftbeams;
302   Molecule rightbeams;
303
304   /* half beams extending to the left. */
305   if (prev)
306     {
307       int lhalfs= lhalfs = here->beams_left_i_ - prev->beams_right_i_ ;
308       int lwholebeams= here->beams_left_i_ <? prev->beams_right_i_ ;
309       Real w = (here->hpos_f () - prev->hpos_f ())/4;
310       Atom a;
311       if (lhalfs)               // generates warnings if not
312         a =  paper()->lookup_l ()->beam (sl, w);
313       a.translate (Offset (-w, -w * sl));
314       for (int j = 0; j  < lhalfs; j++)
315         {
316           Atom b (a);
317           b.translate_axis (-dir_ * dy * (lwholebeams+j), Y_AXIS);
318           leftbeams.add (b);
319         }
320     }
321
322   if (next)
323     {
324       int rhalfs = here->beams_right_i_ - next->beams_left_i_;
325       int rwholebeams = here->beams_right_i_ <? next->beams_left_i_;
326
327       Real w = next->hpos_f() - here->hpos_f ();
328       Atom a = paper()->lookup_l ()->beam (sl, w + stemdx);
329
330       int j = 0;
331       Real gap_f = 0;
332       if (here->beam_gap_i_)
333         {
334           int nogap = rwholebeams - here->beam_gap_i_;
335           for (; j  < nogap; j++)
336             {
337               Atom b (a);
338               b.translate_axis (-dir_ * dy * j, Y_AXIS);
339               rightbeams.add (b);
340             }
341           // TODO: notehead widths differ for different types
342           gap_f = paper()->note_width () / 2;
343           w -= 2 * gap_f;
344           a = paper()->lookup_l ()->beam (sl, w + stemdx);
345         }
346
347       for (; j  < rwholebeams; j++)
348         {
349           Atom b (a);
350           b.translate (Offset (gap_f, -dir_ * dy * j));
351           rightbeams.add (b);
352         }
353
354       w /= 4;
355       if (rhalfs)
356         a = paper()->lookup_l ()->beam (sl, w);
357
358       for (; j  < rwholebeams + rhalfs; j++)
359         {
360           Atom b (a);
361           b.translate_axis (-dir_ * dy * j, Y_AXIS);
362           rightbeams.add (b);
363         }
364
365     }
366   leftbeams.add (rightbeams);
367   return leftbeams;
368 }