]> git.donarmstrong.com Git - lilypond.git/blob - lily/stem.cc
release: 1.1.37
[lilypond.git] / lily / stem.cc
1 /*
2   stem.cc -- implement Stem
3
4   source file of the GNU LilyPond music typesetter
5
6   (c) 1996, 1997--1999 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7
8   TODO: This is way too hairy
9 */
10
11 #include "stem.hh"
12 #include "debug.hh"
13 #include "paper-def.hh"
14 #include "note-head.hh"
15 #include "lookup.hh"
16 #include "molecule.hh"
17 #include "p-col.hh"
18 #include "misc.hh"
19 #include "beam.hh"
20 #include "rest.hh"
21
22 Stem::~Stem ()
23 {
24 }
25
26 Stem::Stem ()
27 {
28   beams_i_drul_[LEFT] = beams_i_drul_[RIGHT] = -1;
29   yextent_drul_[DOWN] = yextent_drul_[UP] = 0;
30   flag_i_ = 2;
31   dir_ = CENTER;
32   beam_l_ = 0;
33 }
34
35 Interval_t<int>
36 Stem::head_positions () const
37 {
38   /* 
39     Mysterious FreeBSD fix by John Galbraith.  Somehow, the empty intervals 
40     trigger FP exceptions on FreeBSD.  Fix: do not return infinity 
41
42    */
43   if (!head_l_arr_.size ())
44     {
45       return Interval_t<int> (100,-100);        
46     }
47
48   Interval_t<int> r;
49   for (int i =0; i < head_l_arr_.size (); i++)
50     {
51       int p = head_l_arr_[i]->position_i_;
52       r[BIGGER] = r[BIGGER] >? p;
53       r[SMALLER] = r[SMALLER] <? p;
54     }
55   return r;
56 }
57
58 void
59 Stem::do_print () const
60 {
61 #ifndef NPRINT
62   DOUT << "flag "<< flag_i_;
63   if (beam_l_)
64     DOUT << "beamed";
65 #endif
66 }
67
68 Real
69 Stem::stem_length_f () const
70 {
71   return yextent_drul_[UP]-yextent_drul_[DOWN] ;
72 }
73
74 Real
75 Stem::stem_begin_f () const
76 {
77   return yextent_drul_[Direction(-dir_)];
78 }
79
80 Real
81 Stem::chord_start_f () const
82 {
83   return head_positions()[dir_] * staff_line_leading_f ()/2.0;
84 }
85
86 Real
87 Stem::stem_end_f () const
88 {
89   return yextent_drul_[dir_];
90 }
91
92 void
93 Stem::set_stemend (Real se)
94 {
95   // todo: margins
96   if (dir_ && dir_ * head_positions()[dir_] >= se*dir_)
97     warning (_ ("weird stem size; check for narrow beams"));
98
99   
100   yextent_drul_[dir_]  =  se;
101   yextent_drul_[Direction(-dir_)] = head_positions()[-dir_];
102 }
103
104 int
105 Stem::type_i () const
106 {
107   return head_l_arr_[0]->balltype_i_;
108 }
109
110 void
111 Stem::add_head (Rhythmic_head *n)
112 {
113   n->add_dependency (this);     // ?
114   if (Note_head *nh = dynamic_cast<Note_head *> (n))
115     {
116       head_l_arr_.push (nh);
117     }
118   else if (Rest *r = dynamic_cast<Rest *> (n))
119     {
120       rest_l_arr_.push (r);
121     }
122 }
123
124 bool
125 Stem::invisible_b () const
126 {
127   return (!head_l_arr_.size () ||
128     head_l_arr_[0]->balltype_i_ <= 0);
129 }
130
131 int
132 Stem::get_center_distance (Direction d) const
133 {
134   int staff_center = 0;
135   int distance = d*(head_positions()[d] - staff_center);
136   return distance >? 0;
137 }
138
139 Direction
140 Stem::get_default_dir () const
141 {
142   return (get_center_distance (UP) >
143           get_center_distance (DOWN)) 
144     ? DOWN 
145     : UP;
146 }
147
148 Direction
149 Stem::get_dir () const
150 {
151   return dir_;
152 }
153
154 void
155 Stem::set_default_dir ()
156 {
157   dir_ = get_default_dir ();
158 }
159
160 void
161 Stem::set_default_stemlen ()
162 {
163   Real internote_f = staff_line_leading_f ()/2.0;
164   Real length_f = paper_l ()->get_var ("stem_length0") / internote_f;
165   Real shorten_f = paper_l ()->get_var ("forced_stem_shorten0") / internote_f;
166
167   if (!dir_)
168     set_default_dir ();
169   /* 
170     stems in unnatural (forced) direction should be shortened, 
171     according to [Roush & Gourlay]
172    */
173   if (((int)chord_start_f ())
174       && (dir_ != get_default_dir ()))
175     length_f -= shorten_f;
176
177   if (flag_i_ >= 5)
178     length_f += 2.0;
179   if (flag_i_ >= 6)
180     length_f += 1.0;
181   
182   set_stemend ((dir_ > 0) ? head_positions()[BIGGER] + length_f:
183                head_positions()[SMALLER] - length_f);
184
185   if (dir_ * stem_end_f () < 0)
186     set_stemend (0);
187 }
188
189 //xxx
190 void
191 Stem::set_default_extents ()
192 {
193   if (!stem_length_f ())
194     set_default_stemlen ();
195
196 }
197
198 void
199 Stem::set_noteheads ()
200 {
201   if (!head_l_arr_.size ())
202     return;
203   head_l_arr_.sort (Note_head::compare);
204   if (dir_ < 0)
205     head_l_arr_.reverse ();
206
207   Note_head * beginhead =   head_l_arr_[0];
208   beginhead->set_elt_property (extremal_scm_sym, SCM_BOOL_T);
209   if  (beginhead !=   head_l_arr_.top ())
210     head_l_arr_.top ()->set_elt_property (extremal_scm_sym, SCM_BOOL_T);
211   
212   int parity=1;
213   int lastpos = beginhead->position_i_;
214   for (int i=1; i < head_l_arr_.size (); i ++)
215     {
216       int dy =abs (lastpos- head_l_arr_[i]->position_i_);
217
218       if (dy <= 1)
219         {
220           if (parity)
221             head_l_arr_[i]->flip_around_stem (dir_);
222           parity = !parity;
223         }
224       else
225         parity = 1;
226       lastpos = head_l_arr_[i]->position_i_;
227     }
228 }
229
230 void
231 Stem::do_pre_processing ()
232 {
233   if (yextent_drul_[DOWN]== yextent_drul_[UP])
234     set_default_extents ();
235   set_noteheads ();
236   flag_i_ = flag_i_;
237   if (invisible_b ())
238     {
239       set_elt_property (transparent_scm_sym, SCM_BOOL_T);
240     }
241   set_empty (invisible_b ());
242 }
243
244
245 Interval
246 Stem::do_width () const
247 {
248   Interval r (0, 0);
249   if (beam_l_ || abs (flag_i_) <= 2)
250     ;   // TODO!
251   else
252     {
253       r = lookup_l ()->flag (flag_i_, dir_).dim_.x ();
254       r += note_delta_f ();
255     }
256   return r;
257 }
258
259
260
261
262 const Real ANGLE = 20* (2.0*M_PI/360.0); // ugh!
263
264 Molecule*
265 Stem::do_brew_molecule_p () const
266 {
267   Molecule *mol_p =new Molecule;
268   Drul_array<Real> stem_y = yextent_drul_;
269   Real dy = staff_line_leading_f ()/2.0;
270
271   Real head_wid = 0;
272   if (head_l_arr_.size ())
273     head_wid = head_l_arr_[0]->extent (X_AXIS).length ();
274   stem_y[Direction(-dir_)] += dir_ * head_wid * tan(ANGLE)/(2*dy);
275   
276   if (!invisible_b ())
277     {
278       Molecule ss =lookup_l ()->stem (stem_y[DOWN]*dy,
279                                      stem_y[UP]*dy);
280       mol_p->add_molecule (ss);
281     }
282
283   if (!beam_l_ && abs (flag_i_) > 2)
284     {
285       Molecule fl = lookup_l ()->flag (flag_i_, dir_);
286       fl.translate_axis(stem_y[dir_]*dy, Y_AXIS);
287       mol_p->add_molecule (fl);
288     }
289
290   if (head_l_arr_.size())
291     {
292       mol_p->translate_axis (note_delta_f (), X_AXIS);
293     }
294   return mol_p;
295 }
296
297 Real
298 Stem::note_delta_f () const
299 {
300   Real r=0;
301   if (head_l_arr_.size())
302     {
303       Interval head_wid(0,  head_l_arr_[0]->extent (X_AXIS).length ());
304       Real rule_thick(paper_l ()->rule_thickness ());
305       Interval stem_wid(-rule_thick/2, rule_thick/2);
306       if (dir_ == CENTER)
307         r = head_wid.center ();
308       else
309         r = head_wid[dir_] - stem_wid[dir_];
310     }
311   return r;
312 }
313
314 Real
315 Stem::hpos_f () const
316 {
317   return note_delta_f () + Item::hpos_f ();
318 }
319
320 void
321 Stem::do_substitute_element_pointer (Score_element*o,Score_element*n)
322 {
323   if (Note_head*h=dynamic_cast<Note_head*> (o))
324     head_l_arr_.substitute (h, dynamic_cast<Note_head*>(n));
325   if (Rest *r=dynamic_cast<Rest*> (o))
326     rest_l_arr_.substitute (r, dynamic_cast<Rest*>(n));
327   if (Beam* b = dynamic_cast<Beam*> (o))
328     {
329       if (b == beam_l_) 
330         beam_l_ = dynamic_cast<Beam*> (n);
331     }
332   Staff_symbol_referencer::do_substitute_element_pointer (o,n);
333 }