]> git.donarmstrong.com Git - lilypond.git/blob - lily/beam.cc
release: 0.1.33
[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   /* 
243      should check for visibility of stem..
244    */
245   Real x0 = stems[0]->hpos_f();
246   for (int j=0; j <stems.size(); j++)
247     {
248       Stem *s = stems[j];
249
250       Real x =  s->hpos_f()-x0;
251       s->set_stemend (left_pos + slope * x);
252     }
253 }
254
255 void
256 Beam::set_grouping (Rhythmic_grouping def, Rhythmic_grouping cur)
257 {
258   def.OK();
259   cur.OK();
260   assert (cur.children.size() == stems.size ());
261
262   cur.split (def);
263
264   Array<int> b;
265   {
266     Array<int> flags;
267     for (int j=0; j <stems.size(); j++)
268       {
269         Stem *s = stems[j];
270
271         int f = s->flag_i_ - 2;
272         assert (f>0);
273         flags.push (f);
274       }
275     int fi =0;
276     b= cur.generate_beams (flags, fi);
277     b.insert (0,0);
278     b.push (0);
279     assert (stems.size() == b.size ()/2);
280   }
281
282   for (int j=0, i=0; i < b.size() && j <stems.size (); i+= 2, j++)
283     {
284       Stem *s = stems[j];
285       s->beams_left_i_ = b[i];
286       s->beams_right_i_ = b[i+1];
287     }
288 }
289
290 /*
291   beams to go with one stem.
292   */
293 Molecule
294 Beam::stem_beams (Stem *here, Stem *next, Stem *prev) const
295 {
296   assert (!next || next->hpos_f() > here->hpos_f ());
297   assert (!prev || prev->hpos_f() < here->hpos_f ());
298   //    Real dy=paper()->internote_f ()*2;
299   Real dy = paper()->interbeam_f ();
300   Real stemdx = paper()->rule_thickness ();
301   Real sl = slope*paper()->internote_f ();
302   paper()->lookup_l ()->beam (sl, 20 PT);
303
304   Molecule leftbeams;
305   Molecule rightbeams;
306
307   /* half beams extending to the left. */
308   if (prev)
309     {
310       int lhalfs= lhalfs = here->beams_left_i_ - prev->beams_right_i_ ;
311       int lwholebeams= here->beams_left_i_ <? prev->beams_right_i_ ;
312       Real w = (here->hpos_f () - prev->hpos_f ())/4;
313       Atom a;
314       if (lhalfs)               // generates warnings if not
315         a =  paper()->lookup_l ()->beam (sl, w);
316       a.translate (Offset (-w, -w * sl));
317       for (int j = 0; j  < lhalfs; j++)
318         {
319           Atom b (a);
320           b.translate_axis (-dir_ * dy * (lwholebeams+j), Y_AXIS);
321           leftbeams.add (b);
322         }
323     }
324
325   if (next)
326     {
327       int rhalfs = here->beams_right_i_ - next->beams_left_i_;
328       int rwholebeams = here->beams_right_i_ <? next->beams_left_i_;
329
330       Real w = next->hpos_f() - here->hpos_f ();
331       Atom a = paper()->lookup_l ()->beam (sl, w + stemdx);
332
333       int j = 0;
334       Real gap_f = 0;
335       if (here->beam_gap_i_)
336         {
337           int nogap = rwholebeams - here->beam_gap_i_;
338           for (; j  < nogap; j++)
339             {
340               Atom b (a);
341               b.translate_axis (-dir_ * dy * j, Y_AXIS);
342               rightbeams.add (b);
343             }
344           // TODO: notehead widths differ for different types
345           gap_f = paper()->note_width () / 2;
346           w -= 2 * gap_f;
347           a = paper()->lookup_l ()->beam (sl, w + stemdx);
348         }
349
350       for (; j  < rwholebeams; j++)
351         {
352           Atom b (a);
353           b.translate (Offset (gap_f, -dir_ * dy * j));
354           rightbeams.add (b);
355         }
356
357       w /= 4;
358       if (rhalfs)
359         a = paper()->lookup_l ()->beam (sl, w);
360
361       for (; j  < rwholebeams + rhalfs; j++)
362         {
363           Atom b (a);
364           b.translate_axis (-dir_ * dy * j, Y_AXIS);
365           rightbeams.add (b);
366         }
367
368     }
369   leftbeams.add (rightbeams);
370   return leftbeams;
371 }