]> git.donarmstrong.com Git - lilypond.git/blob - lily/beam.cc
release: 0.0.67
[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 "varray.hh"
16 #include "proto.hh"
17 #include "dimen.hh"
18 #include "beam.hh"
19 #include "misc.hh"
20 #include "debug.hh"
21 #include "symbol.hh"
22 #include "molecule.hh"
23 #include "leastsquares.hh"
24 #include "p-col.hh"
25 #include "stem.hh"
26 #include "paper-def.hh"
27 #include "lookup.hh"
28 #include "grouping.hh"
29
30
31
32 struct Stem_info {
33     Real x;
34     Real idealy;
35     Real miny;
36     int no_beams;
37
38     
39     Stem_info(){}
40     Stem_info(Stem const *);
41 };
42
43 Stem_info::Stem_info(Stem const *s)
44 {
45     x = s->hpos_f();
46     int dir = s->dir_i_;
47     idealy  = dir * s->stem_end_f();
48     miny = dir * s->stem_start_f() + 2; // ugh
49     assert(miny <= idealy);
50 }
51
52
53 /* *************** */
54
55
56
57 Offset
58 Beam::center()const
59 {
60     Real w=(paper()->note_width() + width().length())/2.0;
61     return Offset(w, (left_pos + w* slope)*paper()->internote_f());
62 }
63
64
65 Beam::Beam()
66 {
67     slope = 0;
68     left_pos = 0.0;
69 }
70
71 void
72 Beam::add(Stem*s)
73 {
74     stems.push(s);
75     s->add_dependency(this);
76     s->print_flag_b_ = false;
77 }
78
79 void
80 Beam::set_default_dir()
81 {
82     int dirs_single = 0, dirs_chord = 0;
83     for (int i=0; i <stems.size(); i++) {
84         Stem *sl = stems[i];
85         if (sl->chord_b())
86             dirs_chord += sl->get_default_dir();
87         else
88             dirs_single += sl->get_center_distance();
89     }
90     dirs_single = -sign(dirs_single);
91     dir_i_ = (dirs_single + dirs_chord > 0) ? 1 : -1;
92
93    for (int i=0; i <stems.size(); i++) {
94         Stem *sl = stems[i];
95         sl->dir_i_ = dir_i_;
96    }
97 }
98
99 /*
100   should use minimum energy formulation (cf linespacing)
101   */
102 void
103 Beam::solve_slope()
104 {
105     Array<Stem_info> sinfo;
106   for (int j=0; j <stems.size(); j++) {
107         Stem *i = stems[j];
108
109         i->set_default_extents();
110         if (i->invisible_b())
111             continue;
112         
113         Stem_info info(i);
114         sinfo.push(info);
115     }
116     Real leftx = sinfo[0].x;
117     Least_squares l;
118     for (int i=0; i < sinfo.size(); i++) {
119         sinfo[i].x -= leftx;
120         l.input.push(Offset(sinfo[i].x, sinfo[i].idealy));
121     }
122
123     l.minimise(slope, left_pos);
124     Real dy = 0.0;
125     for (int i=0; i < sinfo.size(); i++) {
126         Real y = sinfo[i].x * slope + left_pos;
127         Real my = sinfo[i].miny;
128
129         if (my - y > dy)
130             dy = my -y; 
131     }
132     left_pos += dy;
133     left_pos *= dir_i_;    
134     slope *= dir_i_;
135
136                                 // ugh
137     Real sl = slope*paper()->internote_f();
138     paper()->lookup_l()->beam(sl, 20 PT);
139     slope = sl /paper()->internote_f();
140 }
141
142 void
143 Beam::set_stemlens()
144 {
145     Real x0 = stems[0]->hpos_f();    
146     for (int j=0; j <stems.size(); j++) {
147         Stem *s = stems[j];
148
149         Real x =  s->hpos_f()-x0;
150         s->set_stemend(left_pos + slope * x);   
151     }
152 }
153
154
155 void
156 Beam::do_post_processing()
157 {
158     solve_slope();    
159     set_stemlens();
160 }
161
162 void
163 Beam::set_grouping(Rhythmic_grouping def, Rhythmic_grouping cur)
164 {
165     def.OK();
166     cur.OK();
167     assert(cur.children.size() == stems.size());
168     
169     cur.split(def);
170
171     Array<int> b;
172     {
173         Array<int> flags;
174         for (int j=0; j <stems.size(); j++) {
175             Stem *s = stems[j];
176
177             int f = intlog2(abs(s->flag_i_))-2;
178             assert(f>0);
179             flags.push(f);
180         }
181         int fi =0;
182         b= cur.generate_beams(flags, fi);
183         b.insert(0,0);
184         b.push(0);
185         assert(stems.size() == b.size()/2);
186     }
187
188     for (int j=0, i=0; i < b.size() && j <stems.size(); i+= 2, j++) {
189         Stem *s = stems[j];
190         s->beams_left_i_ = b[i];
191         s->beams_right_i_ = b[i+1];
192     }
193 }
194
195 void
196 Beam::do_pre_processing()
197 {
198     left_col_l_ = stems[0]   ->pcol_l_;
199     right_col_l_ = stems.top()->pcol_l_;    
200     assert(stems.size()>1);
201     if (!dir_i_)
202         set_default_dir();
203
204 }
205
206
207 Interval
208 Beam::do_width() const
209 {
210     return Interval( stems[0]->hpos_f(),
211                      stems.top()->hpos_f() );
212 }
213
214 /*
215   beams to go with one stem.
216   */
217 Molecule
218 Beam::stem_beams(Stem *here, Stem *next, Stem *prev)const
219 {
220     assert( !next || next->hpos_f() > here->hpos_f()  );
221     assert( !prev || prev->hpos_f() < here->hpos_f()  );
222     Real dy=paper()->internote_f()*2;
223     Real stemdx = paper()->rule_thickness();
224     Real sl = slope*paper()->internote_f();
225     paper()->lookup_l()->beam(sl, 20 PT);
226
227     Molecule leftbeams;
228     Molecule rightbeams;
229
230     /* half beams extending to the left. */
231     if (prev) {
232         int lhalfs= lhalfs = here->beams_left_i_ - prev->beams_right_i_ ;
233         int lwholebeams= here->beams_left_i_ <? prev->beams_right_i_ ;
234         Real w = (here->hpos_f() - prev->hpos_f())/4;
235         Symbol dummy;
236         Atom a(dummy);
237         if (lhalfs)             // generates warnings if not
238             a =  paper()->lookup_l()->beam(sl, w);
239         a.translate(Offset (-w, -w * sl));
240         for (int j = 0; j  < lhalfs; j++) {
241             Atom b(a);
242             b.translate(Offset(0, -dir_i_ * dy * (lwholebeams+j)));
243             leftbeams.add( b );
244         }
245     }
246         
247     if (next){
248         int rhalfs = here->beams_right_i_ - next->beams_left_i_;
249         int rwholebeams = here->beams_right_i_ <? next->beams_left_i_; 
250
251         Real w = next->hpos_f() - here->hpos_f();
252         Atom a = paper()->lookup_l()->beam(sl, w + stemdx);
253         
254         int j = 0;
255         for (; j  < rwholebeams; j++) {
256             Atom b(a);
257             b.translate(Offset(0, -dir_i_ * dy * j));
258             rightbeams.add( b ); 
259         }
260
261         w /= 4;
262         if (rhalfs)
263             a = paper()->lookup_l()->beam(sl, w);
264         
265         for (; j  < rwholebeams + rhalfs; j++) {
266             Atom b(a);
267             b.translate(Offset(0, -dir_i_ * dy * j));
268             rightbeams.add(b ); 
269         }
270         
271     }
272     leftbeams.add(rightbeams);
273     return leftbeams;
274 }
275
276
277 Molecule*
278 Beam::brew_molecule_p() const 
279 {
280     Molecule *out=0;
281     Real inter=paper()->internote_f();
282     out = new Molecule;
283     Real x0 = stems[0]->hpos_f();
284     for (int j=0; j <stems.size(); j++) {
285         Stem *i = stems[j];
286         Stem * prev = (j > 0)? stems[j-1] : 0;
287         Stem * next = (j < stems.size()-1) ? stems[j+1] :0;
288
289         Molecule sb = stem_beams(i, next, prev);
290         Real  x = i->hpos_f()-x0;
291         sb.translate(Offset(x, (x * slope  + left_pos)* inter));
292         out->add(sb);
293     }
294     out->translate(Offset(x0 - left_col_l_->hpos,0));
295     return out;
296 }
297
298 IMPLEMENT_STATIC_NAME(Beam);
299
300 void
301 Beam::do_print()const
302 {
303 #ifndef NPRINT
304     mtor << "slope " <<slope << "left ypos " << left_pos;
305     Spanner::print();
306 #endif
307 }
308
309 void
310 Beam::do_substitute_dependency(Score_elem*o,Score_elem*n)
311 {
312     int i;
313     while ((i=stems.find_i((Stem*)o->item())) >=0) 
314            if (n) stems[i] = (Stem*) n->item();
315            else stems.del(i);
316 }