]> git.donarmstrong.com Git - lilypond.git/blob - lily/stem.cc
74ac9349aec235fc5a192121f019db55bc48e095
[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 #include "dimension-cache.hh"
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 }
28
29 Interval_t<int>
30 Stem::head_positions () const
31 {
32   /* 
33     Mysterious FreeBSD fix by John Galbraith.  Somehow, the empty intervals 
34     trigger FP exceptions on FreeBSD.  Fix: do not return infinity 
35
36    */
37   if (!head_l_arr_.size ())
38     {
39       return Interval_t<int> (100,-100);        
40     }
41
42   Interval_t<int> r;
43   for (int i =0; i < head_l_arr_.size (); i++)
44     {
45       int p = (int)head_l_arr_[i]->position_f ();
46       r[BIGGER] = r[BIGGER] >? p;
47       r[SMALLER] = r[SMALLER] <? p;
48     }
49   return r;
50 }
51
52 void
53 Stem::do_print () const
54 {
55 #ifndef NPRINT
56   DEBUG_OUT << "flag "<< flag_i_;
57 #endif
58 }
59
60 Real
61 Stem::stem_length_f () const
62 {
63   return yextent_drul_[UP]-yextent_drul_[DOWN] ;
64 }
65
66 Real
67 Stem::stem_begin_f () const
68 {
69   return yextent_drul_[Direction(-get_direction ())];
70 }
71
72 Real
73 Stem::chord_start_f () const
74 {
75   return head_positions()[get_direction ()] * staff_line_leading_f ()/2.0;
76 }
77
78 Real
79 Stem::stem_end_f () const
80 {
81   return yextent_drul_[get_direction ()];
82 }
83
84 void
85 Stem::set_stemend (Real se)
86 {
87   // todo: margins
88   if (get_direction () && get_direction () * head_positions()[get_direction ()] >= se*get_direction ())
89     warning (_ ("Weird stem size; check for narrow beams"));
90
91   
92   yextent_drul_[get_direction ()]  =  se;
93   yextent_drul_[Direction(-get_direction ())] = head_positions()[-get_direction ()];
94 }
95
96 int
97 Stem::type_i () const
98 {
99   return head_l_arr_[0]->balltype_i_;
100 }
101
102 void
103 Stem::add_head (Rhythmic_head *n)
104 {
105   n->stem_l_ = this;
106   n->add_dependency (this);     // ?
107   if (Note_head *nh = dynamic_cast<Note_head *> (n))
108     {
109       head_l_arr_.push (nh);
110     }
111   else if (Rest *r = dynamic_cast<Rest *> (n))
112     {
113       rest_l_arr_.push (r);
114     }
115 }
116
117 bool
118 Stem::invisible_b () const
119 {
120   return (!head_l_arr_.size () ||
121     head_l_arr_[0]->balltype_i_ <= 0);
122 }
123
124 int
125 Stem::get_center_distance (Direction d) const
126 {
127   int staff_center = 0;
128   int distance = d*(head_positions()[d] - staff_center);
129   return distance >? 0;
130 }
131
132 Direction
133 Stem::get_default_dir () const
134 {
135   int du = get_center_distance (UP);
136   int dd = get_center_distance (DOWN);
137
138   if (sign (dd - du))
139     return Direction (sign (dd -du));
140
141   return Direction (int(paper_l ()->get_var ("stem_default_neutral_direction")));
142 }
143
144
145
146 void
147 Stem::set_default_stemlen ()
148 {
149   Real length_f = 0.;
150   SCM scm_len = get_elt_property("length");
151   if (scm_len != SCM_UNDEFINED)
152     {
153       length_f = gh_scm2double (scm_len);
154     }
155   else
156     length_f = paper_l ()->get_var ("stem_length0");
157
158   bool grace_b = get_elt_property ("grace") != SCM_UNDEFINED;
159   String type_str = grace_b ? "grace_" : "";
160
161   Real shorten_f = paper_l ()->get_var (type_str + "forced_stem_shorten0");
162
163   if (!get_direction ())
164     set_direction (get_default_dir ());
165
166   /* 
167     stems in unnatural (forced) direction should be shortened, 
168     according to [Roush & Gourlay]
169    */
170   if (((int)chord_start_f ())
171       && (get_direction () != get_default_dir ()))
172     length_f -= shorten_f;
173
174   if (flag_i_ >= 5)
175     length_f += 2.0;
176   if (flag_i_ >= 6)
177     length_f += 1.0;
178   
179   set_stemend ((get_direction () > 0) ? head_positions()[BIGGER] + length_f:
180                head_positions()[SMALLER] - length_f);
181
182   bool no_extend_b = get_elt_property ("no-stem-extend") != SCM_UNDEFINED;
183   if (!grace_b && !no_extend_b && (get_direction () * stem_end_f () < 0))
184     set_stemend (0);
185 }
186
187 //xxx
188 void
189 Stem::set_default_extents ()
190 {
191   if (!stem_length_f ())
192     set_default_stemlen ();
193
194 }
195
196 void
197 Stem::set_noteheads ()
198 {
199   if (!head_l_arr_.size ())
200     return;
201   head_l_arr_.sort (Note_head::compare);
202   if (get_direction () < 0)
203     head_l_arr_.reverse ();
204
205   Note_head * beginhead =   head_l_arr_[0];
206   beginhead->set_elt_property ("extremal", SCM_BOOL_T);
207   if  (beginhead !=   head_l_arr_.top ())
208     head_l_arr_.top ()->set_elt_property ("extremal", SCM_BOOL_T);
209   
210   int parity=1;
211   int lastpos = int (beginhead->position_f ());
212   for (int i=1; i < head_l_arr_.size (); i ++)
213     {
214       int dy =abs (lastpos- (int)head_l_arr_[i]->position_f ());
215
216       if (dy <= 1)
217         {
218           if (parity)
219             head_l_arr_[i]->flip_around_stem (get_direction ());
220           parity = !parity;
221         }
222       else
223         parity = 1;
224       lastpos = int (head_l_arr_[i]->position_f ());
225     }
226 }
227
228 void
229 Stem::do_pre_processing ()
230 {
231   if (yextent_drul_[DOWN]== yextent_drul_[UP])
232     set_default_extents ();
233   set_noteheads ();
234
235   if (invisible_b ())
236     {
237       set_elt_property ("transparent", SCM_BOOL_T);
238       set_empty (Y_AXIS);      
239       set_empty (X_AXIS);      
240     }
241
242
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::dim_callback (Dimension_cache const* c) 
294 {
295   Stem * s = dynamic_cast<Stem*> (c->element_l ());
296   
297   Interval r (0, 0);
298   if (s->get_elt_property ("beam") != SCM_UNDEFINED || abs (s->flag_i_) <= 2)
299     ;   // TODO!
300   else
301     {
302       r = s->flag ().dim_.x ();
303       r += s->note_delta_f ();
304     }
305   return r;
306 }
307
308
309
310
311 const Real ANGLE = 20* (2.0*M_PI/360.0); // ugh!
312
313 Molecule*
314 Stem::do_brew_molecule_p () const
315 {
316   Molecule *mol_p =new Molecule;
317   Drul_array<Real> stem_y = yextent_drul_;
318   Real dy = staff_line_leading_f ()/2.0;
319
320   Real head_wid = 0;
321   if (head_l_arr_.size ())
322     head_wid = head_l_arr_[0]->extent (X_AXIS).length ();
323   stem_y[Direction(-get_direction ())] += get_direction () * head_wid * tan(ANGLE)/(2*dy);
324   
325   if (!invisible_b ())
326     {
327       Real stem_width = paper_l ()->get_var ("stemthickness");
328       Molecule ss =lookup_l ()->filledbox (Box (Interval (-stem_width/2, stem_width/2),
329                                                  Interval (stem_y[DOWN]*dy, stem_y[UP]*dy)));
330       mol_p->add_molecule (ss);
331     }
332
333   if (get_elt_property ("beam") == SCM_UNDEFINED
334       && abs (flag_i_) > 2)
335     {
336       Molecule fl = flag ();
337       fl.translate_axis(stem_y[get_direction ()]*dy, Y_AXIS);
338       mol_p->add_molecule (fl);
339     }
340
341   if (head_l_arr_.size())
342     {
343       mol_p->translate_axis (note_delta_f (), X_AXIS);
344     }
345   return mol_p;
346 }
347
348 Real
349 Stem::note_delta_f () const
350 {
351   Real r=0;
352   if (head_l_arr_.size())
353     {
354       Interval head_wid(0,  head_l_arr_[0]->extent (X_AXIS).length ());
355          Real rule_thick = paper_l ()->get_var ("stemthickness");
356
357       Interval stem_wid(-rule_thick/2, rule_thick/2);
358       if (get_direction () == CENTER)
359         r = head_wid.center ();
360       else
361         r = head_wid[get_direction ()] - stem_wid[get_direction ()];
362     }
363   return r;
364 }
365
366 Real
367 Stem::hpos_f () const
368 {
369   return note_delta_f () + Item::hpos_f ();
370 }
371
372 void
373 Stem::do_substitute_element_pointer (Score_element*o,Score_element*n)
374 {
375   if (Note_head*h=dynamic_cast<Note_head*> (o))
376     head_l_arr_.substitute (h, dynamic_cast<Note_head*>(n));
377   if (Rest *r=dynamic_cast<Rest*> (o))
378     rest_l_arr_.substitute (r, dynamic_cast<Rest*>(n));
379 }
380
381 Beam*
382 Stem::beam_l ()const
383 {
384   SCM b=  get_elt_property ("beam");
385   if (SMOB_IS_TYPE_B(Score_element, b))
386     return dynamic_cast<Beam*> (SMOB_TO_TYPE(Score_element,b));
387   else
388     return 0;
389 }