2 stem.cc -- implement Stem
4 source file of the GNU LilyPond music typesetter
6 (c) 1996--2007 Han-Wen Nienhuys <hanwen@xs4all.nl>
7 Jan Nieuwenhuizen <janneke@gnu.org>
9 TODO: This is way too hairy
13 Stem-end, chord-start, etc. is all confusing naming.
19 #include <cmath> // rint
23 #include "directional-element-interface.hh"
24 #include "dot-column.hh"
25 #include "font-interface.hh"
26 #include "international.hh"
29 #include "note-head.hh"
30 #include "output-def.hh"
31 #include "paper-column.hh"
32 #include "pointer-group-interface.hh"
34 #include "rhythmic-head.hh"
35 #include "side-position-interface.hh"
36 #include "staff-symbol-referencer.hh"
37 #include "stem-tremolo.hh"
41 Stem::set_beaming (Grob *me, int beam_count, Direction d)
43 SCM pair = me->get_property ("beaming");
45 if (!scm_is_pair (pair))
47 pair = scm_cons (SCM_EOL, SCM_EOL);
48 me->set_property ("beaming", pair);
51 SCM lst = index_get_cell (pair, d);
53 for (int i = 0; i < beam_count; i++)
54 lst = scm_cons (scm_from_int (i), lst);
58 index_set_cell (pair, d, lst);
62 Stem::get_beaming (Grob *me, Direction d)
64 SCM pair = me->get_property ("beaming");
65 if (!scm_is_pair (pair))
68 SCM lst = index_get_cell (pair, d);
70 int len = scm_ilength (lst);
75 Stem::head_positions (Grob *me)
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]));
87 Stem::chord_start_y (Grob *me)
89 Interval hp = head_positions (me);
91 return hp[get_grob_direction (me)] * Staff_symbol_referencer::staff_space (me)
99 Stem::set_stemend (Grob *me, Real se)
102 Direction d = get_grob_direction (me);
104 if (d && d * head_positions (me)[get_grob_direction (me)] >= se * d)
105 me->warning (_ ("weird stem size, check for narrow beams"));
107 me->set_property ("stem-end-position", scm_from_double (se));
110 /* Note head that determines hshift for upstems
111 WARNING: triggers direction */
113 Stem::support_head (Grob *me)
115 extract_grob_set (me, "note-heads", heads);
116 if (heads.size () == 1)
119 return first_head (me);
123 Stem::head_count (Grob *me)
125 return Pointer_group_interface::count (me, ly_symbol2scm ("note-heads"));
128 /* The note head which forms one end of the stem.
129 WARNING: triggers direction */
131 Stem::first_head (Grob *me)
133 Direction d = get_grob_direction (me);
135 return extremal_heads (me)[-d];
139 /* The note head opposite to the first head. */
141 Stem::last_head (Grob *me)
143 Direction d = get_grob_direction (me);
145 return extremal_heads (me)[d];
150 START is part where stem reaches `last' head.
152 This function returns a drul with (bottom-head, top-head).
155 Stem::extremal_heads (Grob *me)
157 const int inf = INT_MAX;
158 Drul_array<int> extpos;
162 Drul_array<Grob *> exthead (0, 0);
163 extract_grob_set (me, "note-heads", heads);
165 for (vsize i = heads.size (); i--;)
168 int p = Staff_symbol_referencer::get_rounded_position (n);
173 if (d * p > d * extpos[d])
179 while (flip (&d) != DOWN);
184 /* The positions, in ascending order. */
186 Stem::note_head_positions (Grob *me)
189 extract_grob_set (me, "note-heads", heads);
191 for (vsize i = heads.size (); i--;)
194 int p = Staff_symbol_referencer::get_rounded_position (n);
199 vector_sort (ps, less<int> ());
204 Stem::add_head (Grob *me, Grob *n)
206 n->set_object ("stem", me->self_scm ());
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);
215 Stem::is_invisible (Grob *me)
217 return !is_normal_stem (me)
218 && (robust_scm2double (me->get_property ("stemlet-length"),
224 Stem::is_normal_stem (Grob *me)
226 return head_count (me) && scm_to_int (me->get_property ("duration-log")) >= 1;
230 MAKE_SCHEME_CALLBACK (Stem, pure_height, 3)
232 Stem::pure_height (SCM smob, SCM start, SCM end)
237 Grob *me = unsmob_grob (smob);
240 if (!is_normal_stem (me))
241 return ly_interval2scm (iv);
243 Real ss = Staff_symbol_referencer::staff_space (me);
244 Real rad = Staff_symbol_referencer::staff_radius (me);
246 if (!to_boolean (me->get_property ("cross-staff")))
248 Real len = scm_to_double (calc_length (smob)) * ss / 2;
249 Direction dir = get_grob_direction (me);
251 Interval hp = head_positions (me);
253 iv = Interval (0, len);
255 iv = Interval (-len, 0);
258 iv.translate (hp[dir] * ss / 2);
260 /* extend the stem (away from the head) to cover the staff */
262 iv[UP] = max (iv[UP], rad * ss);
264 iv[DOWN] = min (iv[DOWN], -rad * ss);
267 iv = Interval (-rad * ss, rad * ss);
269 return ly_interval2scm (iv);
272 MAKE_SCHEME_CALLBACK (Stem, calc_stem_end_position, 1)
274 Stem::calc_stem_end_position (SCM smob)
276 Grob *me = unsmob_grob (smob);
278 if (!head_count (me))
279 return scm_from_double (0.0);
281 if (Grob *beam = get_beam (me))
283 (void) beam->get_property ("quantized-positions");
284 return me->get_property ("stem-end-position");
289 /* WARNING: IN HALF SPACES */
290 Real length = robust_scm2double (me->get_property ("length"), 7);
292 Direction dir = get_grob_direction (me);
293 Interval hp = head_positions (me);
294 Real stem_end = dir ? hp[dir] + dir * length : 0;
296 /* TODO: change name to extend-stems to staff/center/'() */
297 bool no_extend = to_boolean (me->get_property ("no-stem-extend"));
298 if (!no_extend && dir * stem_end < 0)
301 return scm_from_double (stem_end);
304 /* Length is in half-spaces (or: positions) here. */
305 MAKE_SCHEME_CALLBACK (Stem, calc_length, 1)
307 Stem::calc_length (SCM smob)
309 Grob *me = unsmob_grob (smob);
311 SCM details = me->get_property ("details");
312 int durlog = duration_log (me);
314 Real ss = Staff_symbol_referencer::staff_space (me);
316 SCM s = ly_assoc_get (ly_symbol2scm ("lengths"), details, SCM_EOL);
318 length = 2 * scm_to_double (robust_list_ref (durlog - 2, s));
320 Direction dir = get_grob_direction (me);
322 /* Stems in unnatural (forced) direction should be shortened,
323 according to [Roush & Gourlay] */
324 Interval hp = head_positions (me);
325 if (dir && dir * hp[dir] >= 0)
327 SCM sshorten = ly_assoc_get (ly_symbol2scm ("stem-shorten"), details, SCM_EOL);
328 SCM scm_shorten = scm_is_pair (sshorten)
329 ? robust_list_ref (max (duration_log (me) - 2, 0), sshorten) : SCM_EOL;
330 Real shorten = 2* robust_scm2double (scm_shorten, 0);
332 /* On boundary: shorten only half */
333 if (abs (head_positions (me)[dir]) <= 1)
339 length *= robust_scm2double (me->get_property ("length-fraction"), 1.0);
342 Grob *t_flag = unsmob_grob (me->get_object ("tremolo-flag"));
343 if (t_flag && !unsmob_grob (me->get_object ("beam")))
345 /* Crude hack: add extra space if tremolo flag is there.
347 We can't do this for the beam, since we get into a loop
348 (Stem_tremolo::raw_stencil () looks at the beam.) --hwn */
351 + 2 * Stem_tremolo::vertical_length (t_flag) / ss;
353 /* We don't want to add the whole extent of the flag because the trem
354 and the flag can overlap partly. beam_translation gives a good
358 Real beam_trans = Stem_tremolo::get_beam_translation (t_flag);
359 /* the obvious choice is (durlog - 2) here, but we need a bit more space. */
360 minlen += 2 * (durlog - 1.5) * beam_trans;
362 /* up-stems need even a little more space to avoid collisions. This
363 needs to be in sync with the tremolo positioning code in
364 Stem_tremolo::print */
366 minlen += beam_trans;
368 length = max (length, minlen + 1.0);
371 return scm_from_double (length);
373 /* The log of the duration (Number of hooks on the flag minus two) */
375 Stem::duration_log (Grob *me)
377 SCM s = me->get_property ("duration-log");
378 return (scm_is_number (s)) ? scm_to_int (s) : 2;
381 MAKE_SCHEME_CALLBACK (Stem, calc_positioning_done, 1);
383 Stem::calc_positioning_done (SCM smob)
385 Grob *me = unsmob_grob (smob);
386 if (!head_count (me))
389 me->set_property ("positioning-done", SCM_BOOL_T);
391 extract_grob_set (me, "note-heads", ro_heads);
392 vector<Grob*> heads (ro_heads);
393 vector_sort (heads, position_less);
394 Direction dir = get_grob_direction (me);
399 Real thick = thickness (me);
401 Grob *hed = support_head (me);
404 programming_error ("Stem dir must be up or down.");
406 set_grob_direction (me, dir);
409 bool is_harmonic_centered = false;
410 for (vsize i = 0; i < heads.size (); i++)
411 is_harmonic_centered = is_harmonic_centered
412 || heads[i]->get_property ("style") == ly_symbol2scm ("harmonic");
413 is_harmonic_centered = is_harmonic_centered && is_invisible (me);
415 Real w = hed->extent (hed, X_AXIS)[dir];
416 for (vsize i = 0; i < heads.size (); i++)
418 Real amount = w - heads[i]->extent (heads[i], X_AXIS)[dir];
420 if (is_harmonic_centered)
422 hed->extent (hed, X_AXIS).linear_combination (CENTER)
423 - heads[i]->extent (heads[i], X_AXIS).linear_combination (CENTER);
425 heads[i]->translate_axis (amount, X_AXIS);
428 Real lastpos = Real (Staff_symbol_referencer::get_position (heads[0]));
429 for (vsize i = 1; i < heads.size (); i++)
431 Real p = Staff_symbol_referencer::get_position (heads[i]);
432 Real dy = fabs (lastpos- p);
435 dy should always be 0.5, 0.0, 1.0, but provide safety margin
442 Real ell = heads[i]->extent (heads[i], X_AXIS).length ();
444 Direction d = get_grob_direction (me);
446 Reversed head should be shifted ell-thickness, but this
447 looks too crowded, so we only shift ell-0.5*thickness.
449 This leads to assymetry: Normal heads overlap the
450 stem 100% whereas reversed heads only overlaps the
454 Real reverse_overlap = 0.5;
455 heads[i]->translate_axis ((ell - thick * reverse_overlap) * d,
458 if (is_invisible (me))
459 heads[i]->translate_axis (-thick * (2 - reverse_overlap) * d,
464 For some cases we should kern some more: when the
465 distance between the next or prev note is too large, we'd
466 get large white gaps, eg.
487 MAKE_SCHEME_CALLBACK (Stem, calc_direction, 1);
489 Stem::calc_direction (SCM smob)
491 Grob *me = unsmob_grob (smob);
492 Direction dir = CENTER;
493 if (Grob *beam = unsmob_grob (me->get_object ("beam")))
495 SCM ignore_me = beam->get_property ("direction");
497 dir = get_grob_direction (me);
501 SCM dd = me->get_property ("default-direction");
504 return me->get_property ("neutral-direction");
507 return scm_from_int (dir);
510 MAKE_SCHEME_CALLBACK (Stem, calc_default_direction, 1);
512 Stem::calc_default_direction (SCM smob)
514 Grob *me = unsmob_grob (smob);
516 Direction dir = CENTER;
517 int staff_center = 0;
518 Interval hp = head_positions (me);
521 int udistance = (int) (UP * hp[UP] - staff_center);
522 int ddistance = (int) (DOWN * hp[DOWN] - staff_center);
524 dir = Direction (sign (ddistance - udistance));
527 return scm_from_int (dir);
531 MAKE_SCHEME_CALLBACK (Stem, height, 1);
533 Stem::height (SCM smob)
535 Grob *me = unsmob_grob (smob);
536 if (!is_normal_stem (me))
537 return ly_interval2scm (Interval ());
539 Direction dir = get_grob_direction (me);
541 Grob *beam = get_beam (me);
544 /* trigger set-stem-lengths. */
545 beam->get_property ("quantized-positions");
549 Can't get_stencil (), since that would cache stencils too early.
550 This causes problems with beams.
552 Stencil *stencil = unsmob_stencil (print (smob));
553 Interval iv = stencil ? stencil->extent (Y_AXIS) : Interval ();
558 programming_error ("no stem direction");
561 iv[dir] += dir * Beam::get_thickness (beam) * 0.5;
564 return ly_interval2scm (iv);
568 Stem::stem_end_position (Grob *me)
570 return robust_scm2double (me->get_property ("stem-end-position"), 0);
573 MAKE_SCHEME_CALLBACK (Stem, calc_flag, 1);
575 Stem::calc_flag (SCM smob)
577 Grob *me = unsmob_grob (smob);
579 int log = duration_log (me);
581 TODO: maybe property stroke-style should take different values,
582 e.g. "" (i.e. no stroke), "single" and "double" (currently, it's
586 SCM flag_style_scm = me->get_property ("flag-style");
587 if (scm_is_symbol (flag_style_scm))
588 flag_style = ly_symbol2string (flag_style_scm);
590 if (flag_style == "no-flag")
591 return Stencil ().smobbed_copy ();
595 string staffline_offs;
596 if (flag_style == "mensural")
597 /* Mensural notation: For notes on staff lines, use different
598 flags than for notes between staff lines. The idea is that
599 flags are always vertically aligned with the staff lines,
600 regardless if the note head is on a staff line or between two
601 staff lines. In other words, the inner end of a flag always
602 touches a staff line.
607 int p = (int) (rint (stem_end_position (me)));
609 = Staff_symbol_referencer::on_line (me, p) ? "0" : "1";
612 staffline_offs = "2";
617 char dir = (get_grob_direction (me) == UP) ? 'u' : 'd';
618 string font_char = flag_style
619 + to_string (dir) + staffline_offs + to_string (log);
620 Font_metric *fm = Font_interface::get_default_font (me);
621 Stencil flag = fm->find_by_name ("flags." + font_char);
622 if (flag.is_empty ())
623 me->warning (_f ("flag `%s' not found", font_char));
625 SCM stroke_style_scm = me->get_property ("stroke-style");
626 if (scm_is_string (stroke_style_scm))
628 string stroke_style = ly_scm2string (stroke_style_scm);
629 if (!stroke_style.empty ())
631 string font_char = to_string (dir) + stroke_style;
632 Stencil stroke = fm->find_by_name ("flags." + font_char);
633 if (stroke.is_empty ())
634 me->warning (_f ("flag stroke `%s' not found", font_char));
636 flag.add_stencil (stroke);
640 return flag.smobbed_copy ();
645 Stem::flag (Grob *me)
647 int log = duration_log (me);
649 || unsmob_grob (me->get_object ("beam")))
652 if (!is_normal_stem (me))
655 // This get_property call already evaluates the scheme function with
656 // the grob passed as argument! Thus, we only have to check if a valid
657 // stencil is returned.
658 SCM flag_style_scm = me->get_property ("flag");
659 if (Stencil *flag = unsmob_stencil (flag_style_scm)) {
666 MAKE_SCHEME_CALLBACK (Stem, width, 1);
670 Grob *me = unsmob_grob (e);
674 if (is_invisible (me))
676 else if (unsmob_grob (me->get_object ("beam"))
677 || abs (duration_log (me)) <= 2)
679 r = Interval (-1, 1);
680 r *= thickness (me) / 2;
684 r = Interval (-1, 1) * thickness (me) * 0.5;
685 r.unite (flag (me).extent (X_AXIS));
687 return ly_interval2scm (r);
691 Stem::thickness (Grob *me)
693 return scm_to_double (me->get_property ("thickness"))
694 * Staff_symbol_referencer::line_thickness (me);
697 MAKE_SCHEME_CALLBACK (Stem, print, 1);
699 Stem::print (SCM smob)
701 Grob *me = unsmob_grob (smob);
702 Grob *beam = get_beam (me);
705 Direction d = get_grob_direction (me);
707 Real stemlet_length = robust_scm2double (me->get_property ("stemlet-length"),
709 bool stemlet = stemlet_length > 0.0;
711 /* TODO: make the stem start a direction ?
712 This is required to avoid stems passing in tablature chords. */
714 = to_boolean (me->get_property ("avoid-note-head"))
721 if (!lh && stemlet && !beam)
724 if (lh && robust_scm2int (lh->get_property ("duration-log"), 0) < 1)
727 if (is_invisible (me))
730 Real y2 = robust_scm2double (me->get_property ("stem-end-position"), 0.0);
732 Real half_space = Staff_symbol_referencer::staff_space (me) * 0.5;
735 y2 = Staff_symbol_referencer::get_position (lh);
738 Real beam_translation = Beam::get_beam_translation (beam);
739 Real beam_thickness = Beam::get_thickness (beam);
740 int beam_count = beam_multiplicity (me).length () + 1;
743 * (0.5 * beam_thickness
744 + beam_translation * max (0, (beam_count - 1))
745 + stemlet_length) / half_space;
748 Interval stem_y (min (y1, y2), max (y2, y1));
750 if (Grob *head = support_head (me))
753 must not take ledgers into account.
755 Interval head_height = head->extent (head, Y_AXIS);
756 Real y_attach = Note_head::stem_attachment_coordinate (head, Y_AXIS);
758 y_attach = head_height.linear_combination (y_attach);
759 stem_y[Direction (-d)] += d * y_attach / half_space;
763 Real stem_width = thickness (me);
765 = me->layout ()->get_dimension (ly_symbol2scm ("blot-diameter"));
767 Box b = Box (Interval (-stem_width / 2, stem_width / 2),
768 Interval (stem_y[DOWN] * half_space, stem_y[UP] * half_space));
770 Stencil ss = Lookup::round_filled_box (b, blot);
771 mol.add_stencil (ss);
773 mol.add_stencil (get_translated_flag (me));
775 return mol.smobbed_copy ();
779 Stem::get_translated_flag (Grob *me)
781 Stencil fl = flag (me);
784 Direction d = get_grob_direction (me);
786 = me->layout ()->get_dimension (ly_symbol2scm ("blot-diameter"));
787 Real stem_width = thickness (me);
788 Real half_space = Staff_symbol_referencer::staff_space (me) * 0.5;
789 Real y2 = robust_scm2double (me->get_property ("stem-end-position"), 0.0);
790 fl.translate_axis (y2 * half_space - d * blot / 2, Y_AXIS);
791 fl.translate_axis (stem_width / 2, X_AXIS);
798 move the stem to right of the notehead if it is up.
800 MAKE_SCHEME_CALLBACK (Stem, offset_callback, 1);
802 Stem::offset_callback (SCM smob)
804 Grob *me = unsmob_grob (smob);
806 extract_grob_set (me, "rests", rests);
809 Grob *rest = rests.back ();
810 Real r = rest->extent (rest, X_AXIS).center ();
811 return scm_from_double (r);
815 if (Grob *f = first_head (me))
817 Interval head_wid = f->extent (f, X_AXIS);
820 if (is_invisible (me))
823 attach = Note_head::stem_attachment_coordinate (f, X_AXIS);
825 Direction d = get_grob_direction (me);
826 Real real_attach = head_wid.linear_combination (d * attach);
827 Real r = real_attach;
829 /* If not centered: correct for stem thickness. */
832 Real rule_thick = thickness (me);
833 r += -d * rule_thick * 0.5;
835 return scm_from_double (r);
838 programming_error ("Weird stem.");
839 return scm_from_double (0.0);
843 Stem::get_beam (Grob *me)
845 SCM b = me->get_object ("beam");
846 return dynamic_cast<Spanner *> (unsmob_grob (b));
850 Stem::get_stem_info (Grob *me)
853 si.dir_ = get_grob_direction (me);
855 SCM scm_info = me->get_property ("stem-info");
856 si.ideal_y_ = scm_to_double (scm_car (scm_info));
857 si.shortest_y_ = scm_to_double (scm_cadr (scm_info));
861 MAKE_SCHEME_CALLBACK (Stem, calc_stem_info, 1);
863 Stem::calc_stem_info (SCM smob)
865 Grob *me = unsmob_grob (smob);
866 Direction my_dir = get_grob_direction (me);
870 programming_error ("no stem dir set");
874 Real staff_space = Staff_symbol_referencer::staff_space (me);
875 Grob *beam = get_beam (me);
879 (void) beam->get_property ("beaming");
882 Real beam_translation = Beam::get_beam_translation (beam);
883 Real beam_thickness = Beam::get_thickness (beam);
884 int beam_count = Beam::get_direction_beam_count (beam, my_dir);
886 = robust_scm2double (me->get_property ("length-fraction"), 1.0);
888 /* Simple standard stem length */
889 SCM details = me->get_property ("details");
890 SCM lengths = ly_assoc_get (ly_symbol2scm ("beamed-lengths"), details, SCM_EOL);
893 = scm_to_double (robust_list_ref (beam_count - 1, lengths))
897 /* stem only extends to center of beam
899 - 0.5 * beam_thickness;
901 /* Condition: sane minimum free stem length (chord to beams) */
902 lengths = ly_assoc_get (ly_symbol2scm ("beamed-minimum-free-lengths"), details, SCM_EOL);
904 Real ideal_minimum_free
905 = scm_to_double (robust_list_ref (beam_count - 1, lengths))
909 Real height_of_my_trem = 0.0;
910 Grob *trem = unsmob_grob (me->get_object ("tremolo-flag"));
914 = Stem_tremolo::vertical_length (trem)
915 /* hack a bit of space around the trem. */
921 It seems that also for ideal minimum length, we must use
922 the maximum beam count (for this direction):
924 \score{ \notes\relative c''{ [a8 a32] }}
926 must be horizontal. */
927 Real height_of_my_beams = beam_thickness
928 + (beam_count - 1) * beam_translation;
930 Real ideal_minimum_length = ideal_minimum_free
933 /* stem only extends to center of beam */
934 - 0.5 * beam_thickness;
936 ideal_length = max (ideal_length, ideal_minimum_length);
938 /* Convert to Y position, calculate for dir == UP */
940 = /* staff positions */
941 head_positions (me)[my_dir] * 0.5
942 * my_dir * staff_space;
943 Real ideal_y = note_start + ideal_length;
945 /* Conditions for Y position */
947 /* Lowest beam of (UP) beam must never be lower than second staffline
951 Although this (additional) rule is probably correct,
952 I expect that highest beam (UP) should also never be lower
953 than middle staffline, just as normal stems.
957 Obviously not for grace beams.
959 Also, not for knees. Seems to be a good thing. */
960 bool no_extend = to_boolean (me->get_property ("no-stem-extend"));
961 bool is_knee = to_boolean (beam->get_property ("knee"));
962 if (!no_extend && !is_knee)
964 /* Highest beam of (UP) beam must never be lower than middle
966 ideal_y = max (ideal_y, 0.0);
967 /* Lowest beam of (UP) beam must never be lower than second staffline */
968 ideal_y = max (ideal_y, (-staff_space
969 - beam_thickness + height_of_my_beams));
972 ideal_y -= robust_scm2double (beam->get_property ("shorten"), 0);
974 SCM bemfl = ly_assoc_get (ly_symbol2scm ("beamed-extreme-minimum-free-lengths"),
978 = scm_to_double (robust_list_ref (beam_count - 1, bemfl))
982 Real minimum_length = max (minimum_free, height_of_my_trem)
984 /* stem only extends to center of beam */
985 - 0.5 * beam_thickness;
988 Real minimum_y = note_start + minimum_length;
989 Real shortest_y = minimum_y * my_dir;
991 return scm_list_2 (scm_from_double (ideal_y),
992 scm_from_double (shortest_y));
996 Stem::beam_multiplicity (Grob *stem)
998 SCM beaming = stem->get_property ("beaming");
999 Slice le = int_list_to_slice (scm_car (beaming));
1000 Slice ri = int_list_to_slice (scm_cdr (beaming));
1006 Stem::is_cross_staff (Grob *stem)
1008 Grob *beam = unsmob_grob (stem->get_object ("beam"));
1009 return beam && Beam::is_cross_staff (beam);
1012 MAKE_SCHEME_CALLBACK (Stem, calc_cross_staff, 1)
1014 Stem::calc_cross_staff (SCM smob)
1016 return scm_from_bool (is_cross_staff (unsmob_grob (smob)));
1019 /* FIXME: Too many properties */
1020 ADD_INTERFACE (Stem,
1021 "The stem represents the graphical stem. In addition, it"
1022 " internally connects note heads, beams, and tremolos. Rests"
1023 " and whole notes have invisible stems.\n"
1025 "The following properties may be set in the @code{details}"
1029 "@item beamed-lengths\n"
1030 "List of stem lengths given beam multiplicity.\n"
1031 "@item beamed-minimum-free-lengths\n"
1032 "List of normal minimum free stem lengths (chord to beams)"
1033 " given beam multiplicity.\n"
1034 "@item beamed-extreme-minimum-free-lengths\n"
1035 "List of extreme minimum free stem lengths (chord to beams)"
1036 " given beam multiplicity.\n"
1038 "Default stem lengths. The list gives a length for each"
1040 "@item stem-shorten\n"
1041 "How much a stem in a forced direction should be shortened."
1042 " The list gives an amount depending on the number of flags"
1050 "default-direction "
1060 "neutral-direction "
1065 "stem-end-position "
1073 /****************************************************************/
1075 Stem_info::Stem_info ()
1077 ideal_y_ = shortest_y_ = 0;
1082 Stem_info::scale (Real x)