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