]> git.donarmstrong.com Git - lilypond.git/blob - lily/stem.cc
release: 1.3.11
[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     Jan Nieuwenhuizen <janneke@gnu.org>
8
9   TODO: This is way too hairy
10 */
11 #include "dimension-cache.hh"
12 #include "stem.hh"
13 #include "debug.hh"
14 #include "paper-def.hh"
15 #include "note-head.hh"
16 #include "lookup.hh"
17 #include "molecule.hh"
18 #include "paper-column.hh"
19 #include "misc.hh"
20 #include "beam.hh"
21 #include "rest.hh"
22 #include "group-interface.hh"
23 #include "cross-staff.hh"
24 #include "staff-symbol-referencer.hh"
25
26 Stem::Stem ()
27 {
28   beams_i_drul_[LEFT] = beams_i_drul_[RIGHT] = -1;
29 }
30
31 Interval_t<int>
32 Stem::head_positions () const
33 {
34   /* 
35     Mysterious FreeBSD fix by John Galbraith.  Somehow, the empty intervals 
36     trigger FP exceptions on FreeBSD.  Fix: do not return infinity 
37
38    */
39   if (!first_head ())
40     {
41       return Interval_t<int> (100,-100);        
42     }
43
44   Link_array<Note_head> head_l_arr =
45     Group_interface__extract_elements (this, (Note_head*)0, "heads");
46   
47   Interval_t<int> r;
48   for (int i =0; i < head_l_arr.size (); i++)
49     {
50       Staff_symbol_referencer_interface si (head_l_arr[i]);
51       int p = (int)si.position_f ();
52       r[BIGGER] = r[BIGGER] >? p;
53       r[SMALLER] = r[SMALLER] <? p;
54     }
55   return r;
56 }
57
58
59 Real
60 Stem::stem_length_f () const
61 {
62   return yextent_.length();
63 }
64
65 Real
66 Stem::stem_begin_f () const
67 {
68   return yextent_[Direction(-get_direction ())];
69 }
70
71 Real
72 Stem::chord_start_f () const
73 {
74   return head_positions()[get_direction ()]
75     * Staff_symbol_referencer_interface (this).staff_line_leading_f ()/2.0;
76 }
77
78 Real
79 Stem::stem_end_f () const
80 {
81   return yextent_[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_[get_direction ()]  =  se;
93   yextent_[Direction(-get_direction ())] = head_positions()[-get_direction ()];
94 }
95
96 int
97 Stem::type_i () const
98 {
99   return first_head ()->balltype_i ();
100 }
101
102 Note_head*
103 Stem::first_head () const
104 {
105   SCM h =get_elt_property ("heads");
106   if (!gh_pair_p (h))
107     return 0;
108
109   Score_element * sc = unsmob_element (gh_car (h));
110
111   return dynamic_cast<Note_head*> (sc);
112 }
113
114 void
115 Stem::add_head (Rhythmic_head *n)
116 {
117   n->set_elt_property ("stem", this->self_scm_);
118   n->add_dependency (this);     // ?
119   
120
121   Group_interface gi (this);
122   if (Note_head *nh = dynamic_cast<Note_head *> (n))
123     gi.name_ = "heads";
124   else
125     gi.name_ = "rests";
126
127   gi.add_element (n);
128 }
129
130 bool
131 Stem::invisible_b () const
132 {
133   return !(first_head () && first_head()->balltype_i () >= 1);
134 }
135
136 int
137 Stem::get_center_distance (Direction d) const
138 {
139   int staff_center = 0;
140   int distance = d*(head_positions()[d] - staff_center);
141   return distance >? 0;
142 }
143
144 Direction
145 Stem::get_default_dir () const
146 {
147   int du = get_center_distance (UP);
148   int dd = get_center_distance (DOWN);
149
150   if (sign (dd - du))
151     return Direction (sign (dd -du));
152
153   return Direction (int(paper_l ()->get_var ("stem_default_neutral_direction")));
154 }
155
156 void
157 Stem::set_default_stemlen ()
158 {
159   Real length_f = 0.;
160   SCM scm_len = get_elt_property("length");
161   if (scm_len != SCM_UNDEFINED)
162     {
163       length_f = gh_scm2double (scm_len);
164     }
165   else
166     length_f = paper_l ()->get_var ("stem_length0");
167
168   bool grace_b = get_elt_property ("grace") != SCM_UNDEFINED;
169   String type_str = grace_b ? "grace_" : "";
170
171   Real shorten_f = paper_l ()->get_var (type_str + "forced_stem_shorten0");
172
173   if (!get_direction ())
174     set_direction (get_default_dir ());
175
176   /* 
177     stems in unnatural (forced) direction should be shortened, 
178     according to [Roush & Gourlay]
179    */
180   if (((int)chord_start_f ())
181       && (get_direction () != get_default_dir ()))
182     length_f -= shorten_f;
183  
184  if (flag_i () >= 5)
185     length_f += 2.0;
186   if (flag_i () >= 6)
187     length_f += 1.0;
188   
189   set_stemend ((get_direction () > 0) ? head_positions()[BIGGER] + length_f:
190                head_positions()[SMALLER] - length_f);
191
192   bool no_extend_b = get_elt_property ("no-stem-extend") != SCM_UNDEFINED;
193   if (!grace_b && !no_extend_b && (get_direction () * stem_end_f () < 0))
194     set_stemend (0);
195 }
196
197 int
198 Stem::flag_i () const
199 {
200   SCM s = get_elt_property ("duration-log");
201   return  (gh_number_p (s)) ? gh_scm2int (s) : 2;
202 }
203
204 //xxx
205 void
206 Stem::set_default_extents ()
207 {
208   if (!stem_length_f ())
209     set_default_stemlen ();
210
211 }
212
213 void
214 Stem::set_noteheads ()
215 {
216   if (!first_head ())
217     return;
218
219   
220   Link_array<Note_head> head_l_arr =
221     Group_interface__extract_elements (this, (Note_head*)0, "heads");
222
223   head_l_arr.sort (Note_head::compare);
224   if (get_direction () < 0)
225     head_l_arr.reverse ();
226
227   Note_head * beginhead =   first_head ();
228   beginhead->set_elt_property ("extremal", SCM_BOOL_T);
229   if  (beginhead !=   head_l_arr.top ())
230     head_l_arr.top ()->set_elt_property ("extremal", SCM_BOOL_T);
231   
232   int parity=1;
233   int lastpos = int (Staff_symbol_referencer_interface (beginhead).position_f ());
234   for (int i=1; i < head_l_arr.size (); i ++)
235     {
236       Real p = Staff_symbol_referencer_interface (head_l_arr[i]).position_f ();
237       int dy =abs (lastpos- (int)p);
238
239       if (dy <= 1)
240         {
241           if (parity)
242             head_l_arr[i]->flip_around_stem (get_direction ());
243           parity = !parity;
244         }
245       else
246         parity = 1;
247       lastpos = int (p);
248     }
249 }
250
251 void
252 Stem::do_pre_processing ()
253 {
254   if (yextent_.empty_b ())
255     set_default_extents ();
256   set_noteheads ();
257
258   if (invisible_b ())
259     {
260       set_elt_property ("transparent", SCM_BOOL_T);
261       set_empty (Y_AXIS);      
262       set_empty (X_AXIS);      
263     }
264
265
266   set_spacing_hints ();
267 }
268
269
270
271 /**
272    set stem directions for hinting the optical spacing correction.
273
274    Modifies DIR_LIST property of the Stem's Score_column
275
276    TODO: more advanced: supply height of noteheads as well, for more advanced spacing possibilities
277  */
278
279 void
280 Stem::set_spacing_hints () 
281 {
282   if (!invisible_b ())
283     {
284       SCM scmdir  = gh_int2scm (get_direction ());
285       SCM dirlist = column_l ()->get_elt_property ("dir-list");
286       if (dirlist == SCM_UNDEFINED)
287         dirlist = SCM_EOL;
288
289       if (scm_sloppy_memq (scmdir, dirlist) == SCM_EOL)
290         {
291           dirlist = gh_cons (scmdir, dirlist);
292           column_l ()->set_elt_property ("dir-list", dirlist);
293         }
294     }
295 }
296
297 Molecule
298 Stem::flag () const
299 {
300   String style;
301   SCM st = get_elt_property ("style");
302   if ( st != SCM_UNDEFINED)
303     {
304       style = ly_scm2string (st);
305     }
306
307   char c = (get_direction () == UP) ? 'u' : 'd';
308   Molecule m = lookup_l ()->afm_find (String ("flags-") + to_str (c) + 
309                                       to_str (flag_i ()));
310   if (!style.empty_b ())
311     m.add_molecule(lookup_l ()->afm_find (String ("flags-") + to_str (c) + style));
312   return m;
313 }
314
315 Interval
316 Stem::dim_callback (Dimension_cache const* c) 
317 {
318   Stem * s = dynamic_cast<Stem*> (c->element_l ());
319   
320   Interval r (0, 0);
321   if (s->get_elt_property ("beam") != SCM_UNDEFINED || abs (s->flag_i ()) <= 2)
322     ;   // TODO!
323   else
324     {
325       r = s->flag ().dim_.x ();
326       r += s->note_delta_f ();
327     }
328   return r;
329 }
330
331
332
333
334 const Real ANGLE = 20* (2.0*M_PI/360.0); // ugh!
335
336 Molecule*
337 Stem::do_brew_molecule_p () const
338 {
339   Molecule *mol_p =new Molecule;
340   Interval stem_y = yextent_;
341   Real dy = staff_symbol_referencer_interface (this)
342     .staff_line_leading_f ()/2.0;
343
344   Real head_wid = 0;
345   if (first_head ())
346     head_wid = first_head ()->extent (X_AXIS).length ();
347   stem_y[Direction(-get_direction ())] += get_direction () * head_wid * tan(ANGLE)/(2*dy);
348   
349   if (!invisible_b ())
350     {
351       Real stem_width = paper_l ()->get_var ("stemthickness");
352       Molecule ss =lookup_l ()->filledbox (Box (Interval (-stem_width/2, stem_width/2),
353                                                  Interval (stem_y[DOWN]*dy, stem_y[UP]*dy)));
354       mol_p->add_molecule (ss);
355     }
356
357   if (get_elt_property ("beam") == SCM_UNDEFINED
358       && abs (flag_i ()) > 2)
359     {
360       Molecule fl = flag ();
361       fl.translate_axis(stem_y[get_direction ()]*dy, Y_AXIS);
362       mol_p->add_molecule (fl);
363     }
364
365   if (first_head ())
366     {
367       mol_p->translate_axis (note_delta_f (), X_AXIS);
368     }
369   return mol_p;
370 }
371
372 Real
373 Stem::note_delta_f () const
374 {
375   Real r=0;
376   if (first_head ())
377     {
378       Interval head_wid(0,  first_head()->extent (X_AXIS).length ());
379          Real rule_thick = paper_l ()->get_var ("stemthickness");
380
381       Interval stem_wid(-rule_thick/2, rule_thick/2);
382       if (get_direction () == CENTER)
383         r = head_wid.center ();
384       else
385         r = head_wid[get_direction ()] - stem_wid[get_direction ()];
386     }
387   return r;
388 }
389
390 Real
391 Stem::hpos_f () const
392 {
393   return note_delta_f () + Item::hpos_f ();
394 }
395
396
397 Beam*
398 Stem::beam_l ()const
399 {
400   SCM b=  get_elt_property ("beam");
401   return dynamic_cast<Beam*> (unsmob_element (b));
402 }
403
404
405 Stem_info
406 Stem::calc_stem_info () const
407 {
408   assert (beam_l ());
409
410   SCM bd = get_elt_property ("beam-dir");
411   Real internote_f
412     = staff_symbol_referencer_interface (this).staff_line_leading_f ()/2;
413   
414   Direction beam_dir;
415   Stem_info info; 
416
417   if (isdir_b (bd))
418     {
419       beam_dir = to_dir (bd);
420     }
421   else
422     {
423       programming_error ("Beam direction not set."); 
424       beam_dir = UP;            //  GURAUGRNAGURAGU! urg !
425     }
426   
427   Real interbeam_f = paper_l ()->interbeam_f (beam_l ()->multiplicity_i_);
428   Real beam_f = gh_scm2double (beam_l ()->get_elt_property ("beam-thickness"));
429          
430   info.idealy_f_ = chord_start_f ();
431
432   // for simplicity, we calculate as if dir == UP
433   info.idealy_f_ *= beam_dir;
434
435   bool grace_b = get_elt_property ("grace") != SCM_UNDEFINED;
436   bool no_extend_b = get_elt_property ("no-stem-extend") != SCM_UNDEFINED;
437
438   int stem_max = (int)rint(paper_l ()->get_var ("stem_max"));
439   String type_str = grace_b ? "grace_" : "";
440   Real min_stem_f = paper_l ()->get_var (type_str + "minimum_stem_length"
441     + to_str (beam_l ()->multiplicity_i_ <? stem_max)) * internote_f;
442   Real stem_f = paper_l ()->get_var (type_str + "stem_length"
443     + to_str (beam_l ()->multiplicity_i_ <? stem_max)) * internote_f;
444
445   if (!beam_dir || (beam_dir == get_direction ()))
446     /* normal beamed stem */
447     {
448       if (beam_l ()->multiplicity_i_)
449         {
450           info.idealy_f_ += beam_f;
451           info.idealy_f_ += (beam_l ()->multiplicity_i_ - 1) * interbeam_f;
452         }
453       info.miny_f_ = info.idealy_f_;
454       info.maxy_f_ = INT_MAX;
455
456       info.idealy_f_ += stem_f;
457       info.miny_f_ += min_stem_f;
458
459       /*
460         lowest beam of (UP) beam must never be lower than second staffline
461
462         Hmm, reference (Wanske?)
463
464         Although this (additional) rule is probably correct,
465         I expect that highest beam (UP) should also never be lower
466         than middle staffline, just as normal stems.
467         
468       */
469       if (!grace_b && !no_extend_b)
470         {
471           //highest beam of (UP) beam must never be lower than middle staffline
472           info.miny_f_ = info.miny_f_ >? 0;
473           //lowest beam of (UP) beam must never be lower than second staffline
474           info.miny_f_ = info.miny_f_ >? (- 2 * internote_f - beam_f
475                                 + (beam_l ()->multiplicity_i_ > 0) * beam_f + interbeam_f * (beam_l ()->multiplicity_i_ - 1));
476         }
477     }
478   else
479     /* knee */
480     {
481       info.idealy_f_ -= beam_f;
482       info.maxy_f_ = info.idealy_f_;
483       info.miny_f_ = -INT_MAX;
484
485       info.idealy_f_ -= stem_f;
486       info.maxy_f_ -= min_stem_f;
487     }
488
489   info.idealy_f_ = info.maxy_f_ <? info.idealy_f_;
490   info.idealy_f_ = info.miny_f_ >? info.idealy_f_;
491
492   Real interstaff_f = calc_interstaff_dist (this, beam_l ());
493   info.idealy_f_ += interstaff_f * beam_dir;
494
495   SCM s = get_elt_property ("shorten");
496   if (s != SCM_UNDEFINED)
497     info.idealy_f_ -= gh_double2scm (s);
498   info.miny_f_ += interstaff_f * beam_dir;
499   info.maxy_f_ += interstaff_f * beam_dir;
500
501   return info;
502 }
503