]> git.donarmstrong.com Git - lilypond.git/blob - lily/stem.cc
release: 1.3.4
[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 "paper-column.hh"
18 #include "misc.hh"
19 #include "beam.hh"
20 #include "rest.hh"
21
22 Stem::Stem ()
23 {
24   beams_i_drul_[LEFT] = beams_i_drul_[RIGHT] = -1;
25   yextent_drul_[DOWN] = yextent_drul_[UP] = 0;
26   flag_i_ = 2;
27   beam_l_ = 0;
28 }
29
30 Interval_t<int>
31 Stem::head_positions () const
32 {
33   /* 
34     Mysterious FreeBSD fix by John Galbraith.  Somehow, the empty intervals 
35     trigger FP exceptions on FreeBSD.  Fix: do not return infinity 
36
37    */
38   if (!head_l_arr_.size ())
39     {
40       return Interval_t<int> (100,-100);        
41     }
42
43   Interval_t<int> r;
44   for (int i =0; i < head_l_arr_.size (); i++)
45     {
46       int p = (int)head_l_arr_[i]->position_f ();
47       r[BIGGER] = r[BIGGER] >? p;
48       r[SMALLER] = r[SMALLER] <? p;
49     }
50   return r;
51 }
52
53 void
54 Stem::do_print () const
55 {
56 #ifndef NPRINT
57   DEBUG_OUT << "flag "<< flag_i_;
58   if (beam_l_)
59     DEBUG_OUT << "beamed";
60 #endif
61 }
62
63 Real
64 Stem::stem_length_f () const
65 {
66   return yextent_drul_[UP]-yextent_drul_[DOWN] ;
67 }
68
69 Real
70 Stem::stem_begin_f () const
71 {
72   return yextent_drul_[Direction(-get_direction ())];
73 }
74
75 Real
76 Stem::chord_start_f () const
77 {
78   return head_positions()[get_direction ()] * staff_line_leading_f ()/2.0;
79 }
80
81 Real
82 Stem::stem_end_f () const
83 {
84   return yextent_drul_[get_direction ()];
85 }
86
87 void
88 Stem::set_stemend (Real se)
89 {
90   // todo: margins
91   if (get_direction () && get_direction () * head_positions()[get_direction ()] >= se*get_direction ())
92     warning (_ ("Weird stem size; check for narrow beams"));
93
94   
95   yextent_drul_[get_direction ()]  =  se;
96   yextent_drul_[Direction(-get_direction ())] = head_positions()[-get_direction ()];
97 }
98
99 int
100 Stem::type_i () const
101 {
102   return head_l_arr_[0]->balltype_i_;
103 }
104
105 void
106 Stem::add_head (Rhythmic_head *n)
107 {
108   n->stem_l_ = this;
109   n->add_dependency (this);     // ?
110   if (Note_head *nh = dynamic_cast<Note_head *> (n))
111     {
112       head_l_arr_.push (nh);
113     }
114   else if (Rest *r = dynamic_cast<Rest *> (n))
115     {
116       rest_l_arr_.push (r);
117     }
118 }
119
120 bool
121 Stem::invisible_b () const
122 {
123   return (!head_l_arr_.size () ||
124     head_l_arr_[0]->balltype_i_ <= 0);
125 }
126
127 int
128 Stem::get_center_distance (Direction d) const
129 {
130   int staff_center = 0;
131   int distance = d*(head_positions()[d] - staff_center);
132   return distance >? 0;
133 }
134
135 Direction
136 Stem::get_default_dir () const
137 {
138   int du = get_center_distance (UP);
139   int dd = get_center_distance (DOWN);
140
141   if (sign (dd - du))
142     return Direction (sign (dd -du));
143
144   return Direction (int(paper_l ()->get_var ("stem_default_neutral_direction")));
145 }
146
147
148
149 void
150 Stem::set_default_stemlen ()
151 {
152   Real length_f = 0.;
153   SCM scm_len = get_elt_property("length");
154   if (scm_len != SCM_UNDEFINED)
155     {
156       length_f = gh_scm2double (scm_len);
157     }
158   else
159     length_f = paper_l ()->get_var ("stem_length0");
160
161   bool grace_b = get_elt_property ("grace") != SCM_UNDEFINED;
162   String type_str = grace_b ? "grace_" : "";
163
164   Real shorten_f = paper_l ()->get_var (type_str + "forced_stem_shorten0");
165
166   if (!get_direction ())
167     set_direction (get_default_dir ());
168
169   /* 
170     stems in unnatural (forced) direction should be shortened, 
171     according to [Roush & Gourlay]
172    */
173   if (((int)chord_start_f ())
174       && (get_direction () != 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 ((get_direction () > 0) ? head_positions()[BIGGER] + length_f:
183                head_positions()[SMALLER] - length_f);
184
185   bool no_extend_b = get_elt_property ("no-stem-extend") != SCM_UNDEFINED;
186   if (!grace_b && !no_extend_b && (get_direction () * stem_end_f () < 0))
187     set_stemend (0);
188 }
189
190 //xxx
191 void
192 Stem::set_default_extents ()
193 {
194   if (!stem_length_f ())
195     set_default_stemlen ();
196
197 }
198
199 void
200 Stem::set_noteheads ()
201 {
202   if (!head_l_arr_.size ())
203     return;
204   head_l_arr_.sort (Note_head::compare);
205   if (get_direction () < 0)
206     head_l_arr_.reverse ();
207
208   Note_head * beginhead =   head_l_arr_[0];
209   beginhead->set_elt_property ("extremal", SCM_BOOL_T);
210   if  (beginhead !=   head_l_arr_.top ())
211     head_l_arr_.top ()->set_elt_property ("extremal", SCM_BOOL_T);
212   
213   int parity=1;
214   int lastpos = int (beginhead->position_f ());
215   for (int i=1; i < head_l_arr_.size (); i ++)
216     {
217       int dy =abs (lastpos- (int)head_l_arr_[i]->position_f ());
218
219       if (dy <= 1)
220         {
221           if (parity)
222             head_l_arr_[i]->flip_around_stem (get_direction ());
223           parity = !parity;
224         }
225       else
226         parity = 1;
227       lastpos = int (head_l_arr_[i]->position_f ());
228     }
229 }
230
231 void
232 Stem::do_pre_processing ()
233 {
234   if (yextent_drul_[DOWN]== yextent_drul_[UP])
235     set_default_extents ();
236   set_noteheads ();
237
238   if (invisible_b ())
239     {
240       set_elt_property ("transparent", SCM_BOOL_T);
241     }
242   set_empty (invisible_b (), X_AXIS, Y_AXIS);
243   set_spacing_hints ();
244 }
245
246
247
248 /**
249    set stem directions for hinting the optical spacing correction.
250
251    Modifies DIR_LIST property of the Stem's Score_column
252
253    TODO: more advanced: supply height of noteheads as well, for more advanced spacing possibilities
254  */
255
256 void
257 Stem::set_spacing_hints () 
258 {
259   if (!invisible_b ())
260     {
261       SCM scmdir  = gh_int2scm (get_direction ());
262       SCM dirlist = column_l ()->get_elt_property ("dir-list");
263       if (dirlist == SCM_UNDEFINED)
264         dirlist = SCM_EOL;
265
266       if (scm_sloppy_memq (scmdir, dirlist) == SCM_EOL)
267         {
268           dirlist = gh_cons (scmdir, dirlist);
269           column_l ()->set_elt_property ("dir-list", dirlist);
270         }
271     }
272 }
273
274 Molecule
275 Stem::flag () const
276 {
277   String style;
278   SCM st = get_elt_property ("style");
279   if ( st != SCM_UNDEFINED)
280     {
281       style = ly_scm2string (st);
282     }
283
284   char c = (get_direction () == UP) ? 'u' : 'd';
285   Molecule m = lookup_l ()->afm_find (String ("flags-") + to_str (c) + 
286                                       to_str (flag_i_));
287   if (!style.empty_b ())
288     m.add_molecule(lookup_l ()->afm_find (String ("flags-") + to_str (c) + style));
289   return m;
290 }
291
292 Interval
293 Stem::do_width () const
294 {
295   Interval r (0, 0);
296   if (beam_l_ || abs (flag_i_) <= 2)
297     ;   // TODO!
298   else
299     {
300       r = flag ().dim_.x ();
301       r += note_delta_f ();
302     }
303   return r;
304 }
305
306
307
308
309 const Real ANGLE = 20* (2.0*M_PI/360.0); // ugh!
310
311 Molecule*
312 Stem::do_brew_molecule_p () const
313 {
314   Molecule *mol_p =new Molecule;
315   Drul_array<Real> stem_y = yextent_drul_;
316   Real dy = staff_line_leading_f ()/2.0;
317
318   Real head_wid = 0;
319   if (head_l_arr_.size ())
320     head_wid = head_l_arr_[0]->extent (X_AXIS).length ();
321   stem_y[Direction(-get_direction ())] += get_direction () * head_wid * tan(ANGLE)/(2*dy);
322   
323   if (!invisible_b ())
324     {
325       Real stem_width = paper_l ()->get_var ("stemthickness");
326       Molecule ss =lookup_l ()->filledbox (Box (Interval (-stem_width/2, stem_width/2),
327                                                  Interval (stem_y[DOWN]*dy, stem_y[UP]*dy)));
328       mol_p->add_molecule (ss);
329     }
330
331   if (!beam_l_ && abs (flag_i_) > 2)
332     {
333       Molecule fl = flag ();
334       fl.translate_axis(stem_y[get_direction ()]*dy, Y_AXIS);
335       mol_p->add_molecule (fl);
336     }
337
338   if (head_l_arr_.size())
339     {
340       mol_p->translate_axis (note_delta_f (), X_AXIS);
341     }
342   return mol_p;
343 }
344
345 Real
346 Stem::note_delta_f () const
347 {
348   Real r=0;
349   if (head_l_arr_.size())
350     {
351       Interval head_wid(0,  head_l_arr_[0]->extent (X_AXIS).length ());
352          Real rule_thick = paper_l ()->get_var ("stemthickness");
353
354       Interval stem_wid(-rule_thick/2, rule_thick/2);
355       if (get_direction () == CENTER)
356         r = head_wid.center ();
357       else
358         r = head_wid[get_direction ()] - stem_wid[get_direction ()];
359     }
360   return r;
361 }
362
363 Real
364 Stem::hpos_f () const
365 {
366   return note_delta_f () + Item::hpos_f ();
367 }
368
369 void
370 Stem::do_substitute_element_pointer (Score_element*o,Score_element*n)
371 {
372   if (Note_head*h=dynamic_cast<Note_head*> (o))
373     head_l_arr_.substitute (h, dynamic_cast<Note_head*>(n));
374   if (Rest *r=dynamic_cast<Rest*> (o))
375     rest_l_arr_.substitute (r, dynamic_cast<Rest*>(n));
376   if (Beam* b = dynamic_cast<Beam*> (o))
377     {
378       if (b == beam_l_) 
379         beam_l_ = dynamic_cast<Beam*> (n);
380     }
381   Staff_symbol_referencer::do_substitute_element_pointer (o,n);
382 }
383