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);
245 if (!to_boolean (me->get_property ("cross-staff")))
247 Real len = scm_to_double (calc_length (smob)) * ss / 2;
248 Direction dir = get_grob_direction (me);
250 Interval hp = head_positions (me);
252 iv = Interval (0, len);
254 iv = Interval (-len, 0);
257 iv.translate (hp[dir] * ss / 2);
260 /* at a minimum, make the pure-height cover the staff symbol */
261 Real rad = Staff_symbol_referencer::staff_radius (me);
262 iv.add_point (-rad * ss);
263 iv.add_point (rad * ss);
265 return ly_interval2scm (iv);
268 MAKE_SCHEME_CALLBACK (Stem, calc_stem_end_position, 1)
270 Stem::calc_stem_end_position (SCM smob)
272 Grob *me = unsmob_grob (smob);
274 if (!head_count (me))
275 return scm_from_double (0.0);
277 if (Grob *beam = get_beam (me))
279 (void) beam->get_property ("quantized-positions");
280 return me->get_property ("stem-end-position");
285 /* WARNING: IN HALF SPACES */
286 Real length = robust_scm2double (me->get_property ("length"), 7);
288 Direction dir = get_grob_direction (me);
289 Interval hp = head_positions (me);
290 Real stem_end = dir ? hp[dir] + dir * length : 0;
292 /* TODO: change name to extend-stems to staff/center/'() */
293 bool no_extend_b = to_boolean (me->get_property ("no-stem-extend"));
294 if (!no_extend_b && dir * stem_end < 0)
297 return scm_from_double (stem_end);
300 /* Length is in half-spaces (or: positions) here. */
301 MAKE_SCHEME_CALLBACK (Stem, calc_length, 1)
303 Stem::calc_length (SCM smob)
305 Grob *me = unsmob_grob (smob);
307 SCM details = me->get_property ("details");
308 int durlog = duration_log (me);
310 Real ss = Staff_symbol_referencer::staff_space (me);
312 SCM s = scm_cdr (scm_assq (ly_symbol2scm ("lengths"), details));
314 length = 2 * scm_to_double (robust_list_ref (durlog - 2, s));
316 Direction dir = get_grob_direction (me);
318 /* Stems in unnatural (forced) direction should be shortened,
319 according to [Roush & Gourlay] */
320 Interval hp = head_positions (me);
321 if (dir && dir * hp[dir] >= 0)
323 SCM sshorten = scm_cdr (scm_assq (ly_symbol2scm ("stem-shorten"), details));
324 SCM scm_shorten = scm_is_pair (sshorten)
325 ? robust_list_ref (max (duration_log (me) - 2, 0), sshorten) : SCM_EOL;
326 Real shorten = 2* robust_scm2double (scm_shorten, 0);
328 /* On boundary: shorten only half */
329 if (abs (head_positions (me)[dir]) <= 1)
335 length *= robust_scm2double (me->get_property ("length-fraction"), 1.0);
338 Grob *t_flag = unsmob_grob (me->get_object ("tremolo-flag"));
339 if (t_flag && !unsmob_grob (me->get_object ("beam")))
341 /* Crude hack: add extra space if tremolo flag is there.
343 We can't do this for the beam, since we get into a loop
344 (Stem_tremolo::raw_stencil () looks at the beam.) --hwn */
347 + 2 * Stem_tremolo::vertical_length (t_flag) / ss;
349 /* We don't want to add the whole extent of the flag because the trem
350 and the flag can overlap partly. beam_translation gives a good
354 Real beam_trans = Stem_tremolo::get_beam_translation (t_flag);
355 /* the obvious choice is (durlog - 2) here, but we need a bit more space. */
356 minlen += 2 * (durlog - 1.5) * beam_trans;
358 /* up-stems need even a little more space to avoid collisions. This
359 needs to be in sync with the tremolo positioning code in
360 Stem_tremolo::print */
362 minlen += beam_trans;
364 length = max (length, minlen + 1.0);
367 return scm_from_double (length);
369 /* The log of the duration (Number of hooks on the flag minus two) */
371 Stem::duration_log (Grob *me)
373 SCM s = me->get_property ("duration-log");
374 return (scm_is_number (s)) ? scm_to_int (s) : 2;
377 MAKE_SCHEME_CALLBACK (Stem, calc_positioning_done, 1);
379 Stem::calc_positioning_done (SCM smob)
381 Grob *me = unsmob_grob (smob);
382 if (!head_count (me))
385 me->set_property ("positioning-done", SCM_BOOL_T);
387 extract_grob_set (me, "note-heads", ro_heads);
388 vector<Grob*> heads (ro_heads);
389 vector_sort (heads, position_less);
390 Direction dir = get_grob_direction (me);
395 Real thick = thickness (me);
397 Grob *hed = support_head (me);
400 programming_error ("Stem dir must be up or down.");
402 set_grob_direction (me, dir);
405 bool is_harmonic_centered = false;
406 for (vsize i = 0; i < heads.size (); i++)
407 is_harmonic_centered = is_harmonic_centered
408 || heads[i]->get_property ("style") == ly_symbol2scm ("harmonic");
409 is_harmonic_centered = is_harmonic_centered && is_invisible (me);
411 Real w = hed->extent (hed, X_AXIS)[dir];
412 for (vsize i = 0; i < heads.size (); i++)
414 Real amount = w - heads[i]->extent (heads[i], X_AXIS)[dir];
416 if (is_harmonic_centered)
418 hed->extent (hed, X_AXIS).linear_combination (CENTER)
419 - heads[i]->extent (heads[i], X_AXIS).linear_combination (CENTER);
421 heads[i]->translate_axis (amount, X_AXIS);
424 Real lastpos = Real (Staff_symbol_referencer::get_position (heads[0]));
425 for (vsize i = 1; i < heads.size (); i++)
427 Real p = Staff_symbol_referencer::get_position (heads[i]);
428 Real dy = fabs (lastpos- p);
431 dy should always be 0.5, 0.0, 1.0, but provide safety margin
438 Real ell = heads[i]->extent (heads[i], X_AXIS).length ();
440 Direction d = get_grob_direction (me);
442 Reversed head should be shifted ell-thickness, but this
443 looks too crowded, so we only shift ell-0.5*thickness.
445 This leads to assymetry: Normal heads overlap the
446 stem 100% whereas reversed heads only overlaps the
450 Real reverse_overlap = 0.5;
451 heads[i]->translate_axis ((ell - thick * reverse_overlap) * d,
454 if (is_invisible (me))
455 heads[i]->translate_axis (-thick * (2 - reverse_overlap) * d,
460 For some cases we should kern some more: when the
461 distance between the next or prev note is too large, we'd
462 get large white gaps, eg.
483 MAKE_SCHEME_CALLBACK (Stem, calc_direction, 1);
485 Stem::calc_direction (SCM smob)
487 Grob *me = unsmob_grob (smob);
488 Direction dir = CENTER;
489 if (Grob *beam = unsmob_grob (me->get_object ("beam")))
491 SCM ignore_me = beam->get_property ("direction");
493 dir = get_grob_direction (me);
497 SCM dd = me->get_property ("default-direction");
500 return me->get_property ("neutral-direction");
503 return scm_from_int (dir);
506 MAKE_SCHEME_CALLBACK (Stem, calc_default_direction, 1);
508 Stem::calc_default_direction (SCM smob)
510 Grob *me = unsmob_grob (smob);
512 Direction dir = CENTER;
513 int staff_center = 0;
514 Interval hp = head_positions (me);
517 int udistance = (int) (UP * hp[UP] - staff_center);
518 int ddistance = (int) (DOWN * hp[DOWN] - staff_center);
520 dir = Direction (sign (ddistance - udistance));
523 return scm_from_int (dir);
527 MAKE_SCHEME_CALLBACK (Stem, height, 1);
529 Stem::height (SCM smob)
531 Grob *me = unsmob_grob (smob);
532 if (!is_normal_stem (me))
533 return ly_interval2scm (Interval ());
535 Direction dir = get_grob_direction (me);
537 Grob *beam = get_beam (me);
540 /* trigger set-stem-lengths. */
541 beam->get_property ("quantized-positions");
545 Can't get_stencil (), since that would cache stencils too early.
546 This causes problems with beams.
548 Stencil *stencil = unsmob_stencil (print (smob));
549 Interval iv = stencil ? stencil->extent (Y_AXIS) : Interval ();
554 programming_error ("no stem direction");
557 iv[dir] += dir * Beam::get_thickness (beam) * 0.5;
560 return ly_interval2scm (iv);
564 Stem::stem_end_position (Grob *me)
566 return robust_scm2double (me->get_property ("stem-end-position"), 0);
570 Stem::flag (Grob *me)
572 int log = duration_log (me);
574 || unsmob_grob (me->get_object ("beam")))
577 if (!is_normal_stem (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")
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);
643 MAKE_SCHEME_CALLBACK (Stem, width, 1);
647 Grob *me = unsmob_grob (e);
651 if (is_invisible (me))
653 else if (unsmob_grob (me->get_object ("beam"))
654 || abs (duration_log (me)) <= 2)
656 r = Interval (-1, 1);
657 r *= thickness (me) / 2;
661 r = Interval (-1, 1) * thickness (me) * 0.5;
662 r.unite (flag (me).extent (X_AXIS));
664 return ly_interval2scm (r);
668 Stem::thickness (Grob *me)
670 return scm_to_double (me->get_property ("thickness"))
671 * Staff_symbol_referencer::line_thickness (me);
674 MAKE_SCHEME_CALLBACK (Stem, print, 1);
676 Stem::print (SCM smob)
678 Grob *me = unsmob_grob (smob);
679 Grob *beam = get_beam (me);
682 Direction d = get_grob_direction (me);
684 Real stemlet_length = robust_scm2double (me->get_property ("stemlet-length"),
686 bool stemlet = stemlet_length > 0.0;
688 /* TODO: make the stem start a direction ?
689 This is required to avoid stems passing in tablature chords. */
691 = to_boolean (me->get_property ("avoid-note-head"))
698 if (!lh && stemlet && !beam)
701 if (is_invisible (me))
704 Real y2 = robust_scm2double (me->get_property ("stem-end-position"), 0.0);
706 Real half_space = Staff_symbol_referencer::staff_space (me) * 0.5;
709 y2 = Staff_symbol_referencer::get_position (lh);
712 Real beam_translation = Beam::get_beam_translation (beam);
713 Real beam_thickness = Beam::get_thickness (beam);
714 int beam_count = beam_multiplicity (me).length () + 1;
717 * (0.5 * beam_thickness
718 + beam_translation * max (0, (beam_count - 1))
719 + stemlet_length) / half_space;
722 Interval stem_y (min (y1, y2), max (y2, y1));
724 if (Grob *head = support_head (me))
727 must not take ledgers into account.
729 Interval head_height = head->extent (head, Y_AXIS);
730 Real y_attach = Note_head::stem_attachment_coordinate (head, Y_AXIS);
732 y_attach = head_height.linear_combination (y_attach);
733 stem_y[Direction (-d)] += d * y_attach / half_space;
737 Real stem_width = thickness (me);
739 = me->layout ()->get_dimension (ly_symbol2scm ("blot-diameter"));
741 Box b = Box (Interval (-stem_width / 2, stem_width / 2),
742 Interval (stem_y[DOWN] * half_space, stem_y[UP] * half_space));
744 Stencil ss = Lookup::round_filled_box (b, blot);
745 mol.add_stencil (ss);
747 mol.add_stencil (get_translated_flag (me));
749 return mol.smobbed_copy ();
753 Stem::get_translated_flag (Grob *me)
755 Stencil fl = flag (me);
758 Direction d = get_grob_direction (me);
760 = me->layout ()->get_dimension (ly_symbol2scm ("blot-diameter"));
761 Real stem_width = thickness (me);
762 Real half_space = Staff_symbol_referencer::staff_space (me) * 0.5;
763 Real y2 = robust_scm2double (me->get_property ("stem-end-position"), 0.0);
764 fl.translate_axis (y2 * half_space - d * blot / 2, Y_AXIS);
765 fl.translate_axis (stem_width / 2, X_AXIS);
772 move the stem to right of the notehead if it is up.
774 MAKE_SCHEME_CALLBACK (Stem, offset_callback, 1);
776 Stem::offset_callback (SCM smob)
778 Grob *me = unsmob_grob (smob);
780 extract_grob_set (me, "rests", rests);
783 Grob *rest = rests.back ();
784 Real r = rest->extent (rest, X_AXIS).center ();
785 return scm_from_double (r);
789 if (Grob *f = first_head (me))
791 Interval head_wid = f->extent (f, X_AXIS);
794 if (is_invisible (me))
797 attach = Note_head::stem_attachment_coordinate (f, X_AXIS);
799 Direction d = get_grob_direction (me);
800 Real real_attach = head_wid.linear_combination (d * attach);
801 Real r = real_attach;
803 /* If not centered: correct for stem thickness. */
806 Real rule_thick = thickness (me);
807 r += -d * rule_thick * 0.5;
809 return scm_from_double (r);
812 programming_error ("Weird stem.");
813 return scm_from_double (0.0);
817 Stem::get_beam (Grob *me)
819 SCM b = me->get_object ("beam");
820 return dynamic_cast<Spanner *> (unsmob_grob (b));
824 Stem::get_stem_info (Grob *me)
827 si.dir_ = get_grob_direction (me);
829 SCM scm_info = me->get_property ("stem-info");
830 si.ideal_y_ = scm_to_double (scm_car (scm_info));
831 si.shortest_y_ = scm_to_double (scm_cadr (scm_info));
835 MAKE_SCHEME_CALLBACK (Stem, calc_stem_info, 1);
837 Stem::calc_stem_info (SCM smob)
839 Grob *me = unsmob_grob (smob);
840 Direction my_dir = get_grob_direction (me);
844 programming_error ("no stem dir set");
848 Real staff_space = Staff_symbol_referencer::staff_space (me);
849 Grob *beam = get_beam (me);
853 (void) beam->get_property ("beaming");
856 Real beam_translation = Beam::get_beam_translation (beam);
857 Real beam_thickness = Beam::get_thickness (beam);
858 int beam_count = Beam::get_direction_beam_count (beam, my_dir);
860 = robust_scm2double (me->get_property ("length-fraction"), 1.0);
862 /* Simple standard stem length */
863 SCM details = me->get_property ("details");
864 SCM lengths = scm_cdr (scm_assq (ly_symbol2scm ("beamed-lengths"), details));
867 = scm_to_double (robust_list_ref (beam_count - 1, lengths))
871 /* stem only extends to center of beam
873 - 0.5 * beam_thickness;
875 /* Condition: sane minimum free stem length (chord to beams) */
876 lengths = scm_cdr (scm_assq (ly_symbol2scm ("beamed-minimum-free-lengths"), details));
878 Real ideal_minimum_free
879 = scm_to_double (robust_list_ref (beam_count - 1, lengths))
883 Real height_of_my_trem = 0.0;
884 Grob *trem = unsmob_grob (me->get_object ("tremolo-flag"));
888 = Stem_tremolo::vertical_length (trem)
889 /* hack a bit of space around the trem. */
895 It seems that also for ideal minimum length, we must use
896 the maximum beam count (for this direction):
898 \score{ \notes\relative c''{ [a8 a32] }}
900 must be horizontal. */
901 Real height_of_my_beams = beam_thickness
902 + (beam_count - 1) * beam_translation;
904 Real ideal_minimum_length = ideal_minimum_free
907 /* stem only extends to center of beam */
908 - 0.5 * beam_thickness;
910 ideal_length = max (ideal_length, ideal_minimum_length);
912 /* Convert to Y position, calculate for dir == UP */
914 = /* staff positions */
915 head_positions (me)[my_dir] * 0.5
916 * my_dir * staff_space;
917 Real ideal_y = note_start + ideal_length;
919 /* Conditions for Y position */
921 /* Lowest beam of (UP) beam must never be lower than second staffline
925 Although this (additional) rule is probably correct,
926 I expect that highest beam (UP) should also never be lower
927 than middle staffline, just as normal stems.
931 Obviously not for grace beams.
933 Also, not for knees. Seems to be a good thing. */
934 bool no_extend_b = to_boolean (me->get_property ("no-stem-extend"));
935 bool is_knee = to_boolean (beam->get_property ("knee"));
936 if (!no_extend_b && !is_knee)
938 /* Highest beam of (UP) beam must never be lower than middle
940 ideal_y = max (ideal_y, 0.0);
941 /* Lowest beam of (UP) beam must never be lower than second staffline */
942 ideal_y = max (ideal_y, (-staff_space
943 - beam_thickness + height_of_my_beams));
946 ideal_y -= robust_scm2double (beam->get_property ("shorten"), 0);
948 SCM bemfl = scm_cdr (scm_assq (ly_symbol2scm ("beamed-extreme-minimum-free-lengths"),
952 = scm_to_double (robust_list_ref (beam_count - 1, bemfl))
956 Real minimum_length = max (minimum_free, height_of_my_trem)
958 /* stem only extends to center of beam */
959 - 0.5 * beam_thickness;
962 Real minimum_y = note_start + minimum_length;
963 Real shortest_y = minimum_y * my_dir;
965 return scm_list_2 (scm_from_double (ideal_y),
966 scm_from_double (shortest_y));
970 Stem::beam_multiplicity (Grob *stem)
972 SCM beaming = stem->get_property ("beaming");
973 Slice le = int_list_to_slice (scm_car (beaming));
974 Slice ri = int_list_to_slice (scm_cdr (beaming));
980 Stem::is_cross_staff (Grob *stem)
982 Grob *beam = unsmob_grob (stem->get_object ("beam"));
983 return beam && Beam::is_cross_staff (beam);
986 MAKE_SCHEME_CALLBACK (Stem, calc_cross_staff, 1)
988 Stem::calc_cross_staff (SCM smob)
990 return scm_from_bool (is_cross_staff (unsmob_grob (smob)));
993 /* FIXME: Too many properties */
995 "The stem represent the graphical stem. "
996 "In addition, it internally connects note heads, beams and"
998 "Rests and whole notes have invisible stems."
1000 "\n\nThe following properties may be set in the details list."
1002 "@item beamed-lengths \n"
1003 "list of stem lengths given beam multiplicity. \n"
1004 "@item beamed-minimum-free-lengths \n"
1005 "list of normal minimum free stem lengths (chord to beams) given beam multiplicity.\n"
1006 "@item beamed-extreme-minimum-free-lengths\n"
1007 "list of extreme minimum free stem lengths (chord to beams) given beam multiplicity.\n"
1009 "Default stem lengths. The list gives a length for each flag-count.\n"
1010 "@item stem-shorten\n"
1011 "How much a stem in a forced direction "
1012 "should be shortened. The list gives an amount depending on the number "
1021 "default-direction "
1030 "neutral-direction "
1035 "stem-end-position "
1043 /****************************************************************/
1045 Stem_info::Stem_info ()
1047 ideal_y_ = shortest_y_ = 0;
1052 Stem_info::scale (Real x)