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.
18 #include <cmath> // rint
22 #include "directional-element-interface.hh"
23 #include "dot-column.hh"
24 #include "font-interface.hh"
25 #include "international.hh"
28 #include "note-head.hh"
29 #include "output-def.hh"
30 #include "paper-column.hh"
31 #include "pointer-group-interface.hh"
33 #include "rhythmic-head.hh"
34 #include "side-position-interface.hh"
35 #include "staff-symbol-referencer.hh"
36 #include "stem-tremolo.hh"
40 Stem::set_beaming (Grob *me, int beam_count, Direction d)
42 SCM pair = me->get_property ("beaming");
44 if (!scm_is_pair (pair))
46 pair = scm_cons (SCM_EOL, SCM_EOL);
47 me->set_property ("beaming", pair);
50 SCM lst = index_get_cell (pair, d);
52 for (int i = 0; i < beam_count; i++)
53 lst = scm_cons (scm_from_int (i), lst);
57 index_set_cell (pair, d, lst);
61 Stem::get_beaming (Grob *me, Direction d)
63 SCM pair = me->get_property ("beaming");
64 if (!scm_is_pair (pair))
67 SCM lst = index_get_cell (pair, d);
69 int len = scm_ilength (lst);
74 Stem::head_positions (Grob *me)
78 Drul_array<Grob *> e (extremal_heads (me));
79 return Interval (Staff_symbol_referencer::get_position (e[DOWN]),
80 Staff_symbol_referencer::get_position (e[UP]));
86 Stem::chord_start_y (Grob *me)
88 Interval hp = head_positions (me);
90 return hp[get_grob_direction (me)] * Staff_symbol_referencer::staff_space (me)
98 Stem::set_stemend (Grob *me, Real se)
101 Direction d = get_grob_direction (me);
103 if (d && d * head_positions (me)[get_grob_direction (me)] >= se * d)
104 me->warning (_ ("weird stem size, check for narrow beams"));
106 me->set_property ("stem-end-position", scm_from_double (se));
109 /* Note head that determines hshift for upstems
110 WARNING: triggers direction */
112 Stem::support_head (Grob *me)
114 extract_grob_set (me, "note-heads", heads);
115 if (heads.size () == 1)
118 return first_head (me);
122 Stem::head_count (Grob *me)
124 return Pointer_group_interface::count (me, ly_symbol2scm ("note-heads"));
127 /* The note head which forms one end of the stem.
128 WARNING: triggers direction */
130 Stem::first_head (Grob *me)
132 Direction d = get_grob_direction (me);
134 return extremal_heads (me)[-d];
138 /* The note head opposite to the first head. */
140 Stem::last_head (Grob *me)
142 Direction d = get_grob_direction (me);
144 return extremal_heads (me)[d];
149 START is part where stem reaches `last' head.
151 This function returns a drul with (bottom-head, top-head).
154 Stem::extremal_heads (Grob *me)
156 const int inf = 1000000;
157 Drul_array<int> extpos;
161 Drul_array<Grob *> exthead (0, 0);
162 extract_grob_set (me, "note-heads", heads);
164 for (vsize i = heads.size (); i--;)
167 int p = Staff_symbol_referencer::get_rounded_position (n);
172 if (d * p > d * extpos[d])
178 while (flip (&d) != DOWN);
183 /* The positions, in ascending order. */
185 Stem::note_head_positions (Grob *me)
188 extract_grob_set (me, "note-heads", heads);
190 for (vsize i = heads.size (); i--;)
193 int p = Staff_symbol_referencer::get_rounded_position (n);
198 vector_sort (ps, less<int> ());
203 Stem::add_head (Grob *me, Grob *n)
205 n->set_object ("stem", me->self_scm ());
207 if (Note_head::has_interface (n))
208 Pointer_group_interface::add_grob (me, ly_symbol2scm ("note-heads"), n);
209 else if (Rest::has_interface (n))
210 Pointer_group_interface::add_grob (me, ly_symbol2scm ("rests"), n);
214 Stem::is_invisible (Grob *me)
216 Real stemlet_length = robust_scm2double (me->get_property ("stemlet-length"),
219 return !((head_count (me)
220 || stemlet_length > 0.0)
221 && scm_to_int (me->get_property ("duration-log")) >= 1);
224 MAKE_SCHEME_CALLBACK (Stem, pure_height, 3)
226 Stem::pure_height (SCM smob, SCM start, SCM end)
232 Grob *me = unsmob_grob (smob);
233 Real ss = Staff_symbol_referencer::staff_space (me);
234 Real len = scm_to_double (calc_length (smob)) * ss / 2;
235 Direction dir = get_grob_direction (me);
238 Interval hp = head_positions (me);
240 iv = Interval (0, len);
242 iv = Interval (-len, 0);
245 iv.translate (hp[dir] * ss / 2);
247 return ly_interval2scm (iv);
250 MAKE_SCHEME_CALLBACK (Stem, calc_stem_end_position, 1)
252 Stem::calc_stem_end_position (SCM smob)
254 Grob *me = unsmob_grob (smob);
256 if (!head_count (me))
257 return scm_from_double (0.0);
259 if (Grob *beam = get_beam (me))
261 (void) beam->get_property ("quantized-positions");
262 return me->get_property ("stem-end-position");
265 Real ss = Staff_symbol_referencer::staff_space (me);
266 int durlog = duration_log (me);
269 /* WARNING: IN HALF SPACES */
270 Real length = robust_scm2double (me->get_property ("length"), 7);
272 Direction dir = get_grob_direction (me);
273 Interval hp = head_positions (me);
274 Real stem_end = dir ? hp[dir] + dir * length : 0;
276 /* TODO: change name to extend-stems to staff/center/'() */
277 bool no_extend_b = to_boolean (me->get_property ("no-stem-extend"));
278 if (!no_extend_b && dir * stem_end < 0)
282 /* Make a little room if we have a upflag and there is a dot.
283 previous approach was to lengthen the stem. This is not
284 good typesetting practice. */
285 if (!get_beam (me) && dir == UP
288 Grob *closest_to_flag = extremal_heads (me)[dir];
289 Grob *dots = closest_to_flag
290 ? Rhythmic_head::get_dots (closest_to_flag) : 0;
294 Real dp = Staff_symbol_referencer::get_position (dots);
295 Interval flag_yext = flag (me).extent (Y_AXIS) * (2 / ss) + stem_end;
297 /* Very gory: add myself to the X-support of the parent,
298 which should be a dot-column. */
300 if (flag_yext.distance (dp) < 0.5)
302 Grob *par = dots->get_parent (X_AXIS);
304 if (Dot_column::has_interface (par))
306 Side_position_interface::add_support (par, me);
308 /* TODO: apply some better logic here. The flag is
309 curved inwards, so this will typically be too
316 return scm_from_double (stem_end);
320 MAKE_SCHEME_CALLBACK (Stem, calc_length, 1)
322 Stem::calc_length (SCM smob)
324 Grob *me = unsmob_grob (smob);
326 SCM details = me->get_property ("details");
327 int durlog = duration_log (me);
329 Real ss = Staff_symbol_referencer::staff_space (me);
331 SCM s = scm_cdr (scm_assq (ly_symbol2scm ("lengths"), details));
333 length = 2 * scm_to_double (robust_list_ref (durlog - 2, s));
335 Direction dir = get_grob_direction (me);
337 /* Stems in unnatural (forced) direction should be shortened,
338 according to [Roush & Gourlay] */
339 Interval hp = head_positions (me);
340 if (dir && dir * hp[dir] >= 0)
342 SCM sshorten = scm_cdr (scm_assq (ly_symbol2scm ("stem-shorten"), details));
343 SCM scm_shorten = scm_is_pair (sshorten)
344 ? robust_list_ref (max (duration_log (me) - 2, 0), sshorten) : SCM_EOL;
345 Real shorten = 2* robust_scm2double (scm_shorten, 0);
347 /* On boundary: shorten only half */
348 if (abs (head_positions (me)[dir]) <= 1)
354 length *= robust_scm2double (me->get_property ("length-fraction"), 1.0);
357 Grob *t_flag = unsmob_grob (me->get_object ("tremolo-flag"));
358 if (t_flag && !unsmob_grob (me->get_object ("beam")))
360 /* Crude hack: add extra space if tremolo flag is there.
362 We can't do this for the beam, since we get into a loop
363 (Stem_tremolo::raw_stencil () looks at the beam.) --hwn */
366 + 2 * t_flag->extent (t_flag, Y_AXIS).length ()
369 /* We don't want to add the whole extent of the flag because the trem
370 and the flag can overlap partly. beam_translation gives a good
374 Real beam_trans = Stem_tremolo::get_beam_translation (t_flag);
375 /* the obvious choice is (durlog - 2) here, but we need a bit more space. */
376 minlen += 2 * (durlog - 1.5) * beam_trans;
378 /* up-stems need even a little more space to avoid collisions. This
379 needs to be in sync with the tremolo positioning code in
380 Stem_tremolo::print */
382 minlen += beam_trans;
384 length = max (length, minlen + 1.0);
387 return scm_from_double (length);
389 /* The log of the duration (Number of hooks on the flag minus two) */
391 Stem::duration_log (Grob *me)
393 SCM s = me->get_property ("duration-log");
394 return (scm_is_number (s)) ? scm_to_int (s) : 2;
397 MAKE_SCHEME_CALLBACK(Stem, calc_positioning_done, 1);
399 Stem::calc_positioning_done (SCM smob)
401 Grob *me = unsmob_grob (smob);
402 if (!head_count (me))
405 extract_grob_set (me, "note-heads", ro_heads);
406 vector<Grob*> heads (ro_heads);
407 vector_sort (heads, position_less);
408 Direction dir = get_grob_direction (me);
413 Real thick = thickness (me);
415 Grob *hed = support_head (me);
418 programming_error ("Stem dir must be up or down.");
420 set_grob_direction (me, dir);
423 bool is_harmonic_centered = false;
424 for (vsize i = 0; i < heads.size (); i++)
425 is_harmonic_centered = is_harmonic_centered
426 || heads[i]->get_property ("style") == ly_symbol2scm ("harmonic");
427 is_harmonic_centered = is_harmonic_centered && is_invisible (me);
429 Real w = hed->extent (hed, X_AXIS)[dir];
430 for (vsize i = 0; i < heads.size (); i++)
432 Real amount = w - heads[i]->extent (heads[i], X_AXIS)[dir];
434 if (is_harmonic_centered)
436 hed->extent (hed, X_AXIS).linear_combination (CENTER)
437 - heads[i]->extent (heads[i], X_AXIS).linear_combination (CENTER);
439 heads[i]->translate_axis (amount, X_AXIS);
442 Real lastpos = Real (Staff_symbol_referencer::get_position (heads[0]));
443 for (vsize i = 1; i < heads.size (); i++)
445 Real p = Staff_symbol_referencer::get_position (heads[i]);
446 Real dy = fabs (lastpos- p);
449 dy should always be 0.5, 0.0, 1.0, but provide safety margin
456 Real ell = heads[i]->extent (heads[i], X_AXIS).length ();
458 Direction d = get_grob_direction (me);
460 Reversed head should be shifted ell-thickness, but this
461 looks too crowded, so we only shift ell-0.5*thickness.
463 This leads to assymetry: Normal heads overlap the
464 stem 100% whereas reversed heads only overlaps the
468 Real reverse_overlap = 0.5;
469 heads[i]->translate_axis ((ell - thick * reverse_overlap) * d,
472 if (is_invisible (me))
473 heads[i]->translate_axis (-thick * (2 - reverse_overlap) * d,
478 For some cases we should kern some more: when the
479 distance between the next or prev note is too large, we'd
480 get large white gaps, eg.
501 MAKE_SCHEME_CALLBACK(Stem, calc_direction, 1);
503 Stem::calc_direction (SCM smob)
505 Grob *me = unsmob_grob (smob);
506 Direction dir = CENTER;
507 if (Grob *beam = unsmob_grob (me->get_object ("beam")))
509 SCM ignore_me = beam->get_property ("direction");
511 dir = get_grob_direction (me);
515 SCM dd = me->get_property ("default-direction");
518 return me->get_property ("neutral-direction");
521 return scm_from_int (dir);
524 MAKE_SCHEME_CALLBACK(Stem, calc_default_direction, 1);
526 Stem::calc_default_direction (SCM smob)
528 Grob *me = unsmob_grob (smob);
530 Direction dir = CENTER;
531 int staff_center = 0;
532 Interval hp = head_positions (me);
535 int udistance = (int) (UP * hp[UP] - staff_center);
536 int ddistance = (int) (DOWN * hp[DOWN] - staff_center);
538 dir = Direction (sign (ddistance - udistance));
541 return scm_from_int (dir);
545 MAKE_SCHEME_CALLBACK (Stem, height, 1);
547 Stem::height (SCM smob)
549 Grob *me = unsmob_grob (smob);
551 Direction dir = get_grob_direction (me);
555 UGH. Should be automatic
557 Grob *beam = get_beam (me);
560 /* trigger set-stem-lengths. */
561 beam->get_property ("quantized-positions");
565 Can't get_stencil(), since that would cache stencils too early.
566 This causes problems with beams.
568 Stencil *stencil = unsmob_stencil (print (smob));
569 Interval iv = stencil ? stencil->extent (Y_AXIS) : Interval();
574 programming_error ("no stem direction");
577 iv[dir] += dir * Beam::get_thickness (beam) * 0.5;
580 return ly_interval2scm (iv);
584 Stem::stem_end_position (Grob *me)
586 return robust_scm2double (me->get_property ("stem-end-position"), 0);
590 Stem::flag (Grob *me)
592 int log = duration_log (me);
594 || unsmob_grob (me->get_object ("beam")))
598 TODO: maybe property stroke-style should take different values,
599 e.g. "" (i.e. no stroke), "single" and "double" (currently, it's
603 SCM flag_style_scm = me->get_property ("flag-style");
604 if (scm_is_symbol (flag_style_scm))
605 flag_style = ly_symbol2string (flag_style_scm);
607 if (flag_style == "no-flag")
612 string staffline_offs;
613 if (flag_style == "mensural")
614 /* Mensural notation: For notes on staff lines, use different
615 flags than for notes between staff lines. The idea is that
616 flags are always vertically aligned with the staff lines,
617 regardless if the note head is on a staff line or between two
618 staff lines. In other words, the inner end of a flag always
619 touches a staff line.
624 int p = (int) (rint (stem_end_position (me)));
626 = Staff_symbol_referencer::on_line (me, p) ? "0" : "1";
629 staffline_offs = "2";
634 char dir = (get_grob_direction (me) == UP) ? 'u' : 'd';
635 string font_char = flag_style
636 + to_string (dir) + staffline_offs + to_string (log);
637 Font_metric *fm = Font_interface::get_default_font (me);
638 Stencil flag = fm->find_by_name ("flags." + font_char);
639 if (flag.is_empty ())
640 me->warning (_f ("flag `%s' not found", font_char));
642 SCM stroke_style_scm = me->get_property ("stroke-style");
643 if (scm_is_string (stroke_style_scm))
645 string stroke_style = ly_scm2string (stroke_style_scm);
646 if (!stroke_style.empty ())
648 string font_char = to_string (dir) + stroke_style;
649 Stencil stroke = fm->find_by_name ("flags." + font_char);
650 if (stroke.is_empty ())
651 me->warning (_f ("flag stroke `%s' not found", font_char));
653 flag.add_stencil (stroke);
660 MAKE_SCHEME_CALLBACK (Stem, width, 1);
664 Grob *me = unsmob_grob (e);
668 if (is_invisible (me))
670 else if (unsmob_grob (me->get_object ("beam"))
671 || abs (duration_log (me)) <= 2)
673 r = Interval (-1, 1);
674 r *= thickness (me) / 2;
678 r = Interval (-1, 1) * thickness (me) * 0.5;
679 r.unite (flag (me).extent (X_AXIS));
681 return ly_interval2scm (r);
685 Stem::thickness (Grob *me)
687 return scm_to_double (me->get_property ("thickness"))
688 * Staff_symbol_referencer::line_thickness (me);
691 MAKE_SCHEME_CALLBACK (Stem, print, 1);
693 Stem::print (SCM smob)
695 Grob *me = unsmob_grob (smob);
696 Grob *beam = get_beam (me);
699 Direction d = get_grob_direction (me);
701 Real stemlet_length = robust_scm2double (me->get_property ("stemlet-length"),
703 bool stemlet = stemlet_length > 0.0;
705 /* TODO: make the stem start a direction ?
706 This is required to avoid stems passing in tablature chords. */
708 = to_boolean (me->get_property ("avoid-note-head"))
715 if (!lh && stemlet && !beam)
718 if (is_invisible (me))
721 Real y2 = robust_scm2double (me->get_property ("stem-end-position"), 0.0);
723 Real half_space = Staff_symbol_referencer::staff_space (me) * 0.5;
726 y2 = Staff_symbol_referencer::get_position (lh);
729 Real beam_translation = Beam::get_beam_translation (beam);
730 Real beam_thickness = Beam::get_thickness (beam);
731 int beam_count = beam_multiplicity (me).length () + 1;
734 * (0.5 * beam_thickness
735 + beam_translation * max (0, (beam_count - 1))
736 + stemlet_length) / half_space;
739 Interval stem_y (min (y1, y2), max (y2, y1));
741 if (Grob *head = support_head (me))
744 must not take ledgers into account.
746 Interval head_height = head->extent (head, Y_AXIS);
747 Real y_attach = Note_head::stem_attachment_coordinate (head, Y_AXIS);
749 y_attach = head_height.linear_combination (y_attach);
750 stem_y[Direction (-d)] += d * y_attach / half_space;
754 Real stem_width = thickness (me);
756 = me->layout ()->get_dimension (ly_symbol2scm ("blot-diameter"));
758 Box b = Box (Interval (-stem_width / 2, stem_width / 2),
759 Interval (stem_y[DOWN] * half_space, stem_y[UP] * half_space));
761 Stencil ss = Lookup::round_filled_box (b, blot);
762 mol.add_stencil (ss);
764 mol.add_stencil (get_translated_flag (me));
766 return mol.smobbed_copy ();
770 Stem::get_translated_flag (Grob *me)
772 Stencil fl = flag (me);
775 Direction d = get_grob_direction (me);
777 = me->layout ()->get_dimension (ly_symbol2scm ("blot-diameter"));
778 Real stem_width = thickness (me);
779 Real half_space = Staff_symbol_referencer::staff_space (me) * 0.5;
780 Real y2 = robust_scm2double (me->get_property ("stem-end-position"), 0.0);
781 fl.translate_axis (y2 * half_space - d * blot / 2, Y_AXIS);
782 fl.translate_axis (stem_width / 2, X_AXIS);
789 move the stem to right of the notehead if it is up.
791 MAKE_SCHEME_CALLBACK (Stem, offset_callback, 1);
793 Stem::offset_callback (SCM smob)
795 Grob *me = unsmob_grob (smob);
798 if (Grob *f = first_head (me))
800 Interval head_wid = f->extent (f, X_AXIS);
803 if (is_invisible (me))
806 attach = Note_head::stem_attachment_coordinate (f, X_AXIS);
808 Direction d = get_grob_direction (me);
809 Real real_attach = head_wid.linear_combination (d * attach);
812 /* If not centered: correct for stem thickness. */
815 Real rule_thick = thickness (me);
816 r += -d * rule_thick * 0.5;
821 extract_grob_set (me, "rests", rests);
824 Grob *rest = rests.back ();
825 r = rest->extent (rest, X_AXIS).center ();
828 return scm_from_double (r);
832 Stem::get_beam (Grob *me)
834 SCM b = me->get_object ("beam");
835 return dynamic_cast<Spanner *> (unsmob_grob (b));
839 Stem::get_stem_info (Grob *me)
842 si.dir_ = get_grob_direction (me);
844 SCM scm_info = me->get_property ("stem-info");
845 si.ideal_y_ = scm_to_double (scm_car (scm_info));
846 si.shortest_y_ = scm_to_double (scm_cadr (scm_info));
850 MAKE_SCHEME_CALLBACK(Stem, calc_stem_info, 1);
852 Stem::calc_stem_info (SCM smob)
854 Grob *me = unsmob_grob (smob);
855 Direction my_dir = get_grob_direction (me);
859 programming_error ("no stem dir set");
863 Real staff_space = Staff_symbol_referencer::staff_space (me);
864 Grob *beam = get_beam (me);
868 (void) beam->get_property ("beaming");
871 Real beam_translation = Beam::get_beam_translation (beam);
872 Real beam_thickness = Beam::get_thickness (beam);
873 int beam_count = Beam::get_direction_beam_count (beam, my_dir);
875 = robust_scm2double (me->get_property ("length-fraction"), 1.0);
877 /* Simple standard stem length */
878 SCM details = me->get_property ("details");
879 SCM lengths = scm_cdr (scm_assq (ly_symbol2scm ("beamed-lengths"), details));
882 = scm_to_double (robust_list_ref (beam_count - 1, lengths))
886 /* stem only extends to center of beam
888 - 0.5 * beam_thickness;
890 /* Condition: sane minimum free stem length (chord to beams) */
891 lengths = scm_cdr (scm_assq (ly_symbol2scm ("beamed-minimum-free-lengths"), details));
893 Real ideal_minimum_free
894 = scm_to_double (robust_list_ref (beam_count - 1, lengths))
898 Real height_of_my_trem = 0.0;
899 Grob *trem = unsmob_grob (me->get_object ("tremolo-flag"));
901 height_of_my_trem = trem->extent (trem, Y_AXIS).length ()
902 /* hack a bit of space around the trem. */
906 It seems that also for ideal minimum length, we must use
907 the maximum beam count (for this direction):
909 \score{ \notes\relative c''{ [a8 a32] }}
911 must be horizontal. */
912 Real height_of_my_beams = beam_thickness
913 + (beam_count - 1) * beam_translation;
915 Real ideal_minimum_length = ideal_minimum_free
918 /* stem only extends to center of beam */
919 - 0.5 * beam_thickness;
921 ideal_length = max (ideal_length, ideal_minimum_length);
923 /* Convert to Y position, calculate for dir == UP */
925 = /* staff positions */
926 head_positions (me)[my_dir] * 0.5
927 * my_dir * staff_space;
928 Real ideal_y = note_start + ideal_length;
930 /* Conditions for Y position */
932 /* Lowest beam of (UP) beam must never be lower than second staffline
936 Although this (additional) rule is probably correct,
937 I expect that highest beam (UP) should also never be lower
938 than middle staffline, just as normal stems.
942 Obviously not for grace beams.
944 Also, not for knees. Seems to be a good thing. */
945 bool no_extend_b = to_boolean (me->get_property ("no-stem-extend"));
946 bool is_knee = to_boolean (beam->get_property ("knee"));
947 if (!no_extend_b && !is_knee)
949 /* Highest beam of (UP) beam must never be lower than middle
951 ideal_y = max (ideal_y, 0.0);
952 /* Lowest beam of (UP) beam must never be lower than second staffline */
953 ideal_y = max (ideal_y, (-staff_space
954 - beam_thickness + height_of_my_beams));
957 ideal_y -= robust_scm2double (beam->get_property ("shorten"), 0);
959 SCM bemfl = scm_cdr (scm_assq (ly_symbol2scm ("beamed-extreme-minimum-free-lengths"),
963 = scm_to_double (robust_list_ref (beam_count - 1, bemfl))
967 Real minimum_length = max (minimum_free, height_of_my_trem)
969 /* stem only extends to center of beam */
970 - 0.5 * beam_thickness;
973 Real minimum_y = note_start + minimum_length;
974 Real shortest_y = minimum_y * my_dir;
976 return scm_list_2 (scm_from_double (ideal_y),
977 scm_from_double (shortest_y));
981 Stem::beam_multiplicity (Grob *stem)
983 SCM beaming = stem->get_property ("beaming");
984 Slice le = int_list_to_slice (scm_car (beaming));
985 Slice ri = int_list_to_slice (scm_cdr (beaming));
990 /* FIXME: Too many properties */
991 ADD_INTERFACE (Stem, "stem-interface",
992 "The stem represent the graphical stem. "
993 "In addition, it internally connects note heads, beams and"
995 "Rests and whole notes have invisible stems."
997 "\n\nThe following properties may be set in the details list."
999 "@item beamed-lengths \n"
1000 "list of stem lengths given beam multiplicity. \n"
1001 "@item beamed-minimum-free-lengths \n"
1002 "list of normal minimum free stem lengths (chord to beams) given beam multiplicity.\n"
1003 "@item beamed-extreme-minimum-free-lengths\n"
1004 "list of extreme minimum free stem lengths (chord to beams) given beam multiplicity.\n"
1006 "Default stem lengths. The list gives a length for each flag-count.\n"
1007 "@item stem-shorten\n"
1008 "How much a stem in a forced direction "
1009 "should be shortened. The list gives an amount depending on the number "
1018 "default-direction "
1027 "neutral-direction "
1032 "stem-end-position "
1040 /****************************************************************/
1042 Stem_info::Stem_info ()
1044 ideal_y_ = shortest_y_ = 0;
1049 Stem_info::scale (Real x)