]> git.donarmstrong.com Git - lilypond.git/blob - lily/beam.cc
release: 0.0.72pre
[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. 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            
73     /* well eh, huh?
74     idealy_f_  = dir_i_ * s->stem_start_f() + beams_i_ * interbeam_f; 
75     if ( beams_i_ < 3 )
76         idealy_f_ += 2 * interline_f;
77     else
78         idealy_f_ += 1.5 * interline_f;
79     */
80
81     idealy_f_  = dir_i_ * s->stem_end_f();
82
83     miny_f_ = dir_i_ * s->stem_start_f() + notehead_y + beams_i_ * interbeam_f;
84
85     idealy_f_ =  miny_f_ >? idealy_f_;
86 //    assert(miny_f_ <= idealy_f_);
87 }
88
89
90 /* *************** */
91
92
93 Offset
94 Beam::center()const
95 {
96     Real w=(paper()->note_width() + width().length())/2.0;
97     return Offset(w, (left_pos + w* slope)*paper()->internote_f());
98 }
99
100
101 Beam::Beam()
102 {
103     slope = 0;
104     left_pos = 0.0;
105 }
106
107 void
108 Beam::add(Stem*s)
109 {
110     stems.push(s);
111     s->add_dependency(this);
112     s->print_flag_b_ = false;
113 }
114
115 void
116 Beam::set_default_dir()
117 {
118     int up = 0, down = 0;
119     int up_count = 0, down_count = 0;
120
121     for (int i=0; i <stems.size(); i++) {
122         Stem *sl = stems[i];
123         int cur_down = sl->get_center_distance_from_top();
124         int cur_up = sl->get_center_distance_from_bottom();
125         if (cur_down) {
126             down += cur_down;
127             down_count++;
128         }
129         if (cur_up) {
130             up += cur_up;
131             up_count++;
132         }
133     }
134     if (!down)
135         down_count = 1;
136     if (!up)
137         up_count = 1;
138
139     // the following relation is equal to
140     //        up / up_count > down / down_count
141     dir_i_ = (up * down_count > down * up_count) ? 1 : -1;
142
143    for (int i=0; i <stems.size(); i++) {
144         Stem *sl = stems[i];
145         sl->dir_i_ = dir_i_;
146    }
147 }
148
149 /*
150   should use minimum energy formulation (cf linespacing)
151
152   [todo]
153   the y of the (start) of the beam should be quantisized,
154   so that no stafflines appear just in between two beam-flags
155
156 */
157 void
158 Beam::solve_slope()
159 {
160     Array<Stem_info> sinfo;
161     for (int j=0; j <stems.size(); j++) {
162         Stem *i = stems[j];
163
164         i->set_default_extents();
165         if (i->invisible_b())
166             continue;
167         
168         Stem_info info(i);
169         sinfo.push(info);
170     }
171     if (! sinfo.size() )
172         slope = left_pos = 0;
173     else if (sinfo.size() == 1) {
174         slope = 0;
175         left_pos = sinfo[0].idealy_f_;
176     } else {
177         
178         Real leftx = sinfo[0].x;
179         Least_squares l;
180         for (int i=0; i < sinfo.size(); i++) {
181             sinfo[i].x -= leftx;
182             l.input.push(Offset(sinfo[i].x, sinfo[i].idealy_f_));
183         }
184
185         l.minimise(slope, left_pos);
186     }
187     
188     Real dy = 0.0;
189     for (int i=0; i < sinfo.size(); i++) {
190         Real y = sinfo[i].x * slope + left_pos;
191         Real my = sinfo[i].miny_f_;
192
193         if (my - y > dy)
194             dy = my -y; 
195     }
196     left_pos += dy;
197     left_pos *= dir_i_;    
198
199     slope *= dir_i_;
200
201     /*
202       This neat trick is by Werner Lemberg, damped = tanh(slope) corresponds
203       with some tables in [Wanske]
204      */
205     slope = 0.6 * tanh(slope);  
206
207                                 // ugh
208     Real sl = slope*paper()->internote_f();
209     paper()->lookup_l()->beam(sl, 20 PT);
210     slope = sl /paper()->internote_f();
211 }
212
213 void
214 Beam::set_stemlens()
215 {
216     Real x0 = stems[0]->hpos_f();    
217     for (int j=0; j <stems.size(); j++) {
218         Stem *s = stems[j];
219
220         Real x =  s->hpos_f()-x0;
221         s->set_stemend(left_pos + slope * x);   
222     }
223 }
224
225
226 void
227 Beam::do_post_processing()
228 {
229     if ( stems.size() < 2) {
230         warning("Beam with less than 2 stems");
231         transparent_b_ = true;
232         return ;
233     }
234     solve_slope();    
235     set_stemlens();
236 }
237
238 void
239 Beam::set_grouping(Rhythmic_grouping def, Rhythmic_grouping cur)
240 {
241     def.OK();
242     cur.OK();
243     assert(cur.children.size() == stems.size());
244     
245     cur.split(def);
246
247     Array<int> b;
248     {
249         Array<int> flags;
250         for (int j=0; j <stems.size(); j++) {
251             Stem *s = stems[j];
252
253             int f = intlog2(abs(s->flag_i_))-2;
254             assert(f>0);
255             flags.push(f);
256         }
257         int fi =0;
258         b= cur.generate_beams(flags, fi);
259         b.insert(0,0);
260         b.push(0);
261         assert(stems.size() == b.size()/2);
262     }
263
264     for (int j=0, i=0; i < b.size() && j <stems.size(); i+= 2, j++) {
265         Stem *s = stems[j];
266         s->beams_left_i_ = b[i];
267         s->beams_right_i_ = b[i+1];
268     }
269 }
270
271 void
272 Beam::do_pre_processing()
273 {
274     if (!dir_i_)
275         set_default_dir();
276
277 }
278
279
280 Interval
281 Beam::do_width() const
282 {
283     return Interval( stems[0]->hpos_f(),
284                      stems.top()->hpos_f() );
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         int lhalfs= lhalfs = here->beams_left_i_ - prev->beams_right_i_ ;
307         int lwholebeams= here->beams_left_i_ <? prev->beams_right_i_ ;
308         Real w = (here->hpos_f() - prev->hpos_f())/4;
309         Symbol dummy;
310         Atom a(dummy);
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             Atom b(a);
316             b.translate_y( -dir_i_ * dy * (lwholebeams+j));
317             leftbeams.add( b );
318         }
319     }
320         
321     if (next){
322         int rhalfs = here->beams_right_i_ - next->beams_left_i_;
323         int rwholebeams = here->beams_right_i_ <? next->beams_left_i_; 
324
325         Real w = next->hpos_f() - here->hpos_f();
326         Atom a = paper()->lookup_l()->beam(sl, w + stemdx);
327         
328         int j = 0;
329         for (; j  < rwholebeams; j++) {
330             Atom b(a);
331             b.translate_y( -dir_i_ * dy * j);
332             rightbeams.add( b ); 
333         }
334
335         w /= 4;
336         if (rhalfs)
337             a = paper()->lookup_l()->beam(sl, w);
338         
339         for (; j  < rwholebeams + rhalfs; j++) {
340             Atom b(a);
341             b.translate_y( -dir_i_ * dy * j);
342             rightbeams.add(b ); 
343         }
344         
345     }
346     leftbeams.add(rightbeams);
347     return leftbeams;
348 }
349
350
351 Molecule*
352 Beam::brew_molecule_p() const 
353 {
354  
355     Molecule *mol_p = new Molecule;
356     // huh? inter-what
357 //    Real inter_f = paper()->interbeam_f();
358     Real inter_f = paper()->internote_f();
359     Real x0 = stems[0]->hpos_f();
360     for (int j=0; j <stems.size(); j++) {
361         Stem *i = stems[j];
362         Stem * prev = (j > 0)? stems[j-1] : 0;
363         Stem * next = (j < stems.size()-1) ? stems[j+1] :0;
364
365         Molecule sb = stem_beams(i, next, prev);
366         Real  x = i->hpos_f()-x0;
367         sb.translate(Offset(x, (x * slope  + left_pos)* inter_f));
368         mol_p->add(sb);
369     }
370     mol_p->translate_x(x0 - left_col_l_->hpos);
371     return mol_p;
372 }
373
374 IMPLEMENT_STATIC_NAME(Beam);
375 IMPLEMENT_IS_TYPE_B1(Beam, Spanner);
376
377 void
378 Beam::do_print()const
379 {
380 #ifndef NPRINT
381     mtor << "slope " <<slope << "left ypos " << left_pos;
382     Spanner::print();
383 #endif
384 }
385 /*
386   duh. The stem is not a dependency but a dependent
387  */
388 void
389 Beam::do_substitute_dependency(Score_elem*o,Score_elem*n)
390 {
391     if (o->is_type_b( Stem::static_name() )) {
392         stems.substitute( (Stem*)o->item(),  n?(Stem*) n->item():0);
393     }
394 }