]> git.donarmstrong.com Git - lilypond.git/blob - lily/stem.cc
Merge branch 'master' into lilypond/translation
[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, print, 1);
737 SCM
738 Stem::print (SCM smob)
739 {
740   Grob *me = unsmob_grob (smob);
741   Grob *beam = get_beam (me);
742     
743   Stencil mol;
744   Direction d = get_grob_direction (me);
745
746   Real stemlet_length = robust_scm2double (me->get_property ("stemlet-length"),
747                                            0.0);
748   bool stemlet = stemlet_length > 0.0;
749
750   /* TODO: make the stem start a direction ?
751      This is required to avoid stems passing in tablature chords.  */
752   Grob *lh
753     = to_boolean (me->get_property ("avoid-note-head"))
754     ? last_head (me)
755     : first_head (me);
756
757   if (!lh && !stemlet)
758     return SCM_EOL;
759
760   if (!lh && stemlet && !beam)
761     return SCM_EOL;
762
763   if (lh && robust_scm2int (lh->get_property ("duration-log"), 0) < 1) 
764     return SCM_EOL;
765
766   if (is_invisible (me))
767     return SCM_EOL;
768
769   Real y2 = robust_scm2double (me->get_property ("stem-end-position"), 0.0);
770   Real y1 = y2;
771   Real half_space = Staff_symbol_referencer::staff_space (me) * 0.5;
772
773   if (lh)
774     y2 = Staff_symbol_referencer::get_position (lh);
775   else if (stemlet)
776     {
777       Real beam_translation = Beam::get_beam_translation (beam);
778       Real beam_thickness = Beam::get_beam_thickness (beam);
779       int beam_count = beam_multiplicity (me).length () + 1;
780
781       y2 -= d
782         * (0.5 * beam_thickness
783            + beam_translation * max (0, (beam_count - 1))
784            + stemlet_length) / half_space;
785     }
786
787   Interval stem_y (min (y1, y2), max (y2, y1));
788
789   if (Grob *head = support_head (me))
790     {
791       /*
792         must not take ledgers into account.
793       */
794       Interval head_height = head->extent (head, Y_AXIS);
795       Real y_attach = Note_head::stem_attachment_coordinate (head, Y_AXIS);
796
797       y_attach = head_height.linear_combination (y_attach);
798       stem_y[Direction (-d)] += d * y_attach / half_space;
799     }
800
801   // URG
802   Real stem_width = thickness (me);
803   Real blot
804     = me->layout ()->get_dimension (ly_symbol2scm ("blot-diameter"));
805
806   Box b = Box (Interval (-stem_width / 2, stem_width / 2),
807                Interval (stem_y[DOWN] * half_space, stem_y[UP] * half_space));
808
809   Stencil ss = Lookup::round_filled_box (b, blot);
810   mol.add_stencil (ss);
811
812   mol.add_stencil (get_translated_flag (me));
813
814   return mol.smobbed_copy ();
815 }
816
817 Stencil
818 Stem::get_translated_flag (Grob *me)
819 {
820   Stencil fl = flag (me);
821   if (!fl.is_empty ())
822     {
823       Direction d = get_grob_direction (me);
824       Real blot
825         = me->layout ()->get_dimension (ly_symbol2scm ("blot-diameter"));
826       Real stem_width = thickness (me);
827       Real half_space = Staff_symbol_referencer::staff_space (me) * 0.5;
828       Real y2 = robust_scm2double (me->get_property ("stem-end-position"), 0.0);
829       fl.translate_axis (y2 * half_space - d * blot / 2, Y_AXIS);
830       fl.translate_axis (stem_width / 2, X_AXIS);
831     }
832   return fl;
833 }
834
835
836 /*
837   move the stem to right of the notehead if it is up.
838 */
839 MAKE_SCHEME_CALLBACK (Stem, offset_callback, 1);
840 SCM
841 Stem::offset_callback (SCM smob)
842 {
843   Grob *me = unsmob_grob (smob);
844
845   extract_grob_set (me, "rests", rests);
846   if (rests.size ())
847     {
848       Grob *rest = rests.back ();
849       Real r = rest->extent (rest, X_AXIS).center ();
850       return scm_from_double (r);
851     }
852
853   
854   if (Grob *f = first_head (me))
855     {
856       Interval head_wid = f->extent (f, X_AXIS);
857       Real attach = 0.0;
858
859       if (is_invisible (me))
860         attach = 0.0;
861       else
862         attach = Note_head::stem_attachment_coordinate (f, X_AXIS);
863
864       Direction d = get_grob_direction (me);
865       Real real_attach = head_wid.linear_combination (d * attach);
866       Real r = real_attach;
867
868       /* If not centered: correct for stem thickness.  */
869       if (attach)
870         {
871           Real rule_thick = thickness (me);
872           r += -d * rule_thick * 0.5;
873         }
874       return scm_from_double (r);
875     }
876
877   programming_error ("Weird stem.");
878   return scm_from_double (0.0);
879 }
880
881 Spanner *
882 Stem::get_beam (Grob *me)
883 {
884   SCM b = me->get_object ("beam");
885   return dynamic_cast<Spanner *> (unsmob_grob (b));
886 }
887
888 Stem_info
889 Stem::get_stem_info (Grob *me)
890 {
891   Stem_info si;
892   si.dir_ = get_grob_direction (me);
893   
894   SCM scm_info = me->get_property ("stem-info");
895   si.ideal_y_ = scm_to_double (scm_car (scm_info));
896   si.shortest_y_ = scm_to_double (scm_cadr (scm_info));
897   return si;
898 }
899
900 MAKE_SCHEME_CALLBACK (Stem, calc_stem_info, 1);
901 SCM
902 Stem::calc_stem_info (SCM smob)
903 {
904   Grob *me = unsmob_grob (smob);
905   Direction my_dir = get_grob_direction (me);
906
907   if (!my_dir)
908     {
909       programming_error ("no stem dir set");
910       my_dir = UP;
911     }
912
913   Real staff_space = Staff_symbol_referencer::staff_space (me);
914   Grob *beam = get_beam (me);
915
916   if (beam)
917     {
918       (void) beam->get_property ("beaming");
919     }
920   
921   Real beam_translation = Beam::get_beam_translation (beam);
922   Real beam_thickness = Beam::get_beam_thickness (beam);
923   int beam_count = Beam::get_direction_beam_count (beam, my_dir);
924   Real length_fraction
925     = robust_scm2double (me->get_property ("length-fraction"), 1.0);
926
927   /* Simple standard stem length */
928   SCM details = me->get_property ("details");
929   SCM lengths = ly_assoc_get (ly_symbol2scm ("beamed-lengths"), details, SCM_EOL);
930   
931   Real ideal_length
932     = (scm_is_pair (lengths)
933        ? (scm_to_double (robust_list_ref (beam_count - 1, lengths))
934           * staff_space
935           * length_fraction
936           /*
937             stem only extends to center of beam
938           */
939           - 0.5 * beam_thickness)
940        : 0.0);
941
942   /* Condition: sane minimum free stem length (chord to beams) */
943   lengths = ly_assoc_get (ly_symbol2scm ("beamed-minimum-free-lengths"),
944                           details, SCM_EOL);
945
946   Real ideal_minimum_free
947     = (scm_is_pair (lengths)
948        ? (scm_to_double (robust_list_ref (beam_count - 1, lengths))
949           * staff_space
950           * length_fraction)
951        : 0.0);
952
953   Real height_of_my_trem = 0.0;
954   Grob *trem = unsmob_grob (me->get_object ("tremolo-flag"));
955   if (trem)
956     {
957       height_of_my_trem
958         = Stem_tremolo::vertical_length (trem)
959         /* hack a bit of space around the trem. */
960         + beam_translation;
961     }
962
963   
964   /* UGH
965      It seems that also for ideal minimum length, we must use
966      the maximum beam count (for this direction):
967
968      \score { \relative c'' { a8[ a32] } }
969
970      must be horizontal. */
971   Real height_of_my_beams = beam_thickness
972     + (beam_count - 1) * beam_translation;
973
974   Real ideal_minimum_length = ideal_minimum_free
975     + height_of_my_beams
976     + height_of_my_trem
977     /* stem only extends to center of beam */
978     - 0.5 * beam_thickness;
979
980   ideal_length = max (ideal_length, ideal_minimum_length);
981
982   /* Convert to Y position, calculate for dir == UP */
983   Real note_start
984     =     /* staff positions */
985     head_positions (me)[my_dir] * 0.5
986     * my_dir * staff_space;
987   Real ideal_y = note_start + ideal_length;
988
989   /* Conditions for Y position */
990
991   /* Lowest beam of (UP) beam must never be lower than second staffline
992
993   Reference?
994
995   Although this (additional) rule is probably correct,
996   I expect that highest beam (UP) should also never be lower
997   than middle staffline, just as normal stems.
998
999   Reference?
1000
1001   Obviously not for grace beams.
1002
1003   Also, not for knees.  Seems to be a good thing. */
1004   bool no_extend = to_boolean (me->get_property ("no-stem-extend"));
1005   bool is_knee = to_boolean (beam->get_property ("knee"));
1006   if (!no_extend && !is_knee)
1007     {
1008       /* Highest beam of (UP) beam must never be lower than middle
1009          staffline */
1010       ideal_y = max (ideal_y, 0.0);
1011       /* Lowest beam of (UP) beam must never be lower than second staffline */
1012       ideal_y = max (ideal_y, (-staff_space
1013                                - beam_thickness + height_of_my_beams));
1014     }
1015
1016   ideal_y -= robust_scm2double (beam->get_property ("shorten"), 0);
1017
1018   SCM bemfl = ly_assoc_get (ly_symbol2scm ("beamed-extreme-minimum-free-lengths"),
1019                             details, SCM_EOL);
1020   
1021   Real minimum_free
1022     = (scm_is_pair (bemfl)
1023        ? (scm_to_double (robust_list_ref (beam_count - 1, bemfl))
1024           * staff_space
1025           * length_fraction)
1026        : 0.0);
1027
1028   Real minimum_length = max (minimum_free, height_of_my_trem)
1029     + height_of_my_beams
1030     /* stem only extends to center of beam */
1031     - 0.5 * beam_thickness;
1032
1033   ideal_y *= my_dir;
1034   Real minimum_y = note_start + minimum_length;
1035   Real shortest_y = minimum_y * my_dir;
1036
1037   return scm_list_2 (scm_from_double (ideal_y),
1038                      scm_from_double (shortest_y));
1039 }
1040
1041 Slice
1042 Stem::beam_multiplicity (Grob *stem)
1043 {
1044   SCM beaming = stem->get_property ("beaming");
1045   Slice le = int_list_to_slice (scm_car (beaming));
1046   Slice ri = int_list_to_slice (scm_cdr (beaming));
1047   le.unite (ri);
1048   return le;
1049 }
1050
1051 bool
1052 Stem::is_cross_staff (Grob *stem)
1053 {
1054   Grob *beam = unsmob_grob (stem->get_object ("beam"));
1055   return beam && Beam::is_cross_staff (beam);
1056 }
1057
1058 MAKE_SCHEME_CALLBACK (Stem, calc_cross_staff, 1)
1059 SCM
1060 Stem::calc_cross_staff (SCM smob)
1061 {
1062   return scm_from_bool (is_cross_staff (unsmob_grob (smob)));
1063 }
1064
1065 /* FIXME:  Too many properties  */
1066 ADD_INTERFACE (Stem,
1067                "The stem represents the graphical stem.  In addition, it"
1068                " internally connects note heads, beams, and tremolos.  Rests"
1069                " and whole notes have invisible stems.\n"
1070                "\n"
1071                "The following properties may be set in the @code{details}"
1072                " list.\n"
1073                "\n"
1074                "@table @code\n"
1075                "@item beamed-lengths\n"
1076                "List of stem lengths given beam multiplicity.\n"
1077                "@item beamed-minimum-free-lengths\n"
1078                "List of normal minimum free stem lengths (chord to beams)"
1079                " given beam multiplicity.\n"
1080                "@item beamed-extreme-minimum-free-lengths\n"
1081                "List of extreme minimum free stem lengths (chord to beams)"
1082                " given beam multiplicity.\n"
1083                "@item lengths\n"
1084                "Default stem lengths.  The list gives a length for each"
1085                " flag count.\n"
1086                "@item stem-shorten\n"
1087                "How much a stem in a forced direction should be shortened."
1088                "  The list gives an amount depending on the number of flags"
1089                " and beams.\n"
1090                "@end table\n",
1091
1092                /* properties */
1093                "avoid-note-head "
1094                "beam "
1095                "beaming "
1096                "beamlet-default-length "
1097                "beamlet-max-length-proportion "
1098                "default-direction "
1099                "details "
1100                "direction "
1101                "duration-log "
1102                "flag "
1103                "flag-style "
1104                "french-beaming "
1105                "length "
1106                "length-fraction "
1107                "max-beam-connect "
1108                "neutral-direction "
1109                "no-stem-extend "
1110                "note-heads "
1111                "positioning-done "
1112                "rests "
1113                "stem-end-position "
1114                "stem-info "
1115                "stemlet-length "
1116                "stroke-style "
1117                "thickness "
1118                "tremolo-flag "
1119                );
1120
1121 /****************************************************************/
1122
1123 Stem_info::Stem_info ()
1124 {
1125   ideal_y_ = shortest_y_ = 0;
1126   dir_ = CENTER;
1127 }
1128
1129 void
1130 Stem_info::scale (Real x)
1131 {
1132   ideal_y_ *= x;
1133   shortest_y_ *= x;
1134 }