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