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 me->set_property ("positioning-done", SCM_BOOL_T);
419 extract_grob_set (me, "note-heads", ro_heads);
420 vector<Grob*> heads (ro_heads);
421 vector_sort (heads, position_less);
422 Direction dir = get_grob_direction (me);
427 Real thick = thickness (me);
429 Grob *hed = support_head (me);
432 programming_error ("Stem dir must be up or down.");
434 set_grob_direction (me, dir);
437 bool is_harmonic_centered = false;
438 for (vsize i = 0; i < heads.size (); i++)
439 is_harmonic_centered = is_harmonic_centered
440 || heads[i]->get_property ("style") == ly_symbol2scm ("harmonic");
441 is_harmonic_centered = is_harmonic_centered && is_invisible (me);
443 Real w = hed->extent (hed, X_AXIS)[dir];
444 for (vsize i = 0; i < heads.size (); i++)
446 Real amount = w - heads[i]->extent (heads[i], X_AXIS)[dir];
448 if (is_harmonic_centered)
450 hed->extent (hed, X_AXIS).linear_combination (CENTER)
451 - heads[i]->extent (heads[i], X_AXIS).linear_combination (CENTER);
453 heads[i]->translate_axis (amount, X_AXIS);
456 Real lastpos = Real (Staff_symbol_referencer::get_position (heads[0]));
457 for (vsize i = 1; i < heads.size (); i++)
459 Real p = Staff_symbol_referencer::get_position (heads[i]);
460 Real dy = fabs (lastpos- p);
463 dy should always be 0.5, 0.0, 1.0, but provide safety margin
470 Real ell = heads[i]->extent (heads[i], X_AXIS).length ();
472 Direction d = get_grob_direction (me);
474 Reversed head should be shifted ell-thickness, but this
475 looks too crowded, so we only shift ell-0.5*thickness.
477 This leads to assymetry: Normal heads overlap the
478 stem 100% whereas reversed heads only overlaps the
482 Real reverse_overlap = 0.5;
483 heads[i]->translate_axis ((ell - thick * reverse_overlap) * d,
486 if (is_invisible (me))
487 heads[i]->translate_axis (-thick * (2 - reverse_overlap) * d,
492 For some cases we should kern some more: when the
493 distance between the next or prev note is too large, we'd
494 get large white gaps, eg.
515 MAKE_SCHEME_CALLBACK (Stem, calc_direction, 1);
517 Stem::calc_direction (SCM smob)
519 Grob *me = unsmob_grob (smob);
520 Direction dir = CENTER;
521 if (Grob *beam = unsmob_grob (me->get_object ("beam")))
523 SCM ignore_me = beam->get_property ("direction");
525 dir = get_grob_direction (me);
529 SCM dd = me->get_property ("default-direction");
532 return me->get_property ("neutral-direction");
535 return scm_from_int (dir);
538 MAKE_SCHEME_CALLBACK (Stem, calc_default_direction, 1);
540 Stem::calc_default_direction (SCM smob)
542 Grob *me = unsmob_grob (smob);
544 Direction dir = CENTER;
545 int staff_center = 0;
546 Interval hp = head_positions (me);
549 int udistance = (int) (UP * hp[UP] - staff_center);
550 int ddistance = (int) (DOWN * hp[DOWN] - staff_center);
552 dir = Direction (sign (ddistance - udistance));
555 return scm_from_int (dir);
559 MAKE_SCHEME_CALLBACK (Stem, height, 1);
561 Stem::height (SCM smob)
563 Grob *me = unsmob_grob (smob);
564 if (!is_normal_stem (me))
565 return ly_interval2scm (Interval ());
567 Direction dir = get_grob_direction (me);
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"));
917 = Stem_tremolo::vertical_length (trem)
918 /* hack a bit of space around the trem. */
924 It seems that also for ideal minimum length, we must use
925 the maximum beam count (for this direction):
927 \score{ \notes\relative c''{ [a8 a32] }}
929 must be horizontal. */
930 Real height_of_my_beams = beam_thickness
931 + (beam_count - 1) * beam_translation;
933 Real ideal_minimum_length = ideal_minimum_free
936 /* stem only extends to center of beam */
937 - 0.5 * beam_thickness;
939 ideal_length = max (ideal_length, ideal_minimum_length);
941 /* Convert to Y position, calculate for dir == UP */
943 = /* staff positions */
944 head_positions (me)[my_dir] * 0.5
945 * my_dir * staff_space;
946 Real ideal_y = note_start + ideal_length;
948 /* Conditions for Y position */
950 /* Lowest beam of (UP) beam must never be lower than second staffline
954 Although this (additional) rule is probably correct,
955 I expect that highest beam (UP) should also never be lower
956 than middle staffline, just as normal stems.
960 Obviously not for grace beams.
962 Also, not for knees. Seems to be a good thing. */
963 bool no_extend_b = to_boolean (me->get_property ("no-stem-extend"));
964 bool is_knee = to_boolean (beam->get_property ("knee"));
965 if (!no_extend_b && !is_knee)
967 /* Highest beam of (UP) beam must never be lower than middle
969 ideal_y = max (ideal_y, 0.0);
970 /* Lowest beam of (UP) beam must never be lower than second staffline */
971 ideal_y = max (ideal_y, (-staff_space
972 - beam_thickness + height_of_my_beams));
975 ideal_y -= robust_scm2double (beam->get_property ("shorten"), 0);
977 SCM bemfl = scm_cdr (scm_assq (ly_symbol2scm ("beamed-extreme-minimum-free-lengths"),
981 = scm_to_double (robust_list_ref (beam_count - 1, bemfl))
985 Real minimum_length = max (minimum_free, height_of_my_trem)
987 /* stem only extends to center of beam */
988 - 0.5 * beam_thickness;
991 Real minimum_y = note_start + minimum_length;
992 Real shortest_y = minimum_y * my_dir;
994 return scm_list_2 (scm_from_double (ideal_y),
995 scm_from_double (shortest_y));
999 Stem::beam_multiplicity (Grob *stem)
1001 SCM beaming = stem->get_property ("beaming");
1002 Slice le = int_list_to_slice (scm_car (beaming));
1003 Slice ri = int_list_to_slice (scm_cdr (beaming));
1009 Stem::is_cross_staff (Grob *stem)
1011 Grob *beam = unsmob_grob (stem->get_object ("beam"));
1012 return beam && Beam::is_cross_staff (beam);
1015 MAKE_SCHEME_CALLBACK (Stem, cross_staff, 1)
1017 Stem::cross_staff (SCM smob)
1019 return scm_from_bool (is_cross_staff (unsmob_grob (smob)));
1022 /* FIXME: Too many properties */
1023 ADD_INTERFACE (Stem,
1024 "The stem represent the graphical stem. "
1025 "In addition, it internally connects note heads, beams and"
1027 "Rests and whole notes have invisible stems."
1029 "\n\nThe following properties may be set in the details list."
1031 "@item beamed-lengths \n"
1032 "list of stem lengths given beam multiplicity. \n"
1033 "@item beamed-minimum-free-lengths \n"
1034 "list of normal minimum free stem lengths (chord to beams) given beam multiplicity.\n"
1035 "@item beamed-extreme-minimum-free-lengths\n"
1036 "list of extreme minimum free stem lengths (chord to beams) given beam multiplicity.\n"
1038 "Default stem lengths. The list gives a length for each flag-count.\n"
1039 "@item stem-shorten\n"
1040 "How much a stem in a forced direction "
1041 "should be shortened. The list gives an amount depending on the number "
1050 "default-direction "
1059 "neutral-direction "
1064 "stem-end-position "
1072 /****************************************************************/
1074 Stem_info::Stem_info ()
1076 ideal_y_ = shortest_y_ = 0;
1081 Stem_info::scale (Real x)