]> git.donarmstrong.com Git - lilypond.git/blob - lily/stem.cc
Merge with master
[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--2007 Han-Wen Nienhuys <hanwen@xs4all.nl>
7   Jan Nieuwenhuizen <janneke@gnu.org>
8
9   TODO: This is way too hairy
10
11   TODO: fix naming.
12
13   Stem-end, chord-start, etc. is all confusing naming.
14 */
15
16 #include "stem.hh"
17 #include "spanner.hh"
18
19 #include <cmath>                // rint
20 using namespace std;
21
22 #include "beam.hh"
23 #include "directional-element-interface.hh"
24 #include "dot-column.hh"
25 #include "font-interface.hh"
26 #include "international.hh"
27 #include "lookup.hh"
28 #include "misc.hh"
29 #include "note-head.hh"
30 #include "output-def.hh"
31 #include "paper-column.hh"
32 #include "pointer-group-interface.hh"
33 #include "rest.hh"
34 #include "rhythmic-head.hh"
35 #include "side-position-interface.hh"
36 #include "staff-symbol-referencer.hh"
37 #include "stem-tremolo.hh"
38 #include "warn.hh"
39
40 void
41 Stem::set_beaming (Grob *me, int beam_count, Direction d)
42 {
43   SCM pair = me->get_property ("beaming");
44
45   if (!scm_is_pair (pair))
46     {
47       pair = scm_cons (SCM_EOL, SCM_EOL);
48       me->set_property ("beaming", pair);
49     }
50
51   SCM lst = index_get_cell (pair, d);
52   if (beam_count)
53     for (int i = 0; i < beam_count; i++)
54       lst = scm_cons (scm_from_int (i), lst);
55   else
56     lst = SCM_BOOL_F;
57   
58   index_set_cell (pair, d, lst);
59 }
60
61 int
62 Stem::get_beaming (Grob *me, Direction d)
63 {
64   SCM pair = me->get_property ("beaming");
65   if (!scm_is_pair (pair))
66     return 0;
67
68   SCM lst = index_get_cell (pair, d);
69
70   int len = scm_ilength (lst);
71   return max (len, 0);
72 }
73
74 Interval
75 Stem::head_positions (Grob *me)
76 {
77   if (head_count (me))
78     {
79       Drul_array<Grob *> e (extremal_heads (me));
80       return Interval (Staff_symbol_referencer::get_position (e[DOWN]),
81                        Staff_symbol_referencer::get_position (e[UP]));
82     }
83   return Interval ();
84 }
85
86 Real
87 Stem::chord_start_y (Grob *me)
88 {
89   Interval hp = head_positions (me);
90   if (!hp.is_empty ())
91     return hp[get_grob_direction (me)] * Staff_symbol_referencer::staff_space (me)
92       * 0.5;
93   return 0;
94 }
95
96
97
98 void
99 Stem::set_stemend (Grob *me, Real se)
100 {
101   // todo: margins
102   Direction d = get_grob_direction (me);
103
104   if (d && d * head_positions (me)[get_grob_direction (me)] >= se * d)
105     me->warning (_ ("weird stem size, check for narrow beams"));
106
107   me->set_property ("stem-end-position", scm_from_double (se));
108 }
109
110 /* Note head that determines hshift for upstems
111    WARNING: triggers direction  */
112 Grob *
113 Stem::support_head (Grob *me)
114 {
115   extract_grob_set (me, "note-heads", heads);
116   if (heads.size () == 1)
117     return heads[0];
118
119   return first_head (me);
120 }
121
122 int
123 Stem::head_count (Grob *me)
124 {
125   return Pointer_group_interface::count (me, ly_symbol2scm ("note-heads"));
126 }
127
128 /* The note head which forms one end of the stem.
129    WARNING: triggers direction  */
130 Grob *
131 Stem::first_head (Grob *me)
132 {
133   Direction d = get_grob_direction (me);
134   if (d)
135     return extremal_heads (me)[-d];
136   return 0;
137 }
138
139 /* The note head opposite to the first head.  */
140 Grob *
141 Stem::last_head (Grob *me)
142 {
143   Direction d = get_grob_direction (me);
144   if (d)
145     return extremal_heads (me)[d];
146   return 0;
147 }
148
149 /*
150   START is part where stem reaches `last' head.
151
152   This function returns a drul with (bottom-head, top-head).
153 */
154 Drul_array<Grob *>
155 Stem::extremal_heads (Grob *me)
156 {
157   const int inf = INT_MAX;
158   Drul_array<int> extpos;
159   extpos[DOWN] = inf;
160   extpos[UP] = -inf;
161
162   Drul_array<Grob *> exthead (0, 0);
163   extract_grob_set (me, "note-heads", heads);
164
165   for (vsize i = heads.size (); i--;)
166     {
167       Grob *n = heads[i];
168       int p = Staff_symbol_referencer::get_rounded_position (n);
169
170       Direction d = LEFT;
171       do
172         {
173           if (d * p > d * extpos[d])
174             {
175               exthead[d] = n;
176               extpos[d] = p;
177             }
178         }
179       while (flip (&d) != DOWN);
180     }
181   return exthead;
182 }
183
184 /* The positions, in ascending order.  */
185 vector<int>
186 Stem::note_head_positions (Grob *me)
187 {
188   vector<int> ps;
189   extract_grob_set (me, "note-heads", heads);
190
191   for (vsize i = heads.size (); i--;)
192     {
193       Grob *n = heads[i];
194       int p = Staff_symbol_referencer::get_rounded_position (n);
195
196       ps.push_back (p);
197     }
198
199   vector_sort (ps, less<int> ());
200   return ps;
201 }
202
203 void
204 Stem::add_head (Grob *me, Grob *n)
205 {
206   n->set_object ("stem", me->self_scm ());
207
208   if (Note_head::has_interface (n))
209     Pointer_group_interface::add_grob (me, ly_symbol2scm ("note-heads"), n);
210   else if (Rest::has_interface (n))
211     Pointer_group_interface::add_grob (me, ly_symbol2scm ("rests"), n);
212 }
213
214 bool
215 Stem::is_invisible (Grob *me)
216 {
217   return !is_normal_stem (me)
218     && (robust_scm2double (me->get_property ("stemlet-length"),
219                            0.0) == 0.0);
220 }
221
222
223 bool
224 Stem::is_normal_stem (Grob *me)
225 {
226   return head_count (me) && scm_to_int (me->get_property ("duration-log")) >= 1;
227 }
228
229
230 MAKE_SCHEME_CALLBACK (Stem, pure_height, 3)
231 SCM
232 Stem::pure_height (SCM smob, SCM start, SCM end)
233 {
234   (void) start;
235   (void) end;
236
237   Grob *me = unsmob_grob (smob);
238   Interval iv;
239
240   if (!is_normal_stem (me))
241     return ly_interval2scm (iv);
242   
243   Real ss = Staff_symbol_referencer::staff_space (me);
244   Real len = scm_to_double (calc_length (smob)) * ss / 2;
245   Direction dir = get_grob_direction (me);
246
247   Interval hp = head_positions (me);
248   if (dir == UP)
249     iv = Interval (0, len);
250   else
251     iv = Interval (-len, 0);
252
253   if (!hp.is_empty ())
254     iv.translate (hp[dir] * ss / 2);
255
256   return ly_interval2scm (iv);
257 }
258
259 MAKE_SCHEME_CALLBACK (Stem, calc_stem_end_position, 1)
260 SCM
261 Stem::calc_stem_end_position (SCM smob)
262 {
263   Grob *me = unsmob_grob (smob);
264
265   if (!head_count (me))
266     return scm_from_double (0.0);
267
268   if (Grob *beam = get_beam (me))
269     {
270       (void) beam->get_property ("quantized-positions");
271       return me->get_property ("stem-end-position");
272     }
273   
274   Real ss = Staff_symbol_referencer::staff_space (me);
275   int durlog = duration_log (me);
276   vector<Real> a;
277
278   /* WARNING: IN HALF SPACES */
279   Real length = robust_scm2double (me->get_property ("length"), 7);
280
281   Direction dir = get_grob_direction (me);
282   Interval hp = head_positions (me);
283   Real stem_end = dir ? hp[dir] + dir * length : 0;
284
285   /* TODO: change name  to extend-stems to staff/center/'()  */
286   bool no_extend_b = to_boolean (me->get_property ("no-stem-extend"));
287   if (!no_extend_b && dir * stem_end < 0)
288     stem_end = 0.0;
289
290   
291   /* Make a little room if we have a upflag and there is a dot.
292      previous approach was to lengthen the stem. This is not
293      good typesetting practice.  */
294   if (!get_beam (me) && dir == UP
295       && durlog > 2)
296     {
297       Grob *closest_to_flag = extremal_heads (me)[dir];
298       Grob *dots = closest_to_flag
299         ? Rhythmic_head::get_dots (closest_to_flag) : 0;
300
301       if (dots)
302         {
303           Real dp = Staff_symbol_referencer::get_position (dots);
304           Interval flag_yext = flag (me).extent (Y_AXIS) * (2 / ss) + stem_end;
305
306           /* Very gory: add myself to the X-support of the parent,
307              which should be a dot-column. */
308           
309           if (flag_yext.distance (dp) < 0.5)
310             {
311               Grob *par = dots->get_parent (X_AXIS);
312
313               if (Dot_column::has_interface (par))
314                 {
315                   Side_position_interface::add_support (par, me);
316
317                   /* TODO: apply some better logic here. The flag is
318                      curved inwards, so this will typically be too
319                      much. */
320                 }
321             }
322         }
323     }
324
325   return scm_from_double (stem_end);
326 }
327
328 /* Length is in half-spaces (or: positions) here. */
329 MAKE_SCHEME_CALLBACK (Stem, calc_length, 1)
330 SCM
331 Stem::calc_length (SCM smob)
332 {
333   Grob *me = unsmob_grob (smob);
334   
335   SCM details = me->get_property ("details");
336   int durlog = duration_log (me);
337
338   Real ss = Staff_symbol_referencer::staff_space (me);
339   Real length = 7;
340   SCM s = scm_cdr (scm_assq (ly_symbol2scm ("lengths"), details));
341   if (scm_is_pair (s))
342     length = 2 * scm_to_double (robust_list_ref (durlog - 2, s));
343
344   Direction dir = get_grob_direction (me);
345
346   /* Stems in unnatural (forced) direction should be shortened,
347      according to [Roush & Gourlay] */
348   Interval hp = head_positions (me);
349   if (dir && dir * hp[dir] >= 0)
350     {
351       SCM sshorten = scm_cdr (scm_assq (ly_symbol2scm ("stem-shorten"), details));
352       SCM scm_shorten = scm_is_pair (sshorten)
353         ? robust_list_ref (max (duration_log (me) - 2, 0), sshorten) : SCM_EOL;
354       Real shorten = 2* robust_scm2double (scm_shorten, 0);
355
356       /* On boundary: shorten only half */
357       if (abs (head_positions (me)[dir]) <= 1)
358         shorten *= 0.5;
359
360       length -= shorten;
361     }
362
363   length *= robust_scm2double (me->get_property ("length-fraction"), 1.0);
364
365   /* Tremolo stuff.  */
366   Grob *t_flag = unsmob_grob (me->get_object ("tremolo-flag"));
367   if (t_flag && !unsmob_grob (me->get_object ("beam")))
368     {
369       /* Crude hack: add extra space if tremolo flag is there.
370
371       We can't do this for the beam, since we get into a loop
372       (Stem_tremolo::raw_stencil () looks at the beam.) --hwn  */
373
374       Real minlen = 1.0
375         + 2 * Stem_tremolo::vertical_length (t_flag) / ss;
376
377       /* We don't want to add the whole extent of the flag because the trem
378          and the flag can overlap partly. beam_translation gives a good
379          approximation */
380       if (durlog >= 3)
381         {
382           Real beam_trans = Stem_tremolo::get_beam_translation (t_flag);
383           /* the obvious choice is (durlog - 2) here, but we need a bit more space. */
384           minlen += 2 * (durlog - 1.5) * beam_trans;
385
386           /* up-stems need even a little more space to avoid collisions. This
387              needs to be in sync with the tremolo positioning code in
388              Stem_tremolo::print */
389           if (dir == UP)
390             minlen += beam_trans;
391         }
392       length = max (length, minlen + 1.0);
393     }
394   
395   return scm_from_double (length);
396 }
397 /* The log of the duration (Number of hooks on the flag minus two)  */
398 int
399 Stem::duration_log (Grob *me)
400 {
401   SCM s = me->get_property ("duration-log");
402   return (scm_is_number (s)) ? scm_to_int (s) : 2;
403 }
404
405 MAKE_SCHEME_CALLBACK(Stem, calc_positioning_done, 1);
406 SCM
407 Stem::calc_positioning_done (SCM smob)
408 {
409   Grob *me = unsmob_grob (smob);  
410   if (!head_count (me))
411     return SCM_BOOL_T;
412
413   extract_grob_set (me, "note-heads", ro_heads);
414   vector<Grob*> heads (ro_heads);
415   vector_sort (heads, position_less);
416   Direction dir = get_grob_direction (me);
417
418   if (dir < 0)
419     reverse (heads);
420
421   Real thick = thickness (me);
422
423   Grob *hed = support_head (me);
424   if (!dir)
425     {
426       programming_error ("Stem dir must be up or down.");
427       dir = UP;
428       set_grob_direction (me, dir);
429     }
430
431   bool is_harmonic_centered = false;
432   for (vsize i = 0; i < heads.size (); i++)
433     is_harmonic_centered = is_harmonic_centered 
434       || heads[i]->get_property ("style") == ly_symbol2scm ("harmonic");
435   is_harmonic_centered = is_harmonic_centered && is_invisible (me);
436
437   Real w = hed->extent (hed, X_AXIS)[dir];
438   for (vsize i = 0; i < heads.size (); i++)
439     {
440       Real amount = w - heads[i]->extent (heads[i], X_AXIS)[dir];
441
442       if (is_harmonic_centered)
443         amount =
444           hed->extent (hed, X_AXIS).linear_combination (CENTER)
445           - heads[i]->extent (heads[i], X_AXIS).linear_combination (CENTER);
446       
447       heads[i]->translate_axis (amount, X_AXIS);
448     }
449   bool parity = true;
450   Real lastpos = Real (Staff_symbol_referencer::get_position (heads[0]));
451   for (vsize i = 1; i < heads.size (); i++)
452     {
453       Real p = Staff_symbol_referencer::get_position (heads[i]);
454       Real dy = fabs (lastpos- p);
455
456       /*
457         dy should always be 0.5, 0.0, 1.0, but provide safety margin
458         for rounding errors.
459       */
460       if (dy < 1.1)
461         {
462           if (parity)
463             {
464               Real ell = heads[i]->extent (heads[i], X_AXIS).length ();
465
466               Direction d = get_grob_direction (me);
467               /*
468                 Reversed head should be shifted ell-thickness, but this
469                 looks too crowded, so we only shift ell-0.5*thickness.
470
471                 This leads to assymetry: Normal heads overlap the
472                 stem 100% whereas reversed heads only overlaps the
473                 stem 50%
474               */
475
476               Real reverse_overlap = 0.5;
477               heads[i]->translate_axis ((ell - thick * reverse_overlap) * d,
478                                         X_AXIS);
479
480               if (is_invisible (me))
481                 heads[i]->translate_axis (-thick * (2 - reverse_overlap) * d,
482                                           X_AXIS);
483
484               /* TODO:
485
486               For some cases we should kern some more: when the
487               distance between the next or prev note is too large, we'd
488               get large white gaps, eg.
489
490               |
491               X|
492               |X  <- kern this.
493               |
494               X
495
496               */
497             }
498           parity = !parity;
499         }
500       else
501         parity = true;
502
503       lastpos = int (p);
504     }
505
506   return SCM_BOOL_T;
507 }
508
509 MAKE_SCHEME_CALLBACK(Stem, calc_direction, 1);
510 SCM
511 Stem::calc_direction (SCM smob)
512 {
513   Grob *me = unsmob_grob (smob);
514   Direction dir = CENTER;
515   if (Grob *beam = unsmob_grob (me->get_object ("beam")))
516     {
517       SCM ignore_me = beam->get_property ("direction");
518       (void) ignore_me;
519       dir = get_grob_direction (me);
520     }
521   else
522     {
523       SCM dd = me->get_property ("default-direction");
524       dir = to_dir (dd);
525       if (!dir)
526         return me->get_property ("neutral-direction");
527     }
528   
529   return scm_from_int (dir);
530 }
531
532 MAKE_SCHEME_CALLBACK(Stem, calc_default_direction, 1);
533 SCM
534 Stem::calc_default_direction (SCM smob)
535 {
536   Grob *me = unsmob_grob (smob);
537
538   Direction dir = CENTER;
539   int staff_center = 0;
540   Interval hp = head_positions (me);
541   if (!hp.is_empty ())
542     {
543       int udistance = (int) (UP * hp[UP] - staff_center);
544       int ddistance = (int) (DOWN * hp[DOWN] - staff_center);
545       
546       dir = Direction (sign (ddistance - udistance));
547     }
548   
549   return scm_from_int (dir);
550 }
551
552
553 MAKE_SCHEME_CALLBACK (Stem, height, 1);
554 SCM
555 Stem::height (SCM smob)
556 {
557   Grob *me = unsmob_grob (smob);
558
559   Direction dir = get_grob_direction (me);
560   
561   /* Trigger callback.
562
563   UGH. Should be automatic
564   */
565   Grob *beam = get_beam (me);
566   if (beam)
567     {
568       /* trigger set-stem-lengths. */
569       beam->get_property ("quantized-positions");
570     }
571
572   /*
573     Can't get_stencil(), since that would cache stencils too early.
574     This causes problems with beams.
575    */
576   Stencil *stencil = unsmob_stencil (print (smob));
577   Interval iv = stencil ? stencil->extent (Y_AXIS) : Interval();
578   if (beam)
579     {
580       if (dir == CENTER)
581         {
582           programming_error ("no stem direction");
583           dir = UP;
584         }
585       iv[dir] += dir * Beam::get_thickness (beam) * 0.5;
586     }
587
588   return ly_interval2scm (iv);
589 }
590
591 Real
592 Stem::stem_end_position (Grob *me)
593 {
594   return robust_scm2double (me->get_property ("stem-end-position"), 0);
595 }
596
597 Stencil
598 Stem::flag (Grob *me)
599 {
600   int log = duration_log (me);
601   if (log < 3
602       || unsmob_grob (me->get_object ("beam")))
603     return Stencil ();
604
605   /*
606     TODO: maybe property stroke-style should take different values,
607     e.g. "" (i.e. no stroke), "single" and "double" (currently, it's
608     '() or "grace").  */
609   string flag_style;
610
611   SCM flag_style_scm = me->get_property ("flag-style");
612   if (scm_is_symbol (flag_style_scm))
613     flag_style = ly_symbol2string (flag_style_scm);
614
615   if (flag_style == "no-flag")
616     return Stencil ();
617
618   bool adjust = true;
619
620   string staffline_offs;
621   if (flag_style == "mensural")
622     /* Mensural notation: For notes on staff lines, use different
623        flags than for notes between staff lines.  The idea is that
624        flags are always vertically aligned with the staff lines,
625        regardless if the note head is on a staff line or between two
626        staff lines.  In other words, the inner end of a flag always
627        touches a staff line.
628     */
629     {
630       if (adjust)
631         {
632           int p = (int) (rint (stem_end_position (me)));
633           staffline_offs
634             = Staff_symbol_referencer::on_line (me, p) ? "0" : "1";
635         }
636       else
637         staffline_offs = "2";
638     }
639   else
640     staffline_offs = "";
641
642   char dir = (get_grob_direction (me) == UP) ? 'u' : 'd';
643   string font_char = flag_style
644     + to_string (dir) + staffline_offs + to_string (log);
645   Font_metric *fm = Font_interface::get_default_font (me);
646   Stencil flag = fm->find_by_name ("flags." + font_char);
647   if (flag.is_empty ())
648     me->warning (_f ("flag `%s' not found", font_char));
649
650   SCM stroke_style_scm = me->get_property ("stroke-style");
651   if (scm_is_string (stroke_style_scm))
652     {
653       string stroke_style = ly_scm2string (stroke_style_scm);
654       if (!stroke_style.empty ())
655         {
656           string font_char = to_string (dir) + stroke_style;
657           Stencil stroke = fm->find_by_name ("flags." + font_char);
658           if (stroke.is_empty ())
659             me->warning (_f ("flag stroke `%s' not found", font_char));
660           else
661             flag.add_stencil (stroke);
662         }
663     }
664
665   return flag;
666 }
667
668 MAKE_SCHEME_CALLBACK (Stem, width, 1);
669 SCM
670 Stem::width (SCM e)
671 {
672   Grob *me = unsmob_grob (e);
673
674   Interval r;
675
676   if (is_invisible (me))
677     r.set_empty ();
678   else if (unsmob_grob (me->get_object ("beam"))
679            || abs (duration_log (me)) <= 2)
680     {
681       r = Interval (-1, 1);
682       r *= thickness (me) / 2;
683     }
684   else
685     {
686       r = Interval (-1, 1) * thickness (me) * 0.5;
687       r.unite (flag (me).extent (X_AXIS));
688     }
689   return ly_interval2scm (r);
690 }
691
692 Real
693 Stem::thickness (Grob *me)
694 {
695   return scm_to_double (me->get_property ("thickness"))
696     * Staff_symbol_referencer::line_thickness (me);
697 }
698
699 MAKE_SCHEME_CALLBACK (Stem, print, 1);
700 SCM
701 Stem::print (SCM smob)
702 {
703   Grob *me = unsmob_grob (smob);
704   Grob *beam = get_beam (me);
705     
706   Stencil mol;
707   Direction d = get_grob_direction (me);
708
709   Real stemlet_length = robust_scm2double (me->get_property ("stemlet-length"),
710                                            0.0);
711   bool stemlet = stemlet_length > 0.0;
712
713   /* TODO: make the stem start a direction ?
714      This is required to avoid stems passing in tablature chords.  */
715   Grob *lh
716     = to_boolean (me->get_property ("avoid-note-head"))
717     ? last_head (me)
718     : first_head (me);
719
720   if (!lh && !stemlet)
721     return SCM_EOL;
722
723   if (!lh && stemlet && !beam)
724     return SCM_EOL;
725
726   if (is_invisible (me))
727     return SCM_EOL;
728
729   Real y2 = robust_scm2double (me->get_property ("stem-end-position"), 0.0);
730   Real y1 = y2;
731   Real half_space = Staff_symbol_referencer::staff_space (me) * 0.5;
732
733   if (lh)
734     y2 = Staff_symbol_referencer::get_position (lh);
735   else if (stemlet)
736     {
737       Real beam_translation = Beam::get_beam_translation (beam);
738       Real beam_thickness = Beam::get_thickness (beam);
739       int beam_count = beam_multiplicity (me).length () + 1;
740
741       y2 -= d
742         * (0.5 * beam_thickness
743            + beam_translation * max (0, (beam_count - 1))
744            + stemlet_length) / half_space;
745     }
746
747   Interval stem_y (min (y1, y2), max (y2, y1));
748
749   if (Grob *head = support_head (me))
750     {
751       /*
752         must not take ledgers into account.
753       */
754       Interval head_height = head->extent (head, Y_AXIS);
755       Real y_attach = Note_head::stem_attachment_coordinate (head, Y_AXIS);
756
757       y_attach = head_height.linear_combination (y_attach);
758       stem_y[Direction (-d)] += d * y_attach / half_space;
759     }
760
761   // URG
762   Real stem_width = thickness (me);
763   Real blot
764     = me->layout ()->get_dimension (ly_symbol2scm ("blot-diameter"));
765
766   Box b = Box (Interval (-stem_width / 2, stem_width / 2),
767                Interval (stem_y[DOWN] * half_space, stem_y[UP] * half_space));
768
769   Stencil ss = Lookup::round_filled_box (b, blot);
770   mol.add_stencil (ss);
771
772   mol.add_stencil (get_translated_flag (me));
773
774   return mol.smobbed_copy ();
775 }
776
777 Stencil
778 Stem::get_translated_flag (Grob *me)
779 {
780   Stencil fl = flag (me);
781   if (!fl.is_empty ())
782     {
783       Direction d = get_grob_direction (me);
784       Real blot
785         = me->layout ()->get_dimension (ly_symbol2scm ("blot-diameter"));
786       Real stem_width = thickness (me);
787       Real half_space = Staff_symbol_referencer::staff_space (me) * 0.5;
788       Real y2 = robust_scm2double (me->get_property ("stem-end-position"), 0.0);
789       fl.translate_axis (y2 * half_space - d * blot / 2, Y_AXIS);
790       fl.translate_axis (stem_width / 2, X_AXIS);
791     }
792   return fl;
793 }
794
795
796 /*
797   move the stem to right of the notehead if it is up.
798 */
799 MAKE_SCHEME_CALLBACK (Stem, offset_callback, 1);
800 SCM
801 Stem::offset_callback (SCM smob)
802 {
803   Grob *me = unsmob_grob (smob);
804
805   extract_grob_set (me, "rests", rests);
806   if (rests.size ())
807     {
808       Grob *rest = rests.back ();
809       Real r = rest->extent (rest, X_AXIS).center ();
810       return scm_from_double (r);
811     }
812
813   
814   if (Grob *f = first_head (me))
815     {
816       Interval head_wid = f->extent (f, X_AXIS);
817       Real attach = 0.0;
818
819       if (is_invisible (me))
820         attach = 0.0;
821       else
822         attach = Note_head::stem_attachment_coordinate (f, X_AXIS);
823
824       Direction d = get_grob_direction (me);
825       Real real_attach = head_wid.linear_combination (d * attach);
826       Real r = real_attach;
827
828       /* If not centered: correct for stem thickness.  */
829       if (attach)
830         {
831           Real rule_thick = thickness (me);
832           r += -d * rule_thick * 0.5;
833         }
834       return scm_from_double (r);
835     }
836
837   programming_error ("Weird stem.");
838   return scm_from_double (0.0);
839 }
840
841 Spanner *
842 Stem::get_beam (Grob *me)
843 {
844   SCM b = me->get_object ("beam");
845   return dynamic_cast<Spanner *> (unsmob_grob (b));
846 }
847
848 Stem_info
849 Stem::get_stem_info (Grob *me)
850 {
851   Stem_info si;
852   si.dir_ = get_grob_direction (me);
853   
854   SCM scm_info = me->get_property ("stem-info");
855   si.ideal_y_ = scm_to_double (scm_car (scm_info));
856   si.shortest_y_ = scm_to_double (scm_cadr (scm_info));
857   return si;
858 }
859
860 MAKE_SCHEME_CALLBACK(Stem, calc_stem_info, 1);
861 SCM
862 Stem::calc_stem_info (SCM smob)
863 {
864   Grob *me = unsmob_grob (smob);
865   Direction my_dir = get_grob_direction (me);
866
867   if (!my_dir)
868     {
869       programming_error ("no stem dir set");
870       my_dir = UP;
871     }
872
873   Real staff_space = Staff_symbol_referencer::staff_space (me);
874   Grob *beam = get_beam (me);
875
876   if (beam)
877     {
878       (void) beam->get_property ("beaming");
879     }
880   
881   Real beam_translation = Beam::get_beam_translation (beam);
882   Real beam_thickness = Beam::get_thickness (beam);
883   int beam_count = Beam::get_direction_beam_count (beam, my_dir);
884   Real length_fraction
885     = robust_scm2double (me->get_property ("length-fraction"), 1.0);
886
887   /* Simple standard stem length */
888   SCM details = me->get_property ("details");
889   SCM lengths = scm_cdr (scm_assq (ly_symbol2scm ("beamed-lengths"), details));
890   
891   Real ideal_length
892     = scm_to_double (robust_list_ref (beam_count - 1, lengths))
893     * staff_space
894     * length_fraction
895     
896     /* stem only extends to center of beam
897      */
898     - 0.5 * beam_thickness;
899
900   /* Condition: sane minimum free stem length (chord to beams) */
901   lengths = scm_cdr (scm_assq (ly_symbol2scm ("beamed-minimum-free-lengths"), details));
902
903   Real ideal_minimum_free
904     = scm_to_double (robust_list_ref (beam_count - 1, lengths))
905     * staff_space
906     * length_fraction;
907
908   Real height_of_my_trem = 0.0;
909   Grob *trem = unsmob_grob (me->get_object ("tremolo-flag"));
910   if (trem)
911       height_of_my_trem = trem->extent (trem, Y_AXIS).length ()
912         /* hack a bit of space around the trem. */
913         + beam_translation;
914
915   /* UGH
916      It seems that also for ideal minimum length, we must use
917      the maximum beam count (for this direction):
918
919      \score{ \notes\relative c''{ [a8 a32] }}
920
921      must be horizontal. */
922   Real height_of_my_beams = beam_thickness
923     + (beam_count - 1) * beam_translation;
924
925   Real ideal_minimum_length = ideal_minimum_free
926     + height_of_my_beams
927     + height_of_my_trem
928     /* stem only extends to center of beam */
929     - 0.5 * beam_thickness;
930
931   ideal_length = max (ideal_length, ideal_minimum_length);
932
933   /* Convert to Y position, calculate for dir == UP */
934   Real note_start
935     =     /* staff positions */
936     head_positions (me)[my_dir] * 0.5
937     * my_dir * staff_space;
938   Real ideal_y = note_start + ideal_length;
939
940   /* Conditions for Y position */
941
942   /* Lowest beam of (UP) beam must never be lower than second staffline
943
944   Reference?
945
946   Although this (additional) rule is probably correct,
947   I expect that highest beam (UP) should also never be lower
948   than middle staffline, just as normal stems.
949
950   Reference?
951
952   Obviously not for grace beams.
953
954   Also, not for knees.  Seems to be a good thing. */
955   bool no_extend_b = to_boolean (me->get_property ("no-stem-extend"));
956   bool is_knee = to_boolean (beam->get_property ("knee"));
957   if (!no_extend_b && !is_knee)
958     {
959       /* Highest beam of (UP) beam must never be lower than middle
960          staffline */
961       ideal_y = max (ideal_y, 0.0);
962       /* Lowest beam of (UP) beam must never be lower than second staffline */
963       ideal_y = max (ideal_y, (-staff_space
964                                - beam_thickness + height_of_my_beams));
965     }
966
967   ideal_y -= robust_scm2double (beam->get_property ("shorten"), 0);
968
969   SCM bemfl = scm_cdr (scm_assq (ly_symbol2scm ("beamed-extreme-minimum-free-lengths"),
970                                  details));
971   
972   Real minimum_free
973     = scm_to_double (robust_list_ref (beam_count - 1, bemfl))
974     * staff_space
975     * length_fraction;
976
977   Real minimum_length = max (minimum_free, height_of_my_trem)
978     + height_of_my_beams
979     /* stem only extends to center of beam */
980     - 0.5 * beam_thickness;
981
982   ideal_y *= my_dir;
983   Real minimum_y = note_start + minimum_length;
984   Real shortest_y = minimum_y * my_dir;
985
986   return scm_list_2 (scm_from_double (ideal_y),
987                      scm_from_double (shortest_y));
988 }
989
990 Slice
991 Stem::beam_multiplicity (Grob *stem)
992 {
993   SCM beaming = stem->get_property ("beaming");
994   Slice le = int_list_to_slice (scm_car (beaming));
995   Slice ri = int_list_to_slice (scm_cdr (beaming));
996   le.unite (ri);
997   return le;
998 }
999
1000 /* FIXME:  Too many properties  */
1001 ADD_INTERFACE (Stem,
1002                "The stem represent the graphical stem.  "
1003                "In addition, it internally connects note heads, beams and"
1004                "tremolos. "
1005                "Rests and whole notes have invisible stems."
1006
1007                "\n\nThe following properties may be set in the details list." 
1008                "@table @code\n"
1009                "@item  beamed-lengths \n"
1010                "list of stem lengths given beam multiplicity. \n"
1011                "@item beamed-minimum-free-lengths \n"
1012                "list of normal minimum free stem lengths (chord to beams) given beam multiplicity.\n"
1013                "@item beamed-extreme-minimum-free-lengths\n"
1014                "list of extreme minimum free stem lengths (chord to beams) given beam multiplicity.\n"
1015                "@item lengths\n"
1016                "Default stem lengths. The list gives a length for each flag-count.\n"
1017                "@item stem-shorten\n"
1018                "How much a stem in a forced direction "
1019                "should be shortened. The list gives an amount depending on the number "
1020                "of flags/beams."
1021                "@end table\n"
1022                ,
1023
1024                /* properties */
1025                "avoid-note-head "
1026                "beam "
1027                "beaming "
1028                "default-direction "
1029                "details "
1030                "direction "
1031                "duration-log "
1032                "flag-style "
1033                "french-beaming "
1034                "length "
1035                "length-fraction "
1036                "max-beam-connect "
1037                "neutral-direction "
1038                "no-stem-extend "
1039                "note-heads "
1040                "positioning-done "
1041                "rests "
1042                "stem-end-position "
1043                "stem-info "
1044                "stemlet-length "
1045                "stroke-style "
1046                "thickness "
1047                "tremolo-flag "
1048                );
1049
1050 /****************************************************************/
1051
1052 Stem_info::Stem_info ()
1053 {
1054   ideal_y_ = shortest_y_ = 0;
1055   dir_ = CENTER;
1056 }
1057
1058 void
1059 Stem_info::scale (Real x)
1060 {
1061   ideal_y_ *= x;
1062   shortest_y_ *= x;
1063 }