]> git.donarmstrong.com Git - lilypond.git/blob - lily/stem.cc
release: 1.3.55
[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--2000 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7     Jan Nieuwenhuizen <janneke@gnu.org>
8
9   TODO: This is way too hairy
10 */
11 #include <math.h>               // m_pi
12
13 #include "directional-element-interface.hh"
14 #include "dimension-cache.hh"
15 #include "stem.hh"
16 #include "debug.hh"
17 #include "paper-def.hh"
18 #include "note-head.hh"
19 #include "lookup.hh"
20 #include "molecule.hh"
21 #include "paper-column.hh"
22 #include "misc.hh"
23 #include "beam.hh"
24 #include "rest.hh"
25 #include "group-interface.hh"
26 #include "cross-staff.hh"
27 #include "staff-symbol-referencer.hh"
28
29
30
31 void
32 Stem::set_beaming (int i,  Direction d )
33 {
34   SCM pair = get_elt_property ("beaming");
35   
36   if (!gh_pair_p (pair))
37     {
38       pair = gh_cons (gh_int2scm (0),gh_int2scm (0));
39       set_elt_property ("beaming", pair);
40     }
41   index_set_cell (pair, d, gh_int2scm (i));
42 }
43
44 int
45 Stem::beam_count (Direction d) const
46 {
47   SCM p=get_elt_property ("beaming");
48   if (gh_pair_p (p))
49     return gh_scm2int (index_cell (p,d));
50   else
51     return 0;
52 }
53
54 Interval
55 Stem::head_positions () const
56 {
57   if (!heads_i ())
58     {
59       Interval iv;
60       return iv;
61     }
62
63   
64   Drul_array<Note_head*> e (extremal_heads ());
65
66   return Interval (staff_symbol_referencer (e[DOWN]).position_f (),
67                    staff_symbol_referencer( e[UP]).position_f ()); 
68 }
69
70
71 Real
72 Stem::chord_start_f () const
73 {
74   return head_positions()[get_direction ()]
75     * Staff_symbol_referencer_interface (this).staff_space ()/2.0;
76 }
77
78 Real
79 Stem::stem_end_position () const
80 {
81   SCM p =get_elt_property ("stem-end-position");
82   Real pos;
83   if (!gh_number_p (p))
84     {
85       Stem * me = (Stem*) this;
86       pos = get_default_stem_end_position ();
87       me->set_elt_property ("stem-end-position", gh_double2scm (pos));
88     }
89   else
90     pos = gh_scm2double (p);
91
92   return pos;
93 }
94
95 Direction
96 Stem::get_direction () const
97 {
98   Direction d = directional_element (this).get ();
99
100   if (!d)
101     {
102        Stem * me = (Stem*) this;
103        d = get_default_dir ();
104        // urg, AAARGH!
105        directional_element (me).set (d);
106     }
107   return d ;
108 }
109
110
111 void
112 Stem::set_stemend (Real se)
113 {
114   // todo: margins
115   Direction d= get_direction ();
116   
117   if (d && d * head_positions()[get_direction ()] >= se*d)
118     warning (_ ("Weird stem size; check for narrow beams"));
119
120   set_elt_property ("stem-end-position", gh_double2scm (se));
121 }
122
123 int
124 Stem::type_i () const
125 {
126   return first_head () ?  first_head ()->balltype_i () : 2;
127 }
128
129 /*
130   Note head that determines hshift for upstems
131  */ 
132 Score_element*
133 Stem::support_head ()const
134 {
135   SCM h = get_elt_pointer ("support-head");
136   Score_element * nh = unsmob_element (h);
137   if (nh)
138     return nh;
139   else if (heads_i () == 1)
140     {
141       /*
142         UGH.
143        */
144       
145       return unsmob_element (gh_car (get_elt_pointer ("heads")));
146     }
147   else
148     return first_head ();
149 }
150
151
152 int
153 Stem::heads_i ()const
154 {
155   Pointer_group_interface gi (this, "heads");
156   return gi.count ();
157 }
158
159 /*
160   The note head which forms one end of the stem.  
161  */
162 Note_head*
163 Stem::first_head () const
164 {
165   return extremal_heads ()[-get_direction ()];
166 }
167
168 /*
169   START is part where stem reaches `last' head. 
170  */
171 Drul_array<Note_head*>
172 Stem::extremal_heads () const
173 {
174   const int inf = 1000000;
175   Drul_array<int> extpos;
176   extpos[DOWN] = inf;
177   extpos[UP] = -inf;  
178   
179   Drul_array<Note_head *> exthead;
180   exthead[LEFT] = exthead[RIGHT] =0;
181   
182   for (SCM s = get_elt_pointer ("heads"); gh_pair_p (s); s = gh_cdr (s))
183     {
184       Note_head * n = dynamic_cast<Note_head*> (unsmob_element (gh_car (s)));
185       Staff_symbol_referencer_interface si (n);
186       
187       int p = int(si.position_f ());
188
189       Direction d = LEFT;
190       do {
191       if (d* p > d* extpos[d])
192         {
193           exthead[d] = n;
194           extpos[d] = p;
195         }
196       } while (flip (&d) != DOWN);
197     }
198
199   return exthead;
200 }
201
202 void
203 Stem::add_head (Rhythmic_head *n)
204 {
205   n->set_elt_pointer ("stem", this->self_scm_);
206   n->add_dependency (this);
207
208   Pointer_group_interface gi (this);
209   if (Note_head *nh = dynamic_cast<Note_head *> (n))
210     gi.name_ = "heads";
211   else
212     gi.name_ = "rests";
213
214   gi.add_element (n);
215 }
216
217 Stem::Stem (SCM s)
218   : Item (s)
219 {
220   set_elt_pointer ("heads", SCM_EOL);
221   set_elt_pointer ("rests", SCM_EOL);
222
223   add_offset_callback ( &Stem::off_callback, X_AXIS);
224 }
225
226 bool
227 Stem::invisible_b () const
228 {
229   /*
230     UGH. Who determines balltype for stem?
231    */
232   Note_head * nh = dynamic_cast<Note_head*> (support_head ());
233   return !(heads_i () && nh->balltype_i () >= 1);
234 }
235
236 int
237 Stem::get_center_distance (Direction d) const
238 {
239   int staff_center = 0;
240   int distance = (int) (d*(head_positions()[d] - staff_center));
241   return distance >? 0;
242 }
243
244 Direction
245 Stem::get_default_dir () const
246 {
247   int du = get_center_distance (UP);
248   int dd = get_center_distance (DOWN);
249
250   if (sign (dd - du))
251     return Direction (sign (dd -du));
252
253   return Direction (int(paper_l ()->get_var ("stem_default_neutral_direction")));
254 }
255
256 /*
257   ugh. A is used for different purposes. This functionality should be
258   moved into scheme at some point to get rid of the silly
259   conversions. (but lets wait till we have namespaces in SCM)
260  */
261 Real
262 Stem::get_default_stem_end_position () const
263 {
264   bool grace_b = to_boolean (get_elt_property ("grace"));
265   String type_str = grace_b ? "grace-" : "";
266   SCM s;
267   Array<Real> a;
268
269   Real length_f = 0.;
270   SCM scm_len = get_elt_property("length");
271   if (gh_number_p (scm_len))
272     {
273       length_f = gh_scm2double (scm_len);
274     }
275   else
276     {
277       s = scm_eval (ly_symbol2scm ((type_str + "stem-length").ch_C()));
278       for (SCM q = s; q != SCM_EOL; q = gh_cdr (q))
279         a.push (gh_scm2double (gh_car (q)));
280                 
281       // stem uses half-spaces
282       length_f = a[((flag_i () - 2) >? 0) <? (a.size () - 1)] * 2;
283     }
284
285
286   a.clear ();
287   s = scm_eval (ly_symbol2scm ((type_str + "stem-shorten").ch_C()));
288   for (SCM q = s; q != SCM_EOL; q = gh_cdr (q))
289     a.push (gh_scm2double (gh_car (q)));
290
291
292   // stem uses half-spaces
293
294   // fixme: use gh_list_ref () iso. array[]
295   Real shorten_f = a[((flag_i () - 2) >? 0) <? (a.size () - 1)] * 2;
296
297   /* URGURGURG
298      'set-default-stemlen' sets direction too
299    */
300   Direction dir = get_direction ();
301   if (!dir)
302     {
303       dir = get_default_dir ();
304       directional_element (this).set (dir);
305     }
306   
307   /* 
308     stems in unnatural (forced) direction should be shortened, 
309     according to [Roush & Gourlay]
310    */
311   if (((int)chord_start_f ())
312       && (get_direction () != get_default_dir ()))
313     length_f -= shorten_f;
314
315
316    Real st = head_positions()[dir] + dir * length_f;
317   
318    bool no_extend_b = to_boolean (get_elt_property ("no-stem-extend"));
319     if (!grace_b && !no_extend_b && dir * st < 0)
320       st = 0.0;
321
322   return st;
323 }
324
325 /*
326   FIXME: wrong name
327  */
328 int
329 Stem::flag_i () const
330 {
331   SCM s = get_elt_property ("duration-log");
332   return  (gh_number_p (s)) ? gh_scm2int (s) : 2;
333 }
334
335 void
336 Stem::position_noteheads ()
337 {
338   if (!heads_i ())
339     return;
340   
341   Link_array<Score_element> heads =
342     Pointer_group_interface__extract_elements (this, (Score_element*)0, "heads");
343
344   heads.sort (compare_position);
345   Direction dir =get_direction ();
346   
347   if (dir < 0)
348     heads.reverse ();
349
350
351   Real w = support_head ()->extent (X_AXIS)[dir];
352   for (int i=0; i < heads.size (); i++)
353     {
354       heads[i]->translate_axis (w - heads[i]->extent (X_AXIS)[dir], X_AXIS);
355     }
356   
357   bool parity= true;            // todo: make this settable.
358   int lastpos = int (Staff_symbol_referencer_interface (heads[0]).position_f ());
359   for (int i=1; i < heads.size (); i ++)
360     {
361       Real p = Staff_symbol_referencer_interface (heads[i]).position_f ();
362       int dy =abs (lastpos- (int)p);
363
364       if (dy <= 1)
365         {
366           if (parity)
367             {
368               Real l  = heads[i]->extent (X_AXIS).length ();
369               heads[i]->translate_axis (l * get_direction (), X_AXIS);
370             }
371           parity = !parity;
372         }
373       else
374         parity = true;
375       
376       lastpos = int (p);
377     }
378 }
379
380 void
381 Stem::before_line_breaking ()
382 {
383   stem_end_position (); // ugh. Trigger direction calc.
384   position_noteheads ();
385
386   if (invisible_b ())
387     {
388       set_elt_property ("transparent", SCM_BOOL_T);
389       set_extent_callback (0, Y_AXIS);      
390       set_extent_callback (0, X_AXIS);      
391     }
392
393   set_spacing_hints ();
394 }
395
396
397
398 /**
399    set stem directions for hinting the optical spacing correction.
400
401    Modifies DIR_LIST property of the Stem's Paper_column
402
403    TODO: more advanced: supply height of noteheads as well, for more advanced spacing possibilities
404  */
405 void
406 Stem::set_spacing_hints () 
407 {
408   if (!invisible_b ())
409     {
410       SCM scmdir  = gh_int2scm (get_direction ());
411       SCM dirlist = column_l ()->get_elt_property ("dir-list");
412       if (dirlist == SCM_UNDEFINED)
413         dirlist = SCM_EOL;
414
415       if (scm_sloppy_memq (scmdir, dirlist) == SCM_EOL)
416         {
417           dirlist = gh_cons (scmdir, dirlist);
418           column_l ()->set_elt_property ("dir-list", dirlist);
419         }
420     }
421 }
422
423 Molecule
424 Stem::flag () const
425 {
426   String style;
427   SCM st = get_elt_property ("flag-style");
428   if ( gh_string_p (st))
429     {
430       style = ly_scm2string (st);
431     }
432
433   char c = (get_direction () == UP) ? 'u' : 'd';
434   Molecule m = lookup_l ()->afm_find (String ("flags-") + to_str (c) + 
435                                       to_str (flag_i ()));
436   if (!style.empty_b ())
437     m.add_molecule(lookup_l ()->afm_find (String ("flags-") + to_str (c) + style));
438   return m;
439 }
440
441 Interval
442 Stem::dim_callback (Score_element const *se, Axis ) 
443 {
444   Stem * s = dynamic_cast<Stem*> ((Score_element*)se);
445   
446   Interval r (0, 0);
447   if (unsmob_element (s->get_elt_pointer ("beam")) || abs (s->flag_i ()) <= 2)
448     ;   // TODO!
449   else
450     {
451       r = s->flag ().extent (X_AXIS);
452     }
453   return r;
454 }
455
456
457 const Real ANGLE = 20* (2.0*M_PI/360.0); // ugh!
458
459 Molecule 
460 Stem::do_brew_molecule () const
461 {
462   Molecule mol;
463
464   Staff_symbol_referencer_interface si (first_head ());
465   
466   Real y1 = si.position_f();
467   Real y2 = stem_end_position ();
468   
469   Interval stem_y(y1,y2);
470   stem_y.unite (Interval (y2,y1));
471
472   Real dy = staff_symbol_referencer (this).staff_space ()/2.0;
473   Real head_wid = 0;
474   if (support_head ())
475     head_wid = support_head ()->extent (X_AXIS).length ();
476   stem_y[Direction(-get_direction ())] += get_direction () * head_wid * tan(ANGLE)/(2*dy);
477   
478   if (!invisible_b ())
479     {
480       Real stem_width = paper_l ()->get_var ("stemthickness");
481       Molecule ss =lookup_l ()->filledbox (Box (Interval (-stem_width/2, stem_width/2),
482                                                  Interval (stem_y[DOWN]*dy, stem_y[UP]*dy)));
483       mol.add_molecule (ss);
484     }
485
486   if (!beam_l () && abs (flag_i ()) > 2)
487     {
488       Molecule fl = flag ();
489       fl.translate_axis(stem_y[get_direction ()]*dy, Y_AXIS);
490       mol.add_molecule (fl);
491     }
492
493   return mol;
494 }
495
496 Real
497 Stem::off_callback (Score_element const* se, Axis)
498 {
499   Stem *st = dynamic_cast<Stem*> ((Score_element*)se);
500
501   Real r=0;
502   if (Note_head * f = st->first_head ())
503     {
504       Interval head_wid(0, f->extent (X_AXIS).length ());
505
506       if (to_boolean (st->get_elt_property ("stem-centered")))
507         return head_wid.center ();
508       
509       Real rule_thick = st->paper_l ()->get_var ("stemthickness");
510       Direction d = st->get_direction ();
511       r = head_wid[d] - d * rule_thick ;
512     }
513   return r;
514 }
515
516
517
518 Beam*
519 Stem::beam_l ()const
520 {
521   SCM b=  get_elt_pointer ("beam");
522   return dynamic_cast<Beam*> (unsmob_element (b));
523 }
524
525
526 // ugh still very long.
527 Stem_info
528 Stem::calc_stem_info () const
529 {
530   assert (beam_l ());
531
532   Direction beam_dir = directional_element (beam_l ()).get ();
533   if (!beam_dir)
534     {
535       programming_error ("Beam dir not set.");
536       beam_dir = UP;
537     }
538     
539   Staff_symbol_referencer_interface st (this);
540   Real staff_space = st.staff_space ();
541   Real half_space = staff_space / 2;
542   Real interbeam_f = paper_l ()->interbeam_f (beam_l ()->get_multiplicity ());
543   Real thick = gh_scm2double (beam_l ()->get_elt_property ("beam-thickness"));
544   int multiplicity = beam_l ()->get_multiplicity ();
545
546   Stem_info info; 
547   info.idealy_f_ = chord_start_f ();
548
549   // for simplicity, we calculate as if dir == UP
550   info.idealy_f_ *= beam_dir;
551   SCM grace_prop = get_elt_property ("grace");
552
553   bool grace_b = to_boolean (grace_prop);
554   
555   Array<Real> a;
556   SCM s;
557   String type_str = grace_b ? "grace-" : "";
558   
559   s = scm_eval (ly_symbol2scm ((type_str + "beamed-stem-minimum-length").ch_C()));
560   a.clear ();
561   for (SCM q = s; q != SCM_EOL; q = gh_cdr (q))
562     a.push (gh_scm2double (gh_car (q)));
563
564
565   Real minimum_length = a[multiplicity <? (a.size () - 1)] * staff_space;
566   s = scm_eval (ly_symbol2scm ((type_str + "beamed-stem-length").ch_C()));
567
568   a.clear();
569   for (SCM q = s; q != SCM_EOL; q = gh_cdr (q))
570     a.push (gh_scm2double (gh_car (q)));
571
572   Real stem_length =  a[multiplicity <? (a.size () - 1)] * staff_space;
573
574   if (!beam_dir || (beam_dir == directional_element (this).get ()))
575     /* normal beamed stem */
576     {
577       if (multiplicity)
578         {
579           info.idealy_f_ += thick + (multiplicity - 1) * interbeam_f;
580         }
581       info.miny_f_ = info.idealy_f_;
582       info.maxy_f_ = INT_MAX;
583
584       info.idealy_f_ += stem_length;
585       info.miny_f_ += minimum_length;
586
587       /*
588         lowest beam of (UP) beam must never be lower than second staffline
589
590         Hmm, reference (Wanske?)
591
592         Although this (additional) rule is probably correct,
593         I expect that highest beam (UP) should also never be lower
594         than middle staffline, just as normal stems.
595         
596       */
597       bool no_extend_b = to_boolean (get_elt_property ("no-stem-extend"));
598       if (!grace_b && !no_extend_b)
599         {
600           /* highest beam of (UP) beam must never be lower than middle
601              staffline
602              lowest beam of (UP) beam must never be lower than second staffline
603            */
604           info.miny_f_ =
605             info.miny_f_ >? 0
606             >? (- 2 * half_space - thick
607                 + (multiplicity > 0) * thick
608                 + interbeam_f * (multiplicity - 1));
609         }
610     }
611   else
612     /* knee */
613     {
614       info.idealy_f_ -= thick;
615       info.maxy_f_ = info.idealy_f_;
616       info.miny_f_ = -INT_MAX;
617
618       info.idealy_f_ -= stem_length;
619       info.maxy_f_ -= minimum_length;
620     }
621   
622   info.idealy_f_ = (info.maxy_f_ <? info.idealy_f_) >? info.miny_f_;
623
624   s = beam_l ()->get_elt_property ("shorten");
625   if (gh_number_p (s))
626     info.idealy_f_ -= gh_double2scm (s);
627
628   Real interstaff_f = -beam_dir* calc_interstaff_dist (this, beam_l ());
629
630   info.idealy_f_ += interstaff_f;
631   info.miny_f_ += interstaff_f;
632   info.maxy_f_ += interstaff_f ;
633
634   return info;
635 }
636