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)
294 return scm_from_double (stem_end);
297 /* Length is in half-spaces (or: positions) here. */
298 MAKE_SCHEME_CALLBACK (Stem, calc_length, 1)
300 Stem::calc_length (SCM smob)
302 Grob *me = unsmob_grob (smob);
304 SCM details = me->get_property ("details");
305 int durlog = duration_log (me);
307 Real ss = Staff_symbol_referencer::staff_space (me);
309 SCM s = scm_cdr (scm_assq (ly_symbol2scm ("lengths"), details));
311 length = 2 * scm_to_double (robust_list_ref (durlog - 2, s));
313 Direction dir = get_grob_direction (me);
315 /* Stems in unnatural (forced) direction should be shortened,
316 according to [Roush & Gourlay] */
317 Interval hp = head_positions (me);
318 if (dir && dir * hp[dir] >= 0)
320 SCM sshorten = scm_cdr (scm_assq (ly_symbol2scm ("stem-shorten"), details));
321 SCM scm_shorten = scm_is_pair (sshorten)
322 ? robust_list_ref (max (duration_log (me) - 2, 0), sshorten) : SCM_EOL;
323 Real shorten = 2* robust_scm2double (scm_shorten, 0);
325 /* On boundary: shorten only half */
326 if (abs (head_positions (me)[dir]) <= 1)
332 length *= robust_scm2double (me->get_property ("length-fraction"), 1.0);
335 Grob *t_flag = unsmob_grob (me->get_object ("tremolo-flag"));
336 if (t_flag && !unsmob_grob (me->get_object ("beam")))
338 /* Crude hack: add extra space if tremolo flag is there.
340 We can't do this for the beam, since we get into a loop
341 (Stem_tremolo::raw_stencil () looks at the beam.) --hwn */
344 + 2 * Stem_tremolo::vertical_length (t_flag) / ss;
346 /* We don't want to add the whole extent of the flag because the trem
347 and the flag can overlap partly. beam_translation gives a good
351 Real beam_trans = Stem_tremolo::get_beam_translation (t_flag);
352 /* the obvious choice is (durlog - 2) here, but we need a bit more space. */
353 minlen += 2 * (durlog - 1.5) * beam_trans;
355 /* up-stems need even a little more space to avoid collisions. This
356 needs to be in sync with the tremolo positioning code in
357 Stem_tremolo::print */
359 minlen += beam_trans;
361 length = max (length, minlen + 1.0);
364 return scm_from_double (length);
366 /* The log of the duration (Number of hooks on the flag minus two) */
368 Stem::duration_log (Grob *me)
370 SCM s = me->get_property ("duration-log");
371 return (scm_is_number (s)) ? scm_to_int (s) : 2;
374 MAKE_SCHEME_CALLBACK (Stem, calc_positioning_done, 1);
376 Stem::calc_positioning_done (SCM smob)
378 Grob *me = unsmob_grob (smob);
379 if (!head_count (me))
382 me->set_property ("positioning-done", SCM_BOOL_T);
384 extract_grob_set (me, "note-heads", ro_heads);
385 vector<Grob*> heads (ro_heads);
386 vector_sort (heads, position_less);
387 Direction dir = get_grob_direction (me);
392 Real thick = thickness (me);
394 Grob *hed = support_head (me);
397 programming_error ("Stem dir must be up or down.");
399 set_grob_direction (me, dir);
402 bool is_harmonic_centered = false;
403 for (vsize i = 0; i < heads.size (); i++)
404 is_harmonic_centered = is_harmonic_centered
405 || heads[i]->get_property ("style") == ly_symbol2scm ("harmonic");
406 is_harmonic_centered = is_harmonic_centered && is_invisible (me);
408 Real w = hed->extent (hed, X_AXIS)[dir];
409 for (vsize i = 0; i < heads.size (); i++)
411 Real amount = w - heads[i]->extent (heads[i], X_AXIS)[dir];
413 if (is_harmonic_centered)
415 hed->extent (hed, X_AXIS).linear_combination (CENTER)
416 - heads[i]->extent (heads[i], X_AXIS).linear_combination (CENTER);
418 heads[i]->translate_axis (amount, X_AXIS);
421 Real lastpos = Real (Staff_symbol_referencer::get_position (heads[0]));
422 for (vsize i = 1; i < heads.size (); i++)
424 Real p = Staff_symbol_referencer::get_position (heads[i]);
425 Real dy = fabs (lastpos- p);
428 dy should always be 0.5, 0.0, 1.0, but provide safety margin
435 Real ell = heads[i]->extent (heads[i], X_AXIS).length ();
437 Direction d = get_grob_direction (me);
439 Reversed head should be shifted ell-thickness, but this
440 looks too crowded, so we only shift ell-0.5*thickness.
442 This leads to assymetry: Normal heads overlap the
443 stem 100% whereas reversed heads only overlaps the
447 Real reverse_overlap = 0.5;
448 heads[i]->translate_axis ((ell - thick * reverse_overlap) * d,
451 if (is_invisible (me))
452 heads[i]->translate_axis (-thick * (2 - reverse_overlap) * d,
457 For some cases we should kern some more: when the
458 distance between the next or prev note is too large, we'd
459 get large white gaps, eg.
480 MAKE_SCHEME_CALLBACK (Stem, calc_direction, 1);
482 Stem::calc_direction (SCM smob)
484 Grob *me = unsmob_grob (smob);
485 Direction dir = CENTER;
486 if (Grob *beam = unsmob_grob (me->get_object ("beam")))
488 SCM ignore_me = beam->get_property ("direction");
490 dir = get_grob_direction (me);
494 SCM dd = me->get_property ("default-direction");
497 return me->get_property ("neutral-direction");
500 return scm_from_int (dir);
503 MAKE_SCHEME_CALLBACK (Stem, calc_default_direction, 1);
505 Stem::calc_default_direction (SCM smob)
507 Grob *me = unsmob_grob (smob);
509 Direction dir = CENTER;
510 int staff_center = 0;
511 Interval hp = head_positions (me);
514 int udistance = (int) (UP * hp[UP] - staff_center);
515 int ddistance = (int) (DOWN * hp[DOWN] - staff_center);
517 dir = Direction (sign (ddistance - udistance));
520 return scm_from_int (dir);
524 MAKE_SCHEME_CALLBACK (Stem, height, 1);
526 Stem::height (SCM smob)
528 Grob *me = unsmob_grob (smob);
529 if (!is_normal_stem (me))
530 return ly_interval2scm (Interval ());
532 Direction dir = get_grob_direction (me);
534 Grob *beam = get_beam (me);
537 /* trigger set-stem-lengths. */
538 beam->get_property ("quantized-positions");
542 Can't get_stencil (), since that would cache stencils too early.
543 This causes problems with beams.
545 Stencil *stencil = unsmob_stencil (print (smob));
546 Interval iv = stencil ? stencil->extent (Y_AXIS) : Interval ();
551 programming_error ("no stem direction");
554 iv[dir] += dir * Beam::get_thickness (beam) * 0.5;
557 return ly_interval2scm (iv);
561 Stem::stem_end_position (Grob *me)
563 return robust_scm2double (me->get_property ("stem-end-position"), 0);
567 Stem::flag (Grob *me)
569 int log = duration_log (me);
571 || unsmob_grob (me->get_object ("beam")))
575 TODO: maybe property stroke-style should take different values,
576 e.g. "" (i.e. no stroke), "single" and "double" (currently, it's
580 SCM flag_style_scm = me->get_property ("flag-style");
581 if (scm_is_symbol (flag_style_scm))
582 flag_style = ly_symbol2string (flag_style_scm);
584 if (flag_style == "no-flag")
589 string staffline_offs;
590 if (flag_style == "mensural")
591 /* Mensural notation: For notes on staff lines, use different
592 flags than for notes between staff lines. The idea is that
593 flags are always vertically aligned with the staff lines,
594 regardless if the note head is on a staff line or between two
595 staff lines. In other words, the inner end of a flag always
596 touches a staff line.
601 int p = (int) (rint (stem_end_position (me)));
603 = Staff_symbol_referencer::on_line (me, p) ? "0" : "1";
606 staffline_offs = "2";
611 char dir = (get_grob_direction (me) == UP) ? 'u' : 'd';
612 string font_char = flag_style
613 + to_string (dir) + staffline_offs + to_string (log);
614 Font_metric *fm = Font_interface::get_default_font (me);
615 Stencil flag = fm->find_by_name ("flags." + font_char);
616 if (flag.is_empty ())
617 me->warning (_f ("flag `%s' not found", font_char));
619 SCM stroke_style_scm = me->get_property ("stroke-style");
620 if (scm_is_string (stroke_style_scm))
622 string stroke_style = ly_scm2string (stroke_style_scm);
623 if (!stroke_style.empty ())
625 string font_char = to_string (dir) + stroke_style;
626 Stencil stroke = fm->find_by_name ("flags." + font_char);
627 if (stroke.is_empty ())
628 me->warning (_f ("flag stroke `%s' not found", font_char));
630 flag.add_stencil (stroke);
637 MAKE_SCHEME_CALLBACK (Stem, width, 1);
641 Grob *me = unsmob_grob (e);
645 if (is_invisible (me))
647 else if (unsmob_grob (me->get_object ("beam"))
648 || abs (duration_log (me)) <= 2)
650 r = Interval (-1, 1);
651 r *= thickness (me) / 2;
655 r = Interval (-1, 1) * thickness (me) * 0.5;
656 r.unite (flag (me).extent (X_AXIS));
658 return ly_interval2scm (r);
662 Stem::thickness (Grob *me)
664 return scm_to_double (me->get_property ("thickness"))
665 * Staff_symbol_referencer::line_thickness (me);
668 MAKE_SCHEME_CALLBACK (Stem, print, 1);
670 Stem::print (SCM smob)
672 Grob *me = unsmob_grob (smob);
673 Grob *beam = get_beam (me);
676 Direction d = get_grob_direction (me);
678 Real stemlet_length = robust_scm2double (me->get_property ("stemlet-length"),
680 bool stemlet = stemlet_length > 0.0;
682 /* TODO: make the stem start a direction ?
683 This is required to avoid stems passing in tablature chords. */
685 = to_boolean (me->get_property ("avoid-note-head"))
692 if (!lh && stemlet && !beam)
695 if (is_invisible (me))
698 Real y2 = robust_scm2double (me->get_property ("stem-end-position"), 0.0);
700 Real half_space = Staff_symbol_referencer::staff_space (me) * 0.5;
703 y2 = Staff_symbol_referencer::get_position (lh);
706 Real beam_translation = Beam::get_beam_translation (beam);
707 Real beam_thickness = Beam::get_thickness (beam);
708 int beam_count = beam_multiplicity (me).length () + 1;
711 * (0.5 * beam_thickness
712 + beam_translation * max (0, (beam_count - 1))
713 + stemlet_length) / half_space;
716 Interval stem_y (min (y1, y2), max (y2, y1));
718 if (Grob *head = support_head (me))
721 must not take ledgers into account.
723 Interval head_height = head->extent (head, Y_AXIS);
724 Real y_attach = Note_head::stem_attachment_coordinate (head, Y_AXIS);
726 y_attach = head_height.linear_combination (y_attach);
727 stem_y[Direction (-d)] += d * y_attach / half_space;
731 Real stem_width = thickness (me);
733 = me->layout ()->get_dimension (ly_symbol2scm ("blot-diameter"));
735 Box b = Box (Interval (-stem_width / 2, stem_width / 2),
736 Interval (stem_y[DOWN] * half_space, stem_y[UP] * half_space));
738 Stencil ss = Lookup::round_filled_box (b, blot);
739 mol.add_stencil (ss);
741 mol.add_stencil (get_translated_flag (me));
743 return mol.smobbed_copy ();
747 Stem::get_translated_flag (Grob *me)
749 Stencil fl = flag (me);
752 Direction d = get_grob_direction (me);
754 = me->layout ()->get_dimension (ly_symbol2scm ("blot-diameter"));
755 Real stem_width = thickness (me);
756 Real half_space = Staff_symbol_referencer::staff_space (me) * 0.5;
757 Real y2 = robust_scm2double (me->get_property ("stem-end-position"), 0.0);
758 fl.translate_axis (y2 * half_space - d * blot / 2, Y_AXIS);
759 fl.translate_axis (stem_width / 2, X_AXIS);
766 move the stem to right of the notehead if it is up.
768 MAKE_SCHEME_CALLBACK (Stem, offset_callback, 1);
770 Stem::offset_callback (SCM smob)
772 Grob *me = unsmob_grob (smob);
774 extract_grob_set (me, "rests", rests);
777 Grob *rest = rests.back ();
778 Real r = rest->extent (rest, X_AXIS).center ();
779 return scm_from_double (r);
783 if (Grob *f = first_head (me))
785 Interval head_wid = f->extent (f, X_AXIS);
788 if (is_invisible (me))
791 attach = Note_head::stem_attachment_coordinate (f, X_AXIS);
793 Direction d = get_grob_direction (me);
794 Real real_attach = head_wid.linear_combination (d * attach);
795 Real r = real_attach;
797 /* If not centered: correct for stem thickness. */
800 Real rule_thick = thickness (me);
801 r += -d * rule_thick * 0.5;
803 return scm_from_double (r);
806 programming_error ("Weird stem.");
807 return scm_from_double (0.0);
811 Stem::get_beam (Grob *me)
813 SCM b = me->get_object ("beam");
814 return dynamic_cast<Spanner *> (unsmob_grob (b));
818 Stem::get_stem_info (Grob *me)
821 si.dir_ = get_grob_direction (me);
823 SCM scm_info = me->get_property ("stem-info");
824 si.ideal_y_ = scm_to_double (scm_car (scm_info));
825 si.shortest_y_ = scm_to_double (scm_cadr (scm_info));
829 MAKE_SCHEME_CALLBACK (Stem, calc_stem_info, 1);
831 Stem::calc_stem_info (SCM smob)
833 Grob *me = unsmob_grob (smob);
834 Direction my_dir = get_grob_direction (me);
838 programming_error ("no stem dir set");
842 Real staff_space = Staff_symbol_referencer::staff_space (me);
843 Grob *beam = get_beam (me);
847 (void) beam->get_property ("beaming");
850 Real beam_translation = Beam::get_beam_translation (beam);
851 Real beam_thickness = Beam::get_thickness (beam);
852 int beam_count = Beam::get_direction_beam_count (beam, my_dir);
854 = robust_scm2double (me->get_property ("length-fraction"), 1.0);
856 /* Simple standard stem length */
857 SCM details = me->get_property ("details");
858 SCM lengths = scm_cdr (scm_assq (ly_symbol2scm ("beamed-lengths"), details));
861 = scm_to_double (robust_list_ref (beam_count - 1, lengths))
865 /* stem only extends to center of beam
867 - 0.5 * beam_thickness;
869 /* Condition: sane minimum free stem length (chord to beams) */
870 lengths = scm_cdr (scm_assq (ly_symbol2scm ("beamed-minimum-free-lengths"), details));
872 Real ideal_minimum_free
873 = scm_to_double (robust_list_ref (beam_count - 1, lengths))
877 Real height_of_my_trem = 0.0;
878 Grob *trem = unsmob_grob (me->get_object ("tremolo-flag"));
882 = Stem_tremolo::vertical_length (trem)
883 /* hack a bit of space around the trem. */
889 It seems that also for ideal minimum length, we must use
890 the maximum beam count (for this direction):
892 \score{ \notes\relative c''{ [a8 a32] }}
894 must be horizontal. */
895 Real height_of_my_beams = beam_thickness
896 + (beam_count - 1) * beam_translation;
898 Real ideal_minimum_length = ideal_minimum_free
901 /* stem only extends to center of beam */
902 - 0.5 * beam_thickness;
904 ideal_length = max (ideal_length, ideal_minimum_length);
906 /* Convert to Y position, calculate for dir == UP */
908 = /* staff positions */
909 head_positions (me)[my_dir] * 0.5
910 * my_dir * staff_space;
911 Real ideal_y = note_start + ideal_length;
913 /* Conditions for Y position */
915 /* Lowest beam of (UP) beam must never be lower than second staffline
919 Although this (additional) rule is probably correct,
920 I expect that highest beam (UP) should also never be lower
921 than middle staffline, just as normal stems.
925 Obviously not for grace beams.
927 Also, not for knees. Seems to be a good thing. */
928 bool no_extend_b = to_boolean (me->get_property ("no-stem-extend"));
929 bool is_knee = to_boolean (beam->get_property ("knee"));
930 if (!no_extend_b && !is_knee)
932 /* Highest beam of (UP) beam must never be lower than middle
934 ideal_y = max (ideal_y, 0.0);
935 /* Lowest beam of (UP) beam must never be lower than second staffline */
936 ideal_y = max (ideal_y, (-staff_space
937 - beam_thickness + height_of_my_beams));
940 ideal_y -= robust_scm2double (beam->get_property ("shorten"), 0);
942 SCM bemfl = scm_cdr (scm_assq (ly_symbol2scm ("beamed-extreme-minimum-free-lengths"),
946 = scm_to_double (robust_list_ref (beam_count - 1, bemfl))
950 Real minimum_length = max (minimum_free, height_of_my_trem)
952 /* stem only extends to center of beam */
953 - 0.5 * beam_thickness;
956 Real minimum_y = note_start + minimum_length;
957 Real shortest_y = minimum_y * my_dir;
959 return scm_list_2 (scm_from_double (ideal_y),
960 scm_from_double (shortest_y));
964 Stem::beam_multiplicity (Grob *stem)
966 SCM beaming = stem->get_property ("beaming");
967 Slice le = int_list_to_slice (scm_car (beaming));
968 Slice ri = int_list_to_slice (scm_cdr (beaming));
974 Stem::is_cross_staff (Grob *stem)
976 Grob *beam = unsmob_grob (stem->get_object ("beam"));
977 return beam && Beam::is_cross_staff (beam);
980 MAKE_SCHEME_CALLBACK (Stem, calc_cross_staff, 1)
982 Stem::calc_cross_staff (SCM smob)
984 return scm_from_bool (is_cross_staff (unsmob_grob (smob)));
987 /* FIXME: Too many properties */
989 "The stem represent the graphical stem. "
990 "In addition, it internally connects note heads, beams and"
992 "Rests and whole notes have invisible stems."
994 "\n\nThe following properties may be set in the details list."
996 "@item beamed-lengths \n"
997 "list of stem lengths given beam multiplicity. \n"
998 "@item beamed-minimum-free-lengths \n"
999 "list of normal minimum free stem lengths (chord to beams) given beam multiplicity.\n"
1000 "@item beamed-extreme-minimum-free-lengths\n"
1001 "list of extreme minimum free stem lengths (chord to beams) given beam multiplicity.\n"
1003 "Default stem lengths. The list gives a length for each flag-count.\n"
1004 "@item stem-shorten\n"
1005 "How much a stem in a forced direction "
1006 "should be shortened. The list gives an amount depending on the number "
1015 "default-direction "
1024 "neutral-direction "
1029 "stem-end-position "
1037 /****************************************************************/
1039 Stem_info::Stem_info ()
1041 ideal_y_ = shortest_y_ = 0;
1046 Stem_info::scale (Real x)