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 /* if we are part of a cross-staff beam, return empty */
244 if (get_beam (me) && Beam::is_cross_staff (get_beam (me)))
245 return ly_interval2scm (iv);
247 Real ss = Staff_symbol_referencer::staff_space (me);
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 return ly_interval2scm (iv);
263 MAKE_SCHEME_CALLBACK (Stem, calc_stem_end_position, 1)
265 Stem::calc_stem_end_position (SCM smob)
267 Grob *me = unsmob_grob (smob);
269 if (!head_count (me))
270 return scm_from_double (0.0);
272 if (Grob *beam = get_beam (me))
274 (void) beam->get_property ("quantized-positions");
275 return me->get_property ("stem-end-position");
278 Real ss = Staff_symbol_referencer::staff_space (me);
279 int durlog = duration_log (me);
282 /* WARNING: IN HALF SPACES */
283 Real length = robust_scm2double (me->get_property ("length"), 7);
285 Direction dir = get_grob_direction (me);
286 Interval hp = head_positions (me);
287 Real stem_end = dir ? hp[dir] + dir * length : 0;
289 /* TODO: change name to extend-stems to staff/center/'() */
290 bool no_extend_b = to_boolean (me->get_property ("no-stem-extend"));
291 if (!no_extend_b && dir * stem_end < 0)
295 /* Make a little room if we have a upflag and there is a dot.
296 previous approach was to lengthen the stem. This is not
297 good typesetting practice. */
298 if (!get_beam (me) && dir == UP
301 Grob *closest_to_flag = extremal_heads (me)[dir];
302 Grob *dots = closest_to_flag
303 ? Rhythmic_head::get_dots (closest_to_flag) : 0;
307 Real dp = Staff_symbol_referencer::get_position (dots);
308 Interval flag_yext = flag (me).extent (Y_AXIS) * (2 / ss) + stem_end;
310 /* Very gory: add myself to the X-support of the parent,
311 which should be a dot-column. */
313 if (flag_yext.distance (dp) < 0.5)
315 Grob *par = dots->get_parent (X_AXIS);
317 if (Dot_column::has_interface (par))
319 Side_position_interface::add_support (par, me);
321 /* TODO: apply some better logic here. The flag is
322 curved inwards, so this will typically be too
329 return scm_from_double (stem_end);
332 /* Length is in half-spaces (or: positions) here. */
333 MAKE_SCHEME_CALLBACK (Stem, calc_length, 1)
335 Stem::calc_length (SCM smob)
337 Grob *me = unsmob_grob (smob);
339 SCM details = me->get_property ("details");
340 int durlog = duration_log (me);
342 Real ss = Staff_symbol_referencer::staff_space (me);
344 SCM s = scm_cdr (scm_assq (ly_symbol2scm ("lengths"), details));
346 length = 2 * scm_to_double (robust_list_ref (durlog - 2, s));
348 Direction dir = get_grob_direction (me);
350 /* Stems in unnatural (forced) direction should be shortened,
351 according to [Roush & Gourlay] */
352 Interval hp = head_positions (me);
353 if (dir && dir * hp[dir] >= 0)
355 SCM sshorten = scm_cdr (scm_assq (ly_symbol2scm ("stem-shorten"), details));
356 SCM scm_shorten = scm_is_pair (sshorten)
357 ? robust_list_ref (max (duration_log (me) - 2, 0), sshorten) : SCM_EOL;
358 Real shorten = 2* robust_scm2double (scm_shorten, 0);
360 /* On boundary: shorten only half */
361 if (abs (head_positions (me)[dir]) <= 1)
367 length *= robust_scm2double (me->get_property ("length-fraction"), 1.0);
370 Grob *t_flag = unsmob_grob (me->get_object ("tremolo-flag"));
371 if (t_flag && !unsmob_grob (me->get_object ("beam")))
373 /* Crude hack: add extra space if tremolo flag is there.
375 We can't do this for the beam, since we get into a loop
376 (Stem_tremolo::raw_stencil () looks at the beam.) --hwn */
379 + 2 * Stem_tremolo::vertical_length (t_flag) / ss;
381 /* We don't want to add the whole extent of the flag because the trem
382 and the flag can overlap partly. beam_translation gives a good
386 Real beam_trans = Stem_tremolo::get_beam_translation (t_flag);
387 /* the obvious choice is (durlog - 2) here, but we need a bit more space. */
388 minlen += 2 * (durlog - 1.5) * beam_trans;
390 /* up-stems need even a little more space to avoid collisions. This
391 needs to be in sync with the tremolo positioning code in
392 Stem_tremolo::print */
394 minlen += beam_trans;
396 length = max (length, minlen + 1.0);
399 return scm_from_double (length);
401 /* The log of the duration (Number of hooks on the flag minus two) */
403 Stem::duration_log (Grob *me)
405 SCM s = me->get_property ("duration-log");
406 return (scm_is_number (s)) ? scm_to_int (s) : 2;
409 MAKE_SCHEME_CALLBACK(Stem, calc_positioning_done, 1);
411 Stem::calc_positioning_done (SCM smob)
413 Grob *me = unsmob_grob (smob);
414 if (!head_count (me))
417 extract_grob_set (me, "note-heads", ro_heads);
418 vector<Grob*> heads (ro_heads);
419 vector_sort (heads, position_less);
420 Direction dir = get_grob_direction (me);
425 Real thick = thickness (me);
427 Grob *hed = support_head (me);
430 programming_error ("Stem dir must be up or down.");
432 set_grob_direction (me, dir);
435 bool is_harmonic_centered = false;
436 for (vsize i = 0; i < heads.size (); i++)
437 is_harmonic_centered = is_harmonic_centered
438 || heads[i]->get_property ("style") == ly_symbol2scm ("harmonic");
439 is_harmonic_centered = is_harmonic_centered && is_invisible (me);
441 Real w = hed->extent (hed, X_AXIS)[dir];
442 for (vsize i = 0; i < heads.size (); i++)
444 Real amount = w - heads[i]->extent (heads[i], X_AXIS)[dir];
446 if (is_harmonic_centered)
448 hed->extent (hed, X_AXIS).linear_combination (CENTER)
449 - heads[i]->extent (heads[i], X_AXIS).linear_combination (CENTER);
451 heads[i]->translate_axis (amount, X_AXIS);
454 Real lastpos = Real (Staff_symbol_referencer::get_position (heads[0]));
455 for (vsize i = 1; i < heads.size (); i++)
457 Real p = Staff_symbol_referencer::get_position (heads[i]);
458 Real dy = fabs (lastpos- p);
461 dy should always be 0.5, 0.0, 1.0, but provide safety margin
468 Real ell = heads[i]->extent (heads[i], X_AXIS).length ();
470 Direction d = get_grob_direction (me);
472 Reversed head should be shifted ell-thickness, but this
473 looks too crowded, so we only shift ell-0.5*thickness.
475 This leads to assymetry: Normal heads overlap the
476 stem 100% whereas reversed heads only overlaps the
480 Real reverse_overlap = 0.5;
481 heads[i]->translate_axis ((ell - thick * reverse_overlap) * d,
484 if (is_invisible (me))
485 heads[i]->translate_axis (-thick * (2 - reverse_overlap) * d,
490 For some cases we should kern some more: when the
491 distance between the next or prev note is too large, we'd
492 get large white gaps, eg.
513 MAKE_SCHEME_CALLBACK(Stem, calc_direction, 1);
515 Stem::calc_direction (SCM smob)
517 Grob *me = unsmob_grob (smob);
518 Direction dir = CENTER;
519 if (Grob *beam = unsmob_grob (me->get_object ("beam")))
521 SCM ignore_me = beam->get_property ("direction");
523 dir = get_grob_direction (me);
527 SCM dd = me->get_property ("default-direction");
530 return me->get_property ("neutral-direction");
533 return scm_from_int (dir);
536 MAKE_SCHEME_CALLBACK(Stem, calc_default_direction, 1);
538 Stem::calc_default_direction (SCM smob)
540 Grob *me = unsmob_grob (smob);
542 Direction dir = CENTER;
543 int staff_center = 0;
544 Interval hp = head_positions (me);
547 int udistance = (int) (UP * hp[UP] - staff_center);
548 int ddistance = (int) (DOWN * hp[DOWN] - staff_center);
550 dir = Direction (sign (ddistance - udistance));
553 return scm_from_int (dir);
557 MAKE_SCHEME_CALLBACK (Stem, height, 1);
559 Stem::height (SCM smob)
561 Grob *me = unsmob_grob (smob);
563 Direction dir = get_grob_direction (me);
567 UGH. Should be automatic
569 Grob *beam = get_beam (me);
572 /* trigger set-stem-lengths. */
573 beam->get_property ("quantized-positions");
577 Can't get_stencil(), since that would cache stencils too early.
578 This causes problems with beams.
580 Stencil *stencil = unsmob_stencil (print (smob));
581 Interval iv = stencil ? stencil->extent (Y_AXIS) : Interval();
586 programming_error ("no stem direction");
589 iv[dir] += dir * Beam::get_thickness (beam) * 0.5;
592 return ly_interval2scm (iv);
596 Stem::stem_end_position (Grob *me)
598 return robust_scm2double (me->get_property ("stem-end-position"), 0);
602 Stem::flag (Grob *me)
604 int log = duration_log (me);
606 || unsmob_grob (me->get_object ("beam")))
610 TODO: maybe property stroke-style should take different values,
611 e.g. "" (i.e. no stroke), "single" and "double" (currently, it's
615 SCM flag_style_scm = me->get_property ("flag-style");
616 if (scm_is_symbol (flag_style_scm))
617 flag_style = ly_symbol2string (flag_style_scm);
619 if (flag_style == "no-flag")
624 string staffline_offs;
625 if (flag_style == "mensural")
626 /* Mensural notation: For notes on staff lines, use different
627 flags than for notes between staff lines. The idea is that
628 flags are always vertically aligned with the staff lines,
629 regardless if the note head is on a staff line or between two
630 staff lines. In other words, the inner end of a flag always
631 touches a staff line.
636 int p = (int) (rint (stem_end_position (me)));
638 = Staff_symbol_referencer::on_line (me, p) ? "0" : "1";
641 staffline_offs = "2";
646 char dir = (get_grob_direction (me) == UP) ? 'u' : 'd';
647 string font_char = flag_style
648 + to_string (dir) + staffline_offs + to_string (log);
649 Font_metric *fm = Font_interface::get_default_font (me);
650 Stencil flag = fm->find_by_name ("flags." + font_char);
651 if (flag.is_empty ())
652 me->warning (_f ("flag `%s' not found", font_char));
654 SCM stroke_style_scm = me->get_property ("stroke-style");
655 if (scm_is_string (stroke_style_scm))
657 string stroke_style = ly_scm2string (stroke_style_scm);
658 if (!stroke_style.empty ())
660 string font_char = to_string (dir) + stroke_style;
661 Stencil stroke = fm->find_by_name ("flags." + font_char);
662 if (stroke.is_empty ())
663 me->warning (_f ("flag stroke `%s' not found", font_char));
665 flag.add_stencil (stroke);
672 MAKE_SCHEME_CALLBACK (Stem, width, 1);
676 Grob *me = unsmob_grob (e);
680 if (is_invisible (me))
682 else if (unsmob_grob (me->get_object ("beam"))
683 || abs (duration_log (me)) <= 2)
685 r = Interval (-1, 1);
686 r *= thickness (me) / 2;
690 r = Interval (-1, 1) * thickness (me) * 0.5;
691 r.unite (flag (me).extent (X_AXIS));
693 return ly_interval2scm (r);
697 Stem::thickness (Grob *me)
699 return scm_to_double (me->get_property ("thickness"))
700 * Staff_symbol_referencer::line_thickness (me);
703 MAKE_SCHEME_CALLBACK (Stem, print, 1);
705 Stem::print (SCM smob)
707 Grob *me = unsmob_grob (smob);
708 Grob *beam = get_beam (me);
711 Direction d = get_grob_direction (me);
713 Real stemlet_length = robust_scm2double (me->get_property ("stemlet-length"),
715 bool stemlet = stemlet_length > 0.0;
717 /* TODO: make the stem start a direction ?
718 This is required to avoid stems passing in tablature chords. */
720 = to_boolean (me->get_property ("avoid-note-head"))
727 if (!lh && stemlet && !beam)
730 if (is_invisible (me))
733 Real y2 = robust_scm2double (me->get_property ("stem-end-position"), 0.0);
735 Real half_space = Staff_symbol_referencer::staff_space (me) * 0.5;
738 y2 = Staff_symbol_referencer::get_position (lh);
741 Real beam_translation = Beam::get_beam_translation (beam);
742 Real beam_thickness = Beam::get_thickness (beam);
743 int beam_count = beam_multiplicity (me).length () + 1;
746 * (0.5 * beam_thickness
747 + beam_translation * max (0, (beam_count - 1))
748 + stemlet_length) / half_space;
751 Interval stem_y (min (y1, y2), max (y2, y1));
753 if (Grob *head = support_head (me))
756 must not take ledgers into account.
758 Interval head_height = head->extent (head, Y_AXIS);
759 Real y_attach = Note_head::stem_attachment_coordinate (head, Y_AXIS);
761 y_attach = head_height.linear_combination (y_attach);
762 stem_y[Direction (-d)] += d * y_attach / half_space;
766 Real stem_width = thickness (me);
768 = me->layout ()->get_dimension (ly_symbol2scm ("blot-diameter"));
770 Box b = Box (Interval (-stem_width / 2, stem_width / 2),
771 Interval (stem_y[DOWN] * half_space, stem_y[UP] * half_space));
773 Stencil ss = Lookup::round_filled_box (b, blot);
774 mol.add_stencil (ss);
776 mol.add_stencil (get_translated_flag (me));
778 return mol.smobbed_copy ();
782 Stem::get_translated_flag (Grob *me)
784 Stencil fl = flag (me);
787 Direction d = get_grob_direction (me);
789 = me->layout ()->get_dimension (ly_symbol2scm ("blot-diameter"));
790 Real stem_width = thickness (me);
791 Real half_space = Staff_symbol_referencer::staff_space (me) * 0.5;
792 Real y2 = robust_scm2double (me->get_property ("stem-end-position"), 0.0);
793 fl.translate_axis (y2 * half_space - d * blot / 2, Y_AXIS);
794 fl.translate_axis (stem_width / 2, X_AXIS);
801 move the stem to right of the notehead if it is up.
803 MAKE_SCHEME_CALLBACK (Stem, offset_callback, 1);
805 Stem::offset_callback (SCM smob)
807 Grob *me = unsmob_grob (smob);
809 extract_grob_set (me, "rests", rests);
812 Grob *rest = rests.back ();
813 Real r = rest->extent (rest, X_AXIS).center ();
814 return scm_from_double (r);
818 if (Grob *f = first_head (me))
820 Interval head_wid = f->extent (f, X_AXIS);
823 if (is_invisible (me))
826 attach = Note_head::stem_attachment_coordinate (f, X_AXIS);
828 Direction d = get_grob_direction (me);
829 Real real_attach = head_wid.linear_combination (d * attach);
830 Real r = real_attach;
832 /* If not centered: correct for stem thickness. */
835 Real rule_thick = thickness (me);
836 r += -d * rule_thick * 0.5;
838 return scm_from_double (r);
841 programming_error ("Weird stem.");
842 return scm_from_double (0.0);
846 Stem::get_beam (Grob *me)
848 SCM b = me->get_object ("beam");
849 return dynamic_cast<Spanner *> (unsmob_grob (b));
853 Stem::get_stem_info (Grob *me)
856 si.dir_ = get_grob_direction (me);
858 SCM scm_info = me->get_property ("stem-info");
859 si.ideal_y_ = scm_to_double (scm_car (scm_info));
860 si.shortest_y_ = scm_to_double (scm_cadr (scm_info));
864 MAKE_SCHEME_CALLBACK(Stem, calc_stem_info, 1);
866 Stem::calc_stem_info (SCM smob)
868 Grob *me = unsmob_grob (smob);
869 Direction my_dir = get_grob_direction (me);
873 programming_error ("no stem dir set");
877 Real staff_space = Staff_symbol_referencer::staff_space (me);
878 Grob *beam = get_beam (me);
882 (void) beam->get_property ("beaming");
885 Real beam_translation = Beam::get_beam_translation (beam);
886 Real beam_thickness = Beam::get_thickness (beam);
887 int beam_count = Beam::get_direction_beam_count (beam, my_dir);
889 = robust_scm2double (me->get_property ("length-fraction"), 1.0);
891 /* Simple standard stem length */
892 SCM details = me->get_property ("details");
893 SCM lengths = scm_cdr (scm_assq (ly_symbol2scm ("beamed-lengths"), details));
896 = scm_to_double (robust_list_ref (beam_count - 1, lengths))
900 /* stem only extends to center of beam
902 - 0.5 * beam_thickness;
904 /* Condition: sane minimum free stem length (chord to beams) */
905 lengths = scm_cdr (scm_assq (ly_symbol2scm ("beamed-minimum-free-lengths"), details));
907 Real ideal_minimum_free
908 = scm_to_double (robust_list_ref (beam_count - 1, lengths))
912 Real height_of_my_trem = 0.0;
913 Grob *trem = unsmob_grob (me->get_object ("tremolo-flag"));
915 height_of_my_trem = trem->extent (trem, Y_AXIS).length ()
916 /* hack a bit of space around the trem. */
920 It seems that also for ideal minimum length, we must use
921 the maximum beam count (for this direction):
923 \score{ \notes\relative c''{ [a8 a32] }}
925 must be horizontal. */
926 Real height_of_my_beams = beam_thickness
927 + (beam_count - 1) * beam_translation;
929 Real ideal_minimum_length = ideal_minimum_free
932 /* stem only extends to center of beam */
933 - 0.5 * beam_thickness;
935 ideal_length = max (ideal_length, ideal_minimum_length);
937 /* Convert to Y position, calculate for dir == UP */
939 = /* staff positions */
940 head_positions (me)[my_dir] * 0.5
941 * my_dir * staff_space;
942 Real ideal_y = note_start + ideal_length;
944 /* Conditions for Y position */
946 /* Lowest beam of (UP) beam must never be lower than second staffline
950 Although this (additional) rule is probably correct,
951 I expect that highest beam (UP) should also never be lower
952 than middle staffline, just as normal stems.
956 Obviously not for grace beams.
958 Also, not for knees. Seems to be a good thing. */
959 bool no_extend_b = to_boolean (me->get_property ("no-stem-extend"));
960 bool is_knee = to_boolean (beam->get_property ("knee"));
961 if (!no_extend_b && !is_knee)
963 /* Highest beam of (UP) beam must never be lower than middle
965 ideal_y = max (ideal_y, 0.0);
966 /* Lowest beam of (UP) beam must never be lower than second staffline */
967 ideal_y = max (ideal_y, (-staff_space
968 - beam_thickness + height_of_my_beams));
971 ideal_y -= robust_scm2double (beam->get_property ("shorten"), 0);
973 SCM bemfl = scm_cdr (scm_assq (ly_symbol2scm ("beamed-extreme-minimum-free-lengths"),
977 = scm_to_double (robust_list_ref (beam_count - 1, bemfl))
981 Real minimum_length = max (minimum_free, height_of_my_trem)
983 /* stem only extends to center of beam */
984 - 0.5 * beam_thickness;
987 Real minimum_y = note_start + minimum_length;
988 Real shortest_y = minimum_y * my_dir;
990 return scm_list_2 (scm_from_double (ideal_y),
991 scm_from_double (shortest_y));
995 Stem::beam_multiplicity (Grob *stem)
997 SCM beaming = stem->get_property ("beaming");
998 Slice le = int_list_to_slice (scm_car (beaming));
999 Slice ri = int_list_to_slice (scm_cdr (beaming));
1005 Stem::is_cross_staff (Grob *stem)
1007 Grob *beam = unsmob_grob (stem->get_object ("beam"));
1008 return beam && Beam::is_cross_staff (beam);
1011 MAKE_SCHEME_CALLBACK (Stem, cross_staff, 1)
1013 Stem::cross_staff (SCM smob)
1015 return scm_from_bool (is_cross_staff (unsmob_grob (smob)));
1018 /* FIXME: Too many properties */
1019 ADD_INTERFACE (Stem,
1020 "The stem represent the graphical stem. "
1021 "In addition, it internally connects note heads, beams and"
1023 "Rests and whole notes have invisible stems."
1025 "\n\nThe following properties may be set in the details list."
1027 "@item beamed-lengths \n"
1028 "list of stem lengths given beam multiplicity. \n"
1029 "@item beamed-minimum-free-lengths \n"
1030 "list of normal minimum free stem lengths (chord to beams) given beam multiplicity.\n"
1031 "@item beamed-extreme-minimum-free-lengths\n"
1032 "list of extreme minimum free stem lengths (chord to beams) given beam multiplicity.\n"
1034 "Default stem lengths. The list gives a length for each flag-count.\n"
1035 "@item stem-shorten\n"
1036 "How much a stem in a forced direction "
1037 "should be shortened. The list gives an amount depending on the number "
1046 "default-direction "
1055 "neutral-direction "
1060 "stem-end-position "
1068 /****************************************************************/
1070 Stem_info::Stem_info ()
1072 ideal_y_ = shortest_y_ = 0;
1077 Stem_info::scale (Real x)