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