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);
244 Real rad = Staff_symbol_referencer::staff_radius (me);
246 if (!to_boolean (me->get_property ("cross-staff")))
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 /* extend the stem (away from the head) to cover the staff */
262 iv[UP] = max (iv[UP], rad * ss);
264 iv[DOWN] = min (iv[DOWN], -rad * ss);
267 iv = Interval (-rad * ss, rad * ss);
269 return ly_interval2scm (iv);
272 MAKE_SCHEME_CALLBACK (Stem, calc_stem_end_position, 1)
274 Stem::calc_stem_end_position (SCM smob)
276 Grob *me = unsmob_grob (smob);
278 if (!head_count (me))
279 return scm_from_double (0.0);
281 if (Grob *beam = get_beam (me))
283 (void) beam->get_property ("quantized-positions");
284 return me->get_property ("stem-end-position");
289 /* WARNING: IN HALF SPACES */
290 Real length = robust_scm2double (me->get_property ("length"), 7);
292 Direction dir = get_grob_direction (me);
293 Interval hp = head_positions (me);
294 Real stem_end = dir ? hp[dir] + dir * length : 0;
296 /* TODO: change name to extend-stems to staff/center/'() */
297 bool no_extend = to_boolean (me->get_property ("no-stem-extend"));
298 if (!no_extend && dir * stem_end < 0)
301 return scm_from_double (stem_end);
304 /* Length is in half-spaces (or: positions) here. */
305 MAKE_SCHEME_CALLBACK (Stem, calc_length, 1)
307 Stem::calc_length (SCM smob)
309 Grob *me = unsmob_grob (smob);
311 SCM details = me->get_property ("details");
312 int durlog = duration_log (me);
314 Real ss = Staff_symbol_referencer::staff_space (me);
316 SCM s = scm_cdr (scm_assq (ly_symbol2scm ("lengths"), details));
318 length = 2 * scm_to_double (robust_list_ref (durlog - 2, s));
320 Direction dir = get_grob_direction (me);
322 /* Stems in unnatural (forced) direction should be shortened,
323 according to [Roush & Gourlay] */
324 Interval hp = head_positions (me);
325 if (dir && dir * hp[dir] >= 0)
327 SCM sshorten = scm_cdr (scm_assq (ly_symbol2scm ("stem-shorten"), details));
328 SCM scm_shorten = scm_is_pair (sshorten)
329 ? robust_list_ref (max (duration_log (me) - 2, 0), sshorten) : SCM_EOL;
330 Real shorten = 2* robust_scm2double (scm_shorten, 0);
332 /* On boundary: shorten only half */
333 if (abs (head_positions (me)[dir]) <= 1)
339 length *= robust_scm2double (me->get_property ("length-fraction"), 1.0);
342 Grob *t_flag = unsmob_grob (me->get_object ("tremolo-flag"));
343 if (t_flag && !unsmob_grob (me->get_object ("beam")))
345 /* Crude hack: add extra space if tremolo flag is there.
347 We can't do this for the beam, since we get into a loop
348 (Stem_tremolo::raw_stencil () looks at the beam.) --hwn */
351 + 2 * Stem_tremolo::vertical_length (t_flag) / ss;
353 /* We don't want to add the whole extent of the flag because the trem
354 and the flag can overlap partly. beam_translation gives a good
358 Real beam_trans = Stem_tremolo::get_beam_translation (t_flag);
359 /* the obvious choice is (durlog - 2) here, but we need a bit more space. */
360 minlen += 2 * (durlog - 1.5) * beam_trans;
362 /* up-stems need even a little more space to avoid collisions. This
363 needs to be in sync with the tremolo positioning code in
364 Stem_tremolo::print */
366 minlen += beam_trans;
368 length = max (length, minlen + 1.0);
371 return scm_from_double (length);
373 /* The log of the duration (Number of hooks on the flag minus two) */
375 Stem::duration_log (Grob *me)
377 SCM s = me->get_property ("duration-log");
378 return (scm_is_number (s)) ? scm_to_int (s) : 2;
381 MAKE_SCHEME_CALLBACK (Stem, calc_positioning_done, 1);
383 Stem::calc_positioning_done (SCM smob)
385 Grob *me = unsmob_grob (smob);
386 if (!head_count (me))
389 me->set_property ("positioning-done", SCM_BOOL_T);
391 extract_grob_set (me, "note-heads", ro_heads);
392 vector<Grob*> heads (ro_heads);
393 vector_sort (heads, position_less);
394 Direction dir = get_grob_direction (me);
399 Real thick = thickness (me);
401 Grob *hed = support_head (me);
404 programming_error ("Stem dir must be up or down.");
406 set_grob_direction (me, dir);
409 bool is_harmonic_centered = false;
410 for (vsize i = 0; i < heads.size (); i++)
411 is_harmonic_centered = is_harmonic_centered
412 || heads[i]->get_property ("style") == ly_symbol2scm ("harmonic");
413 is_harmonic_centered = is_harmonic_centered && is_invisible (me);
415 Real w = hed->extent (hed, X_AXIS)[dir];
416 for (vsize i = 0; i < heads.size (); i++)
418 Real amount = w - heads[i]->extent (heads[i], X_AXIS)[dir];
420 if (is_harmonic_centered)
422 hed->extent (hed, X_AXIS).linear_combination (CENTER)
423 - heads[i]->extent (heads[i], X_AXIS).linear_combination (CENTER);
425 heads[i]->translate_axis (amount, X_AXIS);
428 Real lastpos = Real (Staff_symbol_referencer::get_position (heads[0]));
429 for (vsize i = 1; i < heads.size (); i++)
431 Real p = Staff_symbol_referencer::get_position (heads[i]);
432 Real dy = fabs (lastpos- p);
435 dy should always be 0.5, 0.0, 1.0, but provide safety margin
442 Real ell = heads[i]->extent (heads[i], X_AXIS).length ();
444 Direction d = get_grob_direction (me);
446 Reversed head should be shifted ell-thickness, but this
447 looks too crowded, so we only shift ell-0.5*thickness.
449 This leads to assymetry: Normal heads overlap the
450 stem 100% whereas reversed heads only overlaps the
454 Real reverse_overlap = 0.5;
455 heads[i]->translate_axis ((ell - thick * reverse_overlap) * d,
458 if (is_invisible (me))
459 heads[i]->translate_axis (-thick * (2 - reverse_overlap) * d,
464 For some cases we should kern some more: when the
465 distance between the next or prev note is too large, we'd
466 get large white gaps, eg.
487 MAKE_SCHEME_CALLBACK (Stem, calc_direction, 1);
489 Stem::calc_direction (SCM smob)
491 Grob *me = unsmob_grob (smob);
492 Direction dir = CENTER;
493 if (Grob *beam = unsmob_grob (me->get_object ("beam")))
495 SCM ignore_me = beam->get_property ("direction");
497 dir = get_grob_direction (me);
501 SCM dd = me->get_property ("default-direction");
504 return me->get_property ("neutral-direction");
507 return scm_from_int (dir);
510 MAKE_SCHEME_CALLBACK (Stem, calc_default_direction, 1);
512 Stem::calc_default_direction (SCM smob)
514 Grob *me = unsmob_grob (smob);
516 Direction dir = CENTER;
517 int staff_center = 0;
518 Interval hp = head_positions (me);
521 int udistance = (int) (UP * hp[UP] - staff_center);
522 int ddistance = (int) (DOWN * hp[DOWN] - staff_center);
524 dir = Direction (sign (ddistance - udistance));
527 return scm_from_int (dir);
531 MAKE_SCHEME_CALLBACK (Stem, height, 1);
533 Stem::height (SCM smob)
535 Grob *me = unsmob_grob (smob);
536 if (!is_normal_stem (me))
537 return ly_interval2scm (Interval ());
539 Direction dir = get_grob_direction (me);
541 Grob *beam = get_beam (me);
544 /* trigger set-stem-lengths. */
545 beam->get_property ("quantized-positions");
549 Can't get_stencil (), since that would cache stencils too early.
550 This causes problems with beams.
552 Stencil *stencil = unsmob_stencil (print (smob));
553 Interval iv = stencil ? stencil->extent (Y_AXIS) : Interval ();
558 programming_error ("no stem direction");
561 iv[dir] += dir * Beam::get_thickness (beam) * 0.5;
564 return ly_interval2scm (iv);
568 Stem::stem_end_position (Grob *me)
570 return robust_scm2double (me->get_property ("stem-end-position"), 0);
574 Stem::flag (Grob *me)
576 int log = duration_log (me);
578 || unsmob_grob (me->get_object ("beam")))
581 if (!is_normal_stem (me))
585 TODO: maybe property stroke-style should take different values,
586 e.g. "" (i.e. no stroke), "single" and "double" (currently, it's
590 SCM flag_style_scm = me->get_property ("flag-style");
591 if (scm_is_symbol (flag_style_scm))
592 flag_style = ly_symbol2string (flag_style_scm);
594 if (flag_style == "no-flag")
599 string staffline_offs;
600 if (flag_style == "mensural")
601 /* Mensural notation: For notes on staff lines, use different
602 flags than for notes between staff lines. The idea is that
603 flags are always vertically aligned with the staff lines,
604 regardless if the note head is on a staff line or between two
605 staff lines. In other words, the inner end of a flag always
606 touches a staff line.
611 int p = (int) (rint (stem_end_position (me)));
613 = Staff_symbol_referencer::on_line (me, p) ? "0" : "1";
616 staffline_offs = "2";
621 char dir = (get_grob_direction (me) == UP) ? 'u' : 'd';
622 string font_char = flag_style
623 + to_string (dir) + staffline_offs + to_string (log);
624 Font_metric *fm = Font_interface::get_default_font (me);
625 Stencil flag = fm->find_by_name ("flags." + font_char);
626 if (flag.is_empty ())
627 me->warning (_f ("flag `%s' not found", font_char));
629 SCM stroke_style_scm = me->get_property ("stroke-style");
630 if (scm_is_string (stroke_style_scm))
632 string stroke_style = ly_scm2string (stroke_style_scm);
633 if (!stroke_style.empty ())
635 string font_char = to_string (dir) + stroke_style;
636 Stencil stroke = fm->find_by_name ("flags." + font_char);
637 if (stroke.is_empty ())
638 me->warning (_f ("flag stroke `%s' not found", font_char));
640 flag.add_stencil (stroke);
647 MAKE_SCHEME_CALLBACK (Stem, width, 1);
651 Grob *me = unsmob_grob (e);
655 if (is_invisible (me))
657 else if (unsmob_grob (me->get_object ("beam"))
658 || abs (duration_log (me)) <= 2)
660 r = Interval (-1, 1);
661 r *= thickness (me) / 2;
665 r = Interval (-1, 1) * thickness (me) * 0.5;
666 r.unite (flag (me).extent (X_AXIS));
668 return ly_interval2scm (r);
672 Stem::thickness (Grob *me)
674 return scm_to_double (me->get_property ("thickness"))
675 * Staff_symbol_referencer::line_thickness (me);
678 MAKE_SCHEME_CALLBACK (Stem, print, 1);
680 Stem::print (SCM smob)
682 Grob *me = unsmob_grob (smob);
683 Grob *beam = get_beam (me);
686 Direction d = get_grob_direction (me);
688 Real stemlet_length = robust_scm2double (me->get_property ("stemlet-length"),
690 bool stemlet = stemlet_length > 0.0;
692 /* TODO: make the stem start a direction ?
693 This is required to avoid stems passing in tablature chords. */
695 = to_boolean (me->get_property ("avoid-note-head"))
702 if (!lh && stemlet && !beam)
705 if (is_invisible (me))
708 Real y2 = robust_scm2double (me->get_property ("stem-end-position"), 0.0);
710 Real half_space = Staff_symbol_referencer::staff_space (me) * 0.5;
713 y2 = Staff_symbol_referencer::get_position (lh);
716 Real beam_translation = Beam::get_beam_translation (beam);
717 Real beam_thickness = Beam::get_thickness (beam);
718 int beam_count = beam_multiplicity (me).length () + 1;
721 * (0.5 * beam_thickness
722 + beam_translation * max (0, (beam_count - 1))
723 + stemlet_length) / half_space;
726 Interval stem_y (min (y1, y2), max (y2, y1));
728 if (Grob *head = support_head (me))
731 must not take ledgers into account.
733 Interval head_height = head->extent (head, Y_AXIS);
734 Real y_attach = Note_head::stem_attachment_coordinate (head, Y_AXIS);
736 y_attach = head_height.linear_combination (y_attach);
737 stem_y[Direction (-d)] += d * y_attach / half_space;
741 Real stem_width = thickness (me);
743 = me->layout ()->get_dimension (ly_symbol2scm ("blot-diameter"));
745 Box b = Box (Interval (-stem_width / 2, stem_width / 2),
746 Interval (stem_y[DOWN] * half_space, stem_y[UP] * half_space));
748 Stencil ss = Lookup::round_filled_box (b, blot);
749 mol.add_stencil (ss);
751 mol.add_stencil (get_translated_flag (me));
753 return mol.smobbed_copy ();
757 Stem::get_translated_flag (Grob *me)
759 Stencil fl = flag (me);
762 Direction d = get_grob_direction (me);
764 = me->layout ()->get_dimension (ly_symbol2scm ("blot-diameter"));
765 Real stem_width = thickness (me);
766 Real half_space = Staff_symbol_referencer::staff_space (me) * 0.5;
767 Real y2 = robust_scm2double (me->get_property ("stem-end-position"), 0.0);
768 fl.translate_axis (y2 * half_space - d * blot / 2, Y_AXIS);
769 fl.translate_axis (stem_width / 2, X_AXIS);
776 move the stem to right of the notehead if it is up.
778 MAKE_SCHEME_CALLBACK (Stem, offset_callback, 1);
780 Stem::offset_callback (SCM smob)
782 Grob *me = unsmob_grob (smob);
784 extract_grob_set (me, "rests", rests);
787 Grob *rest = rests.back ();
788 Real r = rest->extent (rest, X_AXIS).center ();
789 return scm_from_double (r);
793 if (Grob *f = first_head (me))
795 Interval head_wid = f->extent (f, X_AXIS);
798 if (is_invisible (me))
801 attach = Note_head::stem_attachment_coordinate (f, X_AXIS);
803 Direction d = get_grob_direction (me);
804 Real real_attach = head_wid.linear_combination (d * attach);
805 Real r = real_attach;
807 /* If not centered: correct for stem thickness. */
810 Real rule_thick = thickness (me);
811 r += -d * rule_thick * 0.5;
813 return scm_from_double (r);
816 programming_error ("Weird stem.");
817 return scm_from_double (0.0);
821 Stem::get_beam (Grob *me)
823 SCM b = me->get_object ("beam");
824 return dynamic_cast<Spanner *> (unsmob_grob (b));
828 Stem::get_stem_info (Grob *me)
831 si.dir_ = get_grob_direction (me);
833 SCM scm_info = me->get_property ("stem-info");
834 si.ideal_y_ = scm_to_double (scm_car (scm_info));
835 si.shortest_y_ = scm_to_double (scm_cadr (scm_info));
839 MAKE_SCHEME_CALLBACK (Stem, calc_stem_info, 1);
841 Stem::calc_stem_info (SCM smob)
843 Grob *me = unsmob_grob (smob);
844 Direction my_dir = get_grob_direction (me);
848 programming_error ("no stem dir set");
852 Real staff_space = Staff_symbol_referencer::staff_space (me);
853 Grob *beam = get_beam (me);
857 (void) beam->get_property ("beaming");
860 Real beam_translation = Beam::get_beam_translation (beam);
861 Real beam_thickness = Beam::get_thickness (beam);
862 int beam_count = Beam::get_direction_beam_count (beam, my_dir);
864 = robust_scm2double (me->get_property ("length-fraction"), 1.0);
866 /* Simple standard stem length */
867 SCM details = me->get_property ("details");
868 SCM lengths = scm_cdr (scm_assq (ly_symbol2scm ("beamed-lengths"), details));
871 = scm_to_double (robust_list_ref (beam_count - 1, lengths))
875 /* stem only extends to center of beam
877 - 0.5 * beam_thickness;
879 /* Condition: sane minimum free stem length (chord to beams) */
880 lengths = scm_cdr (scm_assq (ly_symbol2scm ("beamed-minimum-free-lengths"), details));
882 Real ideal_minimum_free
883 = scm_to_double (robust_list_ref (beam_count - 1, lengths))
887 Real height_of_my_trem = 0.0;
888 Grob *trem = unsmob_grob (me->get_object ("tremolo-flag"));
892 = Stem_tremolo::vertical_length (trem)
893 /* hack a bit of space around the trem. */
899 It seems that also for ideal minimum length, we must use
900 the maximum beam count (for this direction):
902 \score{ \notes\relative c''{ [a8 a32] }}
904 must be horizontal. */
905 Real height_of_my_beams = beam_thickness
906 + (beam_count - 1) * beam_translation;
908 Real ideal_minimum_length = ideal_minimum_free
911 /* stem only extends to center of beam */
912 - 0.5 * beam_thickness;
914 ideal_length = max (ideal_length, ideal_minimum_length);
916 /* Convert to Y position, calculate for dir == UP */
918 = /* staff positions */
919 head_positions (me)[my_dir] * 0.5
920 * my_dir * staff_space;
921 Real ideal_y = note_start + ideal_length;
923 /* Conditions for Y position */
925 /* Lowest beam of (UP) beam must never be lower than second staffline
929 Although this (additional) rule is probably correct,
930 I expect that highest beam (UP) should also never be lower
931 than middle staffline, just as normal stems.
935 Obviously not for grace beams.
937 Also, not for knees. Seems to be a good thing. */
938 bool no_extend = to_boolean (me->get_property ("no-stem-extend"));
939 bool is_knee = to_boolean (beam->get_property ("knee"));
940 if (!no_extend && !is_knee)
942 /* Highest beam of (UP) beam must never be lower than middle
944 ideal_y = max (ideal_y, 0.0);
945 /* Lowest beam of (UP) beam must never be lower than second staffline */
946 ideal_y = max (ideal_y, (-staff_space
947 - beam_thickness + height_of_my_beams));
950 ideal_y -= robust_scm2double (beam->get_property ("shorten"), 0);
952 SCM bemfl = scm_cdr (scm_assq (ly_symbol2scm ("beamed-extreme-minimum-free-lengths"),
956 = scm_to_double (robust_list_ref (beam_count - 1, bemfl))
960 Real minimum_length = max (minimum_free, height_of_my_trem)
962 /* stem only extends to center of beam */
963 - 0.5 * beam_thickness;
966 Real minimum_y = note_start + minimum_length;
967 Real shortest_y = minimum_y * my_dir;
969 return scm_list_2 (scm_from_double (ideal_y),
970 scm_from_double (shortest_y));
974 Stem::beam_multiplicity (Grob *stem)
976 SCM beaming = stem->get_property ("beaming");
977 Slice le = int_list_to_slice (scm_car (beaming));
978 Slice ri = int_list_to_slice (scm_cdr (beaming));
984 Stem::is_cross_staff (Grob *stem)
986 Grob *beam = unsmob_grob (stem->get_object ("beam"));
987 return beam && Beam::is_cross_staff (beam);
990 MAKE_SCHEME_CALLBACK (Stem, calc_cross_staff, 1)
992 Stem::calc_cross_staff (SCM smob)
994 return scm_from_bool (is_cross_staff (unsmob_grob (smob)));
997 /* FIXME: Too many properties */
999 "The stem represent the graphical stem. "
1000 "In addition, it internally connects note heads, beams and"
1002 "Rests and whole notes have invisible stems."
1004 "\n\nThe following properties may be set in the details list."
1006 "@item beamed-lengths \n"
1007 "list of stem lengths given beam multiplicity. \n"
1008 "@item beamed-minimum-free-lengths \n"
1009 "list of normal minimum free stem lengths (chord to beams) given beam multiplicity.\n"
1010 "@item beamed-extreme-minimum-free-lengths\n"
1011 "list of extreme minimum free stem lengths (chord to beams) given beam multiplicity.\n"
1013 "Default stem lengths. The list gives a length for each flag-count.\n"
1014 "@item stem-shorten\n"
1015 "How much a stem in a forced direction "
1016 "should be shortened. The list gives an amount depending on the number "
1025 "default-direction "
1034 "neutral-direction "
1039 "stem-end-position "
1047 /****************************************************************/
1049 Stem_info::Stem_info ()
1051 ideal_y_ = shortest_y_ = 0;
1056 Stem_info::scale (Real x)