2 stem.cc -- implement Stem
4 source file of the GNU LilyPond music typesetter
6 (c) 1996--2006 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 = 1000000;
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 Real stemlet_length = robust_scm2double (me->get_property ("stemlet-length"),
220 return !((head_count (me)
221 || stemlet_length > 0.0)
222 && scm_to_int (me->get_property ("duration-log")) >= 1);
225 MAKE_SCHEME_CALLBACK (Stem, pure_height, 3)
227 Stem::pure_height (SCM smob, SCM start, SCM end)
233 Grob *me = unsmob_grob (smob);
234 Real ss = Staff_symbol_referencer::staff_space (me);
235 Real len = scm_to_double (calc_length (smob)) * ss / 2;
236 Direction dir = get_grob_direction (me);
239 Interval hp = head_positions (me);
241 iv = Interval (0, len);
243 iv = Interval (-len, 0);
246 iv.translate (hp[dir] * ss / 2);
248 return ly_interval2scm (iv);
251 MAKE_SCHEME_CALLBACK (Stem, calc_stem_end_position, 1)
253 Stem::calc_stem_end_position (SCM smob)
255 Grob *me = unsmob_grob (smob);
257 if (!head_count (me))
258 return scm_from_double (0.0);
260 if (Grob *beam = get_beam (me))
262 (void) beam->get_property ("quantized-positions");
263 return me->get_property ("stem-end-position");
266 Real ss = Staff_symbol_referencer::staff_space (me);
267 int durlog = duration_log (me);
270 /* WARNING: IN HALF SPACES */
271 Real length = robust_scm2double (me->get_property ("length"), 7);
273 Direction dir = get_grob_direction (me);
274 Interval hp = head_positions (me);
275 Real stem_end = dir ? hp[dir] + dir * length : 0;
277 /* TODO: change name to extend-stems to staff/center/'() */
278 bool no_extend_b = to_boolean (me->get_property ("no-stem-extend"));
279 if (!no_extend_b && dir * stem_end < 0)
283 /* Make a little room if we have a upflag and there is a dot.
284 previous approach was to lengthen the stem. This is not
285 good typesetting practice. */
286 if (!get_beam (me) && dir == UP
289 Grob *closest_to_flag = extremal_heads (me)[dir];
290 Grob *dots = closest_to_flag
291 ? Rhythmic_head::get_dots (closest_to_flag) : 0;
295 Real dp = Staff_symbol_referencer::get_position (dots);
296 Interval flag_yext = flag (me).extent (Y_AXIS) * (2 / ss) + stem_end;
298 /* Very gory: add myself to the X-support of the parent,
299 which should be a dot-column. */
301 if (flag_yext.distance (dp) < 0.5)
303 Grob *par = dots->get_parent (X_AXIS);
305 if (Dot_column::has_interface (par))
307 Side_position_interface::add_support (par, me);
309 /* TODO: apply some better logic here. The flag is
310 curved inwards, so this will typically be too
317 return scm_from_double (stem_end);
321 MAKE_SCHEME_CALLBACK (Stem, calc_length, 1)
323 Stem::calc_length (SCM smob)
325 Grob *me = unsmob_grob (smob);
327 SCM details = me->get_property ("details");
328 int durlog = duration_log (me);
330 Real ss = Staff_symbol_referencer::staff_space (me);
332 SCM s = scm_cdr (scm_assq (ly_symbol2scm ("lengths"), details));
334 length = 2 * scm_to_double (robust_list_ref (durlog - 2, s));
336 Direction dir = get_grob_direction (me);
338 /* Stems in unnatural (forced) direction should be shortened,
339 according to [Roush & Gourlay] */
340 Interval hp = head_positions (me);
341 if (dir && dir * hp[dir] >= 0)
343 SCM sshorten = scm_cdr (scm_assq (ly_symbol2scm ("stem-shorten"), details));
344 SCM scm_shorten = scm_is_pair (sshorten)
345 ? robust_list_ref (max (duration_log (me) - 2, 0), sshorten) : SCM_EOL;
346 Real shorten = 2* robust_scm2double (scm_shorten, 0);
348 /* On boundary: shorten only half */
349 if (abs (head_positions (me)[dir]) <= 1)
355 length *= robust_scm2double (me->get_property ("length-fraction"), 1.0);
358 Grob *t_flag = unsmob_grob (me->get_object ("tremolo-flag"));
359 if (t_flag && !unsmob_grob (me->get_object ("beam")))
361 /* Crude hack: add extra space if tremolo flag is there.
363 We can't do this for the beam, since we get into a loop
364 (Stem_tremolo::raw_stencil () looks at the beam.) --hwn */
367 + 2 * t_flag->extent (t_flag, Y_AXIS).length ()
370 /* We don't want to add the whole extent of the flag because the trem
371 and the flag can overlap partly. beam_translation gives a good
375 Real beam_trans = Stem_tremolo::get_beam_translation (t_flag);
376 /* the obvious choice is (durlog - 2) here, but we need a bit more space. */
377 minlen += 2 * (durlog - 1.5) * beam_trans;
379 /* up-stems need even a little more space to avoid collisions. This
380 needs to be in sync with the tremolo positioning code in
381 Stem_tremolo::print */
383 minlen += beam_trans;
385 length = max (length, minlen + 1.0);
388 return scm_from_double (length);
390 /* The log of the duration (Number of hooks on the flag minus two) */
392 Stem::duration_log (Grob *me)
394 SCM s = me->get_property ("duration-log");
395 return (scm_is_number (s)) ? scm_to_int (s) : 2;
398 MAKE_SCHEME_CALLBACK(Stem, calc_positioning_done, 1);
400 Stem::calc_positioning_done (SCM smob)
402 Grob *me = unsmob_grob (smob);
403 if (!head_count (me))
406 extract_grob_set (me, "note-heads", ro_heads);
407 vector<Grob*> heads (ro_heads);
408 vector_sort (heads, position_less);
409 Direction dir = get_grob_direction (me);
414 Real thick = thickness (me);
416 Grob *hed = support_head (me);
419 programming_error ("Stem dir must be up or down.");
421 set_grob_direction (me, dir);
424 bool is_harmonic_centered = false;
425 for (vsize i = 0; i < heads.size (); i++)
426 is_harmonic_centered = is_harmonic_centered
427 || heads[i]->get_property ("style") == ly_symbol2scm ("harmonic");
428 is_harmonic_centered = is_harmonic_centered && is_invisible (me);
430 Real w = hed->extent (hed, X_AXIS)[dir];
431 for (vsize i = 0; i < heads.size (); i++)
433 Real amount = w - heads[i]->extent (heads[i], X_AXIS)[dir];
435 if (is_harmonic_centered)
437 hed->extent (hed, X_AXIS).linear_combination (CENTER)
438 - heads[i]->extent (heads[i], X_AXIS).linear_combination (CENTER);
440 heads[i]->translate_axis (amount, X_AXIS);
443 Real lastpos = Real (Staff_symbol_referencer::get_position (heads[0]));
444 for (vsize i = 1; i < heads.size (); i++)
446 Real p = Staff_symbol_referencer::get_position (heads[i]);
447 Real dy = fabs (lastpos- p);
450 dy should always be 0.5, 0.0, 1.0, but provide safety margin
457 Real ell = heads[i]->extent (heads[i], X_AXIS).length ();
459 Direction d = get_grob_direction (me);
461 Reversed head should be shifted ell-thickness, but this
462 looks too crowded, so we only shift ell-0.5*thickness.
464 This leads to assymetry: Normal heads overlap the
465 stem 100% whereas reversed heads only overlaps the
469 Real reverse_overlap = 0.5;
470 heads[i]->translate_axis ((ell - thick * reverse_overlap) * d,
473 if (is_invisible (me))
474 heads[i]->translate_axis (-thick * (2 - reverse_overlap) * d,
479 For some cases we should kern some more: when the
480 distance between the next or prev note is too large, we'd
481 get large white gaps, eg.
502 MAKE_SCHEME_CALLBACK(Stem, calc_direction, 1);
504 Stem::calc_direction (SCM smob)
506 Grob *me = unsmob_grob (smob);
507 Direction dir = CENTER;
508 if (Grob *beam = unsmob_grob (me->get_object ("beam")))
510 SCM ignore_me = beam->get_property ("direction");
512 dir = get_grob_direction (me);
516 SCM dd = me->get_property ("default-direction");
519 return me->get_property ("neutral-direction");
522 return scm_from_int (dir);
525 MAKE_SCHEME_CALLBACK(Stem, calc_default_direction, 1);
527 Stem::calc_default_direction (SCM smob)
529 Grob *me = unsmob_grob (smob);
531 Direction dir = CENTER;
532 int staff_center = 0;
533 Interval hp = head_positions (me);
536 int udistance = (int) (UP * hp[UP] - staff_center);
537 int ddistance = (int) (DOWN * hp[DOWN] - staff_center);
539 dir = Direction (sign (ddistance - udistance));
542 return scm_from_int (dir);
546 MAKE_SCHEME_CALLBACK (Stem, height, 1);
548 Stem::height (SCM smob)
550 Grob *me = unsmob_grob (smob);
552 Direction dir = get_grob_direction (me);
556 UGH. Should be automatic
558 Grob *beam = get_beam (me);
561 /* trigger set-stem-lengths. */
562 beam->get_property ("quantized-positions");
566 Can't get_stencil(), since that would cache stencils too early.
567 This causes problems with beams.
569 Stencil *stencil = unsmob_stencil (print (smob));
570 Interval iv = stencil ? stencil->extent (Y_AXIS) : Interval();
575 programming_error ("no stem direction");
578 iv[dir] += dir * Beam::get_thickness (beam) * 0.5;
581 return ly_interval2scm (iv);
585 Stem::stem_end_position (Grob *me)
587 return robust_scm2double (me->get_property ("stem-end-position"), 0);
591 Stem::flag (Grob *me)
593 int log = duration_log (me);
595 || unsmob_grob (me->get_object ("beam")))
599 TODO: maybe property stroke-style should take different values,
600 e.g. "" (i.e. no stroke), "single" and "double" (currently, it's
604 SCM flag_style_scm = me->get_property ("flag-style");
605 if (scm_is_symbol (flag_style_scm))
606 flag_style = ly_symbol2string (flag_style_scm);
608 if (flag_style == "no-flag")
613 string staffline_offs;
614 if (flag_style == "mensural")
615 /* Mensural notation: For notes on staff lines, use different
616 flags than for notes between staff lines. The idea is that
617 flags are always vertically aligned with the staff lines,
618 regardless if the note head is on a staff line or between two
619 staff lines. In other words, the inner end of a flag always
620 touches a staff line.
625 int p = (int) (rint (stem_end_position (me)));
627 = Staff_symbol_referencer::on_line (me, p) ? "0" : "1";
630 staffline_offs = "2";
635 char dir = (get_grob_direction (me) == UP) ? 'u' : 'd';
636 string font_char = flag_style
637 + to_string (dir) + staffline_offs + to_string (log);
638 Font_metric *fm = Font_interface::get_default_font (me);
639 Stencil flag = fm->find_by_name ("flags." + font_char);
640 if (flag.is_empty ())
641 me->warning (_f ("flag `%s' not found", font_char));
643 SCM stroke_style_scm = me->get_property ("stroke-style");
644 if (scm_is_string (stroke_style_scm))
646 string stroke_style = ly_scm2string (stroke_style_scm);
647 if (!stroke_style.empty ())
649 string font_char = to_string (dir) + stroke_style;
650 Stencil stroke = fm->find_by_name ("flags." + font_char);
651 if (stroke.is_empty ())
652 me->warning (_f ("flag stroke `%s' not found", font_char));
654 flag.add_stencil (stroke);
661 MAKE_SCHEME_CALLBACK (Stem, width, 1);
665 Grob *me = unsmob_grob (e);
669 if (is_invisible (me))
671 else if (unsmob_grob (me->get_object ("beam"))
672 || abs (duration_log (me)) <= 2)
674 r = Interval (-1, 1);
675 r *= thickness (me) / 2;
679 r = Interval (-1, 1) * thickness (me) * 0.5;
680 r.unite (flag (me).extent (X_AXIS));
682 return ly_interval2scm (r);
686 Stem::thickness (Grob *me)
688 return scm_to_double (me->get_property ("thickness"))
689 * Staff_symbol_referencer::line_thickness (me);
692 MAKE_SCHEME_CALLBACK (Stem, print, 1);
694 Stem::print (SCM smob)
696 Grob *me = unsmob_grob (smob);
697 Grob *beam = get_beam (me);
700 Direction d = get_grob_direction (me);
702 Real stemlet_length = robust_scm2double (me->get_property ("stemlet-length"),
704 bool stemlet = stemlet_length > 0.0;
706 /* TODO: make the stem start a direction ?
707 This is required to avoid stems passing in tablature chords. */
709 = to_boolean (me->get_property ("avoid-note-head"))
716 if (!lh && stemlet && !beam)
719 if (is_invisible (me))
722 Real y2 = robust_scm2double (me->get_property ("stem-end-position"), 0.0);
724 Real half_space = Staff_symbol_referencer::staff_space (me) * 0.5;
727 y2 = Staff_symbol_referencer::get_position (lh);
730 Real beam_translation = Beam::get_beam_translation (beam);
731 Real beam_thickness = Beam::get_thickness (beam);
732 int beam_count = beam_multiplicity (me).length () + 1;
735 * (0.5 * beam_thickness
736 + beam_translation * max (0, (beam_count - 1))
737 + stemlet_length) / half_space;
740 Interval stem_y (min (y1, y2), max (y2, y1));
742 if (Grob *head = support_head (me))
745 must not take ledgers into account.
747 Interval head_height = head->extent (head, Y_AXIS);
748 Real y_attach = Note_head::stem_attachment_coordinate (head, Y_AXIS);
750 y_attach = head_height.linear_combination (y_attach);
751 stem_y[Direction (-d)] += d * y_attach / half_space;
755 Real stem_width = thickness (me);
757 = me->layout ()->get_dimension (ly_symbol2scm ("blot-diameter"));
759 Box b = Box (Interval (-stem_width / 2, stem_width / 2),
760 Interval (stem_y[DOWN] * half_space, stem_y[UP] * half_space));
762 Stencil ss = Lookup::round_filled_box (b, blot);
763 mol.add_stencil (ss);
765 mol.add_stencil (get_translated_flag (me));
767 return mol.smobbed_copy ();
771 Stem::get_translated_flag (Grob *me)
773 Stencil fl = flag (me);
776 Direction d = get_grob_direction (me);
778 = me->layout ()->get_dimension (ly_symbol2scm ("blot-diameter"));
779 Real stem_width = thickness (me);
780 Real half_space = Staff_symbol_referencer::staff_space (me) * 0.5;
781 Real y2 = robust_scm2double (me->get_property ("stem-end-position"), 0.0);
782 fl.translate_axis (y2 * half_space - d * blot / 2, Y_AXIS);
783 fl.translate_axis (stem_width / 2, X_AXIS);
790 move the stem to right of the notehead if it is up.
792 MAKE_SCHEME_CALLBACK (Stem, offset_callback, 1);
794 Stem::offset_callback (SCM smob)
796 Grob *me = unsmob_grob (smob);
799 if (Grob *f = first_head (me))
801 Interval head_wid = f->extent (f, X_AXIS);
804 if (is_invisible (me))
807 attach = Note_head::stem_attachment_coordinate (f, X_AXIS);
809 Direction d = get_grob_direction (me);
810 Real real_attach = head_wid.linear_combination (d * attach);
813 /* If not centered: correct for stem thickness. */
816 Real rule_thick = thickness (me);
817 r += -d * rule_thick * 0.5;
822 extract_grob_set (me, "rests", rests);
825 Grob *rest = rests.back ();
826 r = rest->extent (rest, X_AXIS).center ();
829 return scm_from_double (r);
833 Stem::get_beam (Grob *me)
835 SCM b = me->get_object ("beam");
836 return dynamic_cast<Spanner *> (unsmob_grob (b));
840 Stem::get_stem_info (Grob *me)
843 si.dir_ = get_grob_direction (me);
845 SCM scm_info = me->get_property ("stem-info");
846 si.ideal_y_ = scm_to_double (scm_car (scm_info));
847 si.shortest_y_ = scm_to_double (scm_cadr (scm_info));
851 MAKE_SCHEME_CALLBACK(Stem, calc_stem_info, 1);
853 Stem::calc_stem_info (SCM smob)
855 Grob *me = unsmob_grob (smob);
856 Direction my_dir = get_grob_direction (me);
860 programming_error ("no stem dir set");
864 Real staff_space = Staff_symbol_referencer::staff_space (me);
865 Grob *beam = get_beam (me);
869 (void) beam->get_property ("beaming");
872 Real beam_translation = Beam::get_beam_translation (beam);
873 Real beam_thickness = Beam::get_thickness (beam);
874 int beam_count = Beam::get_direction_beam_count (beam, my_dir);
876 = robust_scm2double (me->get_property ("length-fraction"), 1.0);
878 /* Simple standard stem length */
879 SCM details = me->get_property ("details");
880 SCM lengths = scm_cdr (scm_assq (ly_symbol2scm ("beamed-lengths"), details));
883 = scm_to_double (robust_list_ref (beam_count - 1, lengths))
887 /* stem only extends to center of beam
889 - 0.5 * beam_thickness;
891 /* Condition: sane minimum free stem length (chord to beams) */
892 lengths = scm_cdr (scm_assq (ly_symbol2scm ("beamed-minimum-free-lengths"), details));
894 Real ideal_minimum_free
895 = scm_to_double (robust_list_ref (beam_count - 1, lengths))
899 Real height_of_my_trem = 0.0;
900 Grob *trem = unsmob_grob (me->get_object ("tremolo-flag"));
902 height_of_my_trem = trem->extent (trem, Y_AXIS).length ()
903 /* hack a bit of space around the trem. */
907 It seems that also for ideal minimum length, we must use
908 the maximum beam count (for this direction):
910 \score{ \notes\relative c''{ [a8 a32] }}
912 must be horizontal. */
913 Real height_of_my_beams = beam_thickness
914 + (beam_count - 1) * beam_translation;
916 Real ideal_minimum_length = ideal_minimum_free
919 /* stem only extends to center of beam */
920 - 0.5 * beam_thickness;
922 ideal_length = max (ideal_length, ideal_minimum_length);
924 /* Convert to Y position, calculate for dir == UP */
926 = /* staff positions */
927 head_positions (me)[my_dir] * 0.5
928 * my_dir * staff_space;
929 Real ideal_y = note_start + ideal_length;
931 /* Conditions for Y position */
933 /* Lowest beam of (UP) beam must never be lower than second staffline
937 Although this (additional) rule is probably correct,
938 I expect that highest beam (UP) should also never be lower
939 than middle staffline, just as normal stems.
943 Obviously not for grace beams.
945 Also, not for knees. Seems to be a good thing. */
946 bool no_extend_b = to_boolean (me->get_property ("no-stem-extend"));
947 bool is_knee = to_boolean (beam->get_property ("knee"));
948 if (!no_extend_b && !is_knee)
950 /* Highest beam of (UP) beam must never be lower than middle
952 ideal_y = max (ideal_y, 0.0);
953 /* Lowest beam of (UP) beam must never be lower than second staffline */
954 ideal_y = max (ideal_y, (-staff_space
955 - beam_thickness + height_of_my_beams));
958 ideal_y -= robust_scm2double (beam->get_property ("shorten"), 0);
960 SCM bemfl = scm_cdr (scm_assq (ly_symbol2scm ("beamed-extreme-minimum-free-lengths"),
964 = scm_to_double (robust_list_ref (beam_count - 1, bemfl))
968 Real minimum_length = max (minimum_free, height_of_my_trem)
970 /* stem only extends to center of beam */
971 - 0.5 * beam_thickness;
974 Real minimum_y = note_start + minimum_length;
975 Real shortest_y = minimum_y * my_dir;
977 return scm_list_2 (scm_from_double (ideal_y),
978 scm_from_double (shortest_y));
982 Stem::beam_multiplicity (Grob *stem)
984 SCM beaming = stem->get_property ("beaming");
985 Slice le = int_list_to_slice (scm_car (beaming));
986 Slice ri = int_list_to_slice (scm_cdr (beaming));
991 /* FIXME: Too many properties */
993 "The stem represent the graphical stem. "
994 "In addition, it internally connects note heads, beams and"
996 "Rests and whole notes have invisible stems."
998 "\n\nThe following properties may be set in the details list."
1000 "@item beamed-lengths \n"
1001 "list of stem lengths given beam multiplicity. \n"
1002 "@item beamed-minimum-free-lengths \n"
1003 "list of normal minimum free stem lengths (chord to beams) given beam multiplicity.\n"
1004 "@item beamed-extreme-minimum-free-lengths\n"
1005 "list of extreme minimum free stem lengths (chord to beams) given beam multiplicity.\n"
1007 "Default stem lengths. The list gives a length for each flag-count.\n"
1008 "@item stem-shorten\n"
1009 "How much a stem in a forced direction "
1010 "should be shortened. The list gives an amount depending on the number "
1019 "default-direction "
1028 "neutral-direction "
1033 "stem-end-position "
1041 /****************************************************************/
1043 Stem_info::Stem_info ()
1045 ideal_y_ = shortest_y_ = 0;
1050 Stem_info::scale (Real x)