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");
280 /* WARNING: IN HALF SPACES */
281 Real length = robust_scm2double (me->get_property ("length"), 7);
283 Direction dir = get_grob_direction (me);
284 Interval hp = head_positions (me);
285 Real stem_end = dir ? hp[dir] + dir * length : 0;
287 /* TODO: change name to extend-stems to staff/center/'() */
288 bool no_extend_b = to_boolean (me->get_property ("no-stem-extend"));
289 if (!no_extend_b && dir * stem_end < 0)
292 return scm_from_double (stem_end);
295 /* Length is in half-spaces (or: positions) here. */
296 MAKE_SCHEME_CALLBACK (Stem, calc_length, 1)
298 Stem::calc_length (SCM smob)
300 Grob *me = unsmob_grob (smob);
302 SCM details = me->get_property ("details");
303 int durlog = duration_log (me);
305 Real ss = Staff_symbol_referencer::staff_space (me);
307 SCM s = scm_cdr (scm_assq (ly_symbol2scm ("lengths"), details));
309 length = 2 * scm_to_double (robust_list_ref (durlog - 2, s));
311 Direction dir = get_grob_direction (me);
313 /* Stems in unnatural (forced) direction should be shortened,
314 according to [Roush & Gourlay] */
315 Interval hp = head_positions (me);
316 if (dir && dir * hp[dir] >= 0)
318 SCM sshorten = scm_cdr (scm_assq (ly_symbol2scm ("stem-shorten"), details));
319 SCM scm_shorten = scm_is_pair (sshorten)
320 ? robust_list_ref (max (duration_log (me) - 2, 0), sshorten) : SCM_EOL;
321 Real shorten = 2* robust_scm2double (scm_shorten, 0);
323 /* On boundary: shorten only half */
324 if (abs (head_positions (me)[dir]) <= 1)
330 length *= robust_scm2double (me->get_property ("length-fraction"), 1.0);
333 Grob *t_flag = unsmob_grob (me->get_object ("tremolo-flag"));
334 if (t_flag && !unsmob_grob (me->get_object ("beam")))
336 /* Crude hack: add extra space if tremolo flag is there.
338 We can't do this for the beam, since we get into a loop
339 (Stem_tremolo::raw_stencil () looks at the beam.) --hwn */
342 + 2 * Stem_tremolo::vertical_length (t_flag) / ss;
344 /* We don't want to add the whole extent of the flag because the trem
345 and the flag can overlap partly. beam_translation gives a good
349 Real beam_trans = Stem_tremolo::get_beam_translation (t_flag);
350 /* the obvious choice is (durlog - 2) here, but we need a bit more space. */
351 minlen += 2 * (durlog - 1.5) * beam_trans;
353 /* up-stems need even a little more space to avoid collisions. This
354 needs to be in sync with the tremolo positioning code in
355 Stem_tremolo::print */
357 minlen += beam_trans;
359 length = max (length, minlen + 1.0);
362 return scm_from_double (length);
364 /* The log of the duration (Number of hooks on the flag minus two) */
366 Stem::duration_log (Grob *me)
368 SCM s = me->get_property ("duration-log");
369 return (scm_is_number (s)) ? scm_to_int (s) : 2;
372 MAKE_SCHEME_CALLBACK (Stem, calc_positioning_done, 1);
374 Stem::calc_positioning_done (SCM smob)
376 Grob *me = unsmob_grob (smob);
377 if (!head_count (me))
380 me->set_property ("positioning-done", SCM_BOOL_T);
382 extract_grob_set (me, "note-heads", ro_heads);
383 vector<Grob*> heads (ro_heads);
384 vector_sort (heads, position_less);
385 Direction dir = get_grob_direction (me);
390 Real thick = thickness (me);
392 Grob *hed = support_head (me);
395 programming_error ("Stem dir must be up or down.");
397 set_grob_direction (me, dir);
400 bool is_harmonic_centered = false;
401 for (vsize i = 0; i < heads.size (); i++)
402 is_harmonic_centered = is_harmonic_centered
403 || heads[i]->get_property ("style") == ly_symbol2scm ("harmonic");
404 is_harmonic_centered = is_harmonic_centered && is_invisible (me);
406 Real w = hed->extent (hed, X_AXIS)[dir];
407 for (vsize i = 0; i < heads.size (); i++)
409 Real amount = w - heads[i]->extent (heads[i], X_AXIS)[dir];
411 if (is_harmonic_centered)
413 hed->extent (hed, X_AXIS).linear_combination (CENTER)
414 - heads[i]->extent (heads[i], X_AXIS).linear_combination (CENTER);
416 heads[i]->translate_axis (amount, X_AXIS);
419 Real lastpos = Real (Staff_symbol_referencer::get_position (heads[0]));
420 for (vsize i = 1; i < heads.size (); i++)
422 Real p = Staff_symbol_referencer::get_position (heads[i]);
423 Real dy = fabs (lastpos- p);
426 dy should always be 0.5, 0.0, 1.0, but provide safety margin
433 Real ell = heads[i]->extent (heads[i], X_AXIS).length ();
435 Direction d = get_grob_direction (me);
437 Reversed head should be shifted ell-thickness, but this
438 looks too crowded, so we only shift ell-0.5*thickness.
440 This leads to assymetry: Normal heads overlap the
441 stem 100% whereas reversed heads only overlaps the
445 Real reverse_overlap = 0.5;
446 heads[i]->translate_axis ((ell - thick * reverse_overlap) * d,
449 if (is_invisible (me))
450 heads[i]->translate_axis (-thick * (2 - reverse_overlap) * d,
455 For some cases we should kern some more: when the
456 distance between the next or prev note is too large, we'd
457 get large white gaps, eg.
478 MAKE_SCHEME_CALLBACK (Stem, calc_direction, 1);
480 Stem::calc_direction (SCM smob)
482 Grob *me = unsmob_grob (smob);
483 Direction dir = CENTER;
484 if (Grob *beam = unsmob_grob (me->get_object ("beam")))
486 SCM ignore_me = beam->get_property ("direction");
488 dir = get_grob_direction (me);
492 SCM dd = me->get_property ("default-direction");
495 return me->get_property ("neutral-direction");
498 return scm_from_int (dir);
501 MAKE_SCHEME_CALLBACK (Stem, calc_default_direction, 1);
503 Stem::calc_default_direction (SCM smob)
505 Grob *me = unsmob_grob (smob);
507 Direction dir = CENTER;
508 int staff_center = 0;
509 Interval hp = head_positions (me);
512 int udistance = (int) (UP * hp[UP] - staff_center);
513 int ddistance = (int) (DOWN * hp[DOWN] - staff_center);
515 dir = Direction (sign (ddistance - udistance));
518 return scm_from_int (dir);
522 MAKE_SCHEME_CALLBACK (Stem, height, 1);
524 Stem::height (SCM smob)
526 Grob *me = unsmob_grob (smob);
527 if (!is_normal_stem (me))
528 return ly_interval2scm (Interval ());
530 Direction dir = get_grob_direction (me);
532 Grob *beam = get_beam (me);
535 /* trigger set-stem-lengths. */
536 beam->get_property ("quantized-positions");
540 Can't get_stencil (), since that would cache stencils too early.
541 This causes problems with beams.
543 Stencil *stencil = unsmob_stencil (print (smob));
544 Interval iv = stencil ? stencil->extent (Y_AXIS) : Interval ();
549 programming_error ("no stem direction");
552 iv[dir] += dir * Beam::get_thickness (beam) * 0.5;
555 return ly_interval2scm (iv);
559 Stem::stem_end_position (Grob *me)
561 return robust_scm2double (me->get_property ("stem-end-position"), 0);
565 Stem::flag (Grob *me)
567 int log = duration_log (me);
569 || unsmob_grob (me->get_object ("beam")))
572 if (!is_normal_stem (me))
576 TODO: maybe property stroke-style should take different values,
577 e.g. "" (i.e. no stroke), "single" and "double" (currently, it's
581 SCM flag_style_scm = me->get_property ("flag-style");
582 if (scm_is_symbol (flag_style_scm))
583 flag_style = ly_symbol2string (flag_style_scm);
585 if (flag_style == "no-flag")
590 string staffline_offs;
591 if (flag_style == "mensural")
592 /* Mensural notation: For notes on staff lines, use different
593 flags than for notes between staff lines. The idea is that
594 flags are always vertically aligned with the staff lines,
595 regardless if the note head is on a staff line or between two
596 staff lines. In other words, the inner end of a flag always
597 touches a staff line.
602 int p = (int) (rint (stem_end_position (me)));
604 = Staff_symbol_referencer::on_line (me, p) ? "0" : "1";
607 staffline_offs = "2";
612 char dir = (get_grob_direction (me) == UP) ? 'u' : 'd';
613 string font_char = flag_style
614 + to_string (dir) + staffline_offs + to_string (log);
615 Font_metric *fm = Font_interface::get_default_font (me);
616 Stencil flag = fm->find_by_name ("flags." + font_char);
617 if (flag.is_empty ())
618 me->warning (_f ("flag `%s' not found", font_char));
620 SCM stroke_style_scm = me->get_property ("stroke-style");
621 if (scm_is_string (stroke_style_scm))
623 string stroke_style = ly_scm2string (stroke_style_scm);
624 if (!stroke_style.empty ())
626 string font_char = to_string (dir) + stroke_style;
627 Stencil stroke = fm->find_by_name ("flags." + font_char);
628 if (stroke.is_empty ())
629 me->warning (_f ("flag stroke `%s' not found", font_char));
631 flag.add_stencil (stroke);
638 MAKE_SCHEME_CALLBACK (Stem, width, 1);
642 Grob *me = unsmob_grob (e);
646 if (is_invisible (me))
648 else if (unsmob_grob (me->get_object ("beam"))
649 || abs (duration_log (me)) <= 2)
651 r = Interval (-1, 1);
652 r *= thickness (me) / 2;
656 r = Interval (-1, 1) * thickness (me) * 0.5;
657 r.unite (flag (me).extent (X_AXIS));
659 return ly_interval2scm (r);
663 Stem::thickness (Grob *me)
665 return scm_to_double (me->get_property ("thickness"))
666 * Staff_symbol_referencer::line_thickness (me);
669 MAKE_SCHEME_CALLBACK (Stem, print, 1);
671 Stem::print (SCM smob)
673 Grob *me = unsmob_grob (smob);
674 Grob *beam = get_beam (me);
677 Direction d = get_grob_direction (me);
679 Real stemlet_length = robust_scm2double (me->get_property ("stemlet-length"),
681 bool stemlet = stemlet_length > 0.0;
683 /* TODO: make the stem start a direction ?
684 This is required to avoid stems passing in tablature chords. */
686 = to_boolean (me->get_property ("avoid-note-head"))
693 if (!lh && stemlet && !beam)
696 if (is_invisible (me))
699 Real y2 = robust_scm2double (me->get_property ("stem-end-position"), 0.0);
701 Real half_space = Staff_symbol_referencer::staff_space (me) * 0.5;
704 y2 = Staff_symbol_referencer::get_position (lh);
707 Real beam_translation = Beam::get_beam_translation (beam);
708 Real beam_thickness = Beam::get_thickness (beam);
709 int beam_count = beam_multiplicity (me).length () + 1;
712 * (0.5 * beam_thickness
713 + beam_translation * max (0, (beam_count - 1))
714 + stemlet_length) / half_space;
717 Interval stem_y (min (y1, y2), max (y2, y1));
719 if (Grob *head = support_head (me))
722 must not take ledgers into account.
724 Interval head_height = head->extent (head, Y_AXIS);
725 Real y_attach = Note_head::stem_attachment_coordinate (head, Y_AXIS);
727 y_attach = head_height.linear_combination (y_attach);
728 stem_y[Direction (-d)] += d * y_attach / half_space;
732 Real stem_width = thickness (me);
734 = me->layout ()->get_dimension (ly_symbol2scm ("blot-diameter"));
736 Box b = Box (Interval (-stem_width / 2, stem_width / 2),
737 Interval (stem_y[DOWN] * half_space, stem_y[UP] * half_space));
739 Stencil ss = Lookup::round_filled_box (b, blot);
740 mol.add_stencil (ss);
742 mol.add_stencil (get_translated_flag (me));
744 return mol.smobbed_copy ();
748 Stem::get_translated_flag (Grob *me)
750 Stencil fl = flag (me);
753 Direction d = get_grob_direction (me);
755 = me->layout ()->get_dimension (ly_symbol2scm ("blot-diameter"));
756 Real stem_width = thickness (me);
757 Real half_space = Staff_symbol_referencer::staff_space (me) * 0.5;
758 Real y2 = robust_scm2double (me->get_property ("stem-end-position"), 0.0);
759 fl.translate_axis (y2 * half_space - d * blot / 2, Y_AXIS);
760 fl.translate_axis (stem_width / 2, X_AXIS);
767 move the stem to right of the notehead if it is up.
769 MAKE_SCHEME_CALLBACK (Stem, offset_callback, 1);
771 Stem::offset_callback (SCM smob)
773 Grob *me = unsmob_grob (smob);
775 extract_grob_set (me, "rests", rests);
778 Grob *rest = rests.back ();
779 Real r = rest->extent (rest, X_AXIS).center ();
780 return scm_from_double (r);
784 if (Grob *f = first_head (me))
786 Interval head_wid = f->extent (f, X_AXIS);
789 if (is_invisible (me))
792 attach = Note_head::stem_attachment_coordinate (f, X_AXIS);
794 Direction d = get_grob_direction (me);
795 Real real_attach = head_wid.linear_combination (d * attach);
796 Real r = real_attach;
798 /* If not centered: correct for stem thickness. */
801 Real rule_thick = thickness (me);
802 r += -d * rule_thick * 0.5;
804 return scm_from_double (r);
807 programming_error ("Weird stem.");
808 return scm_from_double (0.0);
812 Stem::get_beam (Grob *me)
814 SCM b = me->get_object ("beam");
815 return dynamic_cast<Spanner *> (unsmob_grob (b));
819 Stem::get_stem_info (Grob *me)
822 si.dir_ = get_grob_direction (me);
824 SCM scm_info = me->get_property ("stem-info");
825 si.ideal_y_ = scm_to_double (scm_car (scm_info));
826 si.shortest_y_ = scm_to_double (scm_cadr (scm_info));
830 MAKE_SCHEME_CALLBACK (Stem, calc_stem_info, 1);
832 Stem::calc_stem_info (SCM smob)
834 Grob *me = unsmob_grob (smob);
835 Direction my_dir = get_grob_direction (me);
839 programming_error ("no stem dir set");
843 Real staff_space = Staff_symbol_referencer::staff_space (me);
844 Grob *beam = get_beam (me);
848 (void) beam->get_property ("beaming");
851 Real beam_translation = Beam::get_beam_translation (beam);
852 Real beam_thickness = Beam::get_thickness (beam);
853 int beam_count = Beam::get_direction_beam_count (beam, my_dir);
855 = robust_scm2double (me->get_property ("length-fraction"), 1.0);
857 /* Simple standard stem length */
858 SCM details = me->get_property ("details");
859 SCM lengths = scm_cdr (scm_assq (ly_symbol2scm ("beamed-lengths"), details));
862 = scm_to_double (robust_list_ref (beam_count - 1, lengths))
866 /* stem only extends to center of beam
868 - 0.5 * beam_thickness;
870 /* Condition: sane minimum free stem length (chord to beams) */
871 lengths = scm_cdr (scm_assq (ly_symbol2scm ("beamed-minimum-free-lengths"), details));
873 Real ideal_minimum_free
874 = scm_to_double (robust_list_ref (beam_count - 1, lengths))
878 Real height_of_my_trem = 0.0;
879 Grob *trem = unsmob_grob (me->get_object ("tremolo-flag"));
883 = Stem_tremolo::vertical_length (trem)
884 /* hack a bit of space around the trem. */
890 It seems that also for ideal minimum length, we must use
891 the maximum beam count (for this direction):
893 \score{ \notes\relative c''{ [a8 a32] }}
895 must be horizontal. */
896 Real height_of_my_beams = beam_thickness
897 + (beam_count - 1) * beam_translation;
899 Real ideal_minimum_length = ideal_minimum_free
902 /* stem only extends to center of beam */
903 - 0.5 * beam_thickness;
905 ideal_length = max (ideal_length, ideal_minimum_length);
907 /* Convert to Y position, calculate for dir == UP */
909 = /* staff positions */
910 head_positions (me)[my_dir] * 0.5
911 * my_dir * staff_space;
912 Real ideal_y = note_start + ideal_length;
914 /* Conditions for Y position */
916 /* Lowest beam of (UP) beam must never be lower than second staffline
920 Although this (additional) rule is probably correct,
921 I expect that highest beam (UP) should also never be lower
922 than middle staffline, just as normal stems.
926 Obviously not for grace beams.
928 Also, not for knees. Seems to be a good thing. */
929 bool no_extend_b = to_boolean (me->get_property ("no-stem-extend"));
930 bool is_knee = to_boolean (beam->get_property ("knee"));
931 if (!no_extend_b && !is_knee)
933 /* Highest beam of (UP) beam must never be lower than middle
935 ideal_y = max (ideal_y, 0.0);
936 /* Lowest beam of (UP) beam must never be lower than second staffline */
937 ideal_y = max (ideal_y, (-staff_space
938 - beam_thickness + height_of_my_beams));
941 ideal_y -= robust_scm2double (beam->get_property ("shorten"), 0);
943 SCM bemfl = scm_cdr (scm_assq (ly_symbol2scm ("beamed-extreme-minimum-free-lengths"),
947 = scm_to_double (robust_list_ref (beam_count - 1, bemfl))
951 Real minimum_length = max (minimum_free, height_of_my_trem)
953 /* stem only extends to center of beam */
954 - 0.5 * beam_thickness;
957 Real minimum_y = note_start + minimum_length;
958 Real shortest_y = minimum_y * my_dir;
960 return scm_list_2 (scm_from_double (ideal_y),
961 scm_from_double (shortest_y));
965 Stem::beam_multiplicity (Grob *stem)
967 SCM beaming = stem->get_property ("beaming");
968 Slice le = int_list_to_slice (scm_car (beaming));
969 Slice ri = int_list_to_slice (scm_cdr (beaming));
975 Stem::is_cross_staff (Grob *stem)
977 Grob *beam = unsmob_grob (stem->get_object ("beam"));
978 return beam && Beam::is_cross_staff (beam);
981 MAKE_SCHEME_CALLBACK (Stem, calc_cross_staff, 1)
983 Stem::calc_cross_staff (SCM smob)
985 return scm_from_bool (is_cross_staff (unsmob_grob (smob)));
988 /* FIXME: Too many properties */
990 "The stem represent the graphical stem. "
991 "In addition, it internally connects note heads, beams and"
993 "Rests and whole notes have invisible stems."
995 "\n\nThe following properties may be set in the details list."
997 "@item beamed-lengths \n"
998 "list of stem lengths given beam multiplicity. \n"
999 "@item beamed-minimum-free-lengths \n"
1000 "list of normal minimum free stem lengths (chord to beams) given beam multiplicity.\n"
1001 "@item beamed-extreme-minimum-free-lengths\n"
1002 "list of extreme minimum free stem lengths (chord to beams) given beam multiplicity.\n"
1004 "Default stem lengths. The list gives a length for each flag-count.\n"
1005 "@item stem-shorten\n"
1006 "How much a stem in a forced direction "
1007 "should be shortened. The list gives an amount depending on the number "
1016 "default-direction "
1025 "neutral-direction "
1030 "stem-end-position "
1038 /****************************************************************/
1040 Stem_info::Stem_info ()
1042 ideal_y_ = shortest_y_ = 0;
1047 Stem_info::scale (Real x)