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 len = scm_to_double (calc_length (smob)) * ss / 2;
245 Direction dir = get_grob_direction (me);
247 Interval hp = head_positions (me);
249 iv = Interval (0, len);
251 iv = Interval (-len, 0);
254 iv.translate (hp[dir] * ss / 2);
256 return ly_interval2scm (iv);
259 MAKE_SCHEME_CALLBACK (Stem, calc_stem_end_position, 1)
261 Stem::calc_stem_end_position (SCM smob)
263 Grob *me = unsmob_grob (smob);
265 if (!head_count (me))
266 return scm_from_double (0.0);
268 if (Grob *beam = get_beam (me))
270 (void) beam->get_property ("quantized-positions");
271 return me->get_property ("stem-end-position");
274 Real ss = Staff_symbol_referencer::staff_space (me);
275 int durlog = duration_log (me);
278 /* WARNING: IN HALF SPACES */
279 Real length = robust_scm2double (me->get_property ("length"), 7);
281 Direction dir = get_grob_direction (me);
282 Interval hp = head_positions (me);
283 Real stem_end = dir ? hp[dir] + dir * length : 0;
285 /* TODO: change name to extend-stems to staff/center/'() */
286 bool no_extend_b = to_boolean (me->get_property ("no-stem-extend"));
287 if (!no_extend_b && dir * stem_end < 0)
291 /* Make a little room if we have a upflag and there is a dot.
292 previous approach was to lengthen the stem. This is not
293 good typesetting practice. */
294 if (!get_beam (me) && dir == UP
297 Grob *closest_to_flag = extremal_heads (me)[dir];
298 Grob *dots = closest_to_flag
299 ? Rhythmic_head::get_dots (closest_to_flag) : 0;
303 Real dp = Staff_symbol_referencer::get_position (dots);
304 Interval flag_yext = flag (me).extent (Y_AXIS) * (2 / ss) + stem_end;
306 /* Very gory: add myself to the X-support of the parent,
307 which should be a dot-column. */
309 if (flag_yext.distance (dp) < 0.5)
311 Grob *par = dots->get_parent (X_AXIS);
313 if (Dot_column::has_interface (par))
315 Side_position_interface::add_support (par, me);
317 /* TODO: apply some better logic here. The flag is
318 curved inwards, so this will typically be too
325 return scm_from_double (stem_end);
328 /* Length is in half-spaces (or: positions) here. */
329 MAKE_SCHEME_CALLBACK (Stem, calc_length, 1)
331 Stem::calc_length (SCM smob)
333 Grob *me = unsmob_grob (smob);
335 SCM details = me->get_property ("details");
336 int durlog = duration_log (me);
338 Real ss = Staff_symbol_referencer::staff_space (me);
340 SCM s = scm_cdr (scm_assq (ly_symbol2scm ("lengths"), details));
342 length = 2 * scm_to_double (robust_list_ref (durlog - 2, s));
344 Direction dir = get_grob_direction (me);
346 /* Stems in unnatural (forced) direction should be shortened,
347 according to [Roush & Gourlay] */
348 Interval hp = head_positions (me);
349 if (dir && dir * hp[dir] >= 0)
351 SCM sshorten = scm_cdr (scm_assq (ly_symbol2scm ("stem-shorten"), details));
352 SCM scm_shorten = scm_is_pair (sshorten)
353 ? robust_list_ref (max (duration_log (me) - 2, 0), sshorten) : SCM_EOL;
354 Real shorten = 2* robust_scm2double (scm_shorten, 0);
356 /* On boundary: shorten only half */
357 if (abs (head_positions (me)[dir]) <= 1)
363 length *= robust_scm2double (me->get_property ("length-fraction"), 1.0);
366 Grob *t_flag = unsmob_grob (me->get_object ("tremolo-flag"));
367 if (t_flag && !unsmob_grob (me->get_object ("beam")))
369 /* Crude hack: add extra space if tremolo flag is there.
371 We can't do this for the beam, since we get into a loop
372 (Stem_tremolo::raw_stencil () looks at the beam.) --hwn */
375 + 2 * Stem_tremolo::vertical_length (t_flag) / ss;
377 /* We don't want to add the whole extent of the flag because the trem
378 and the flag can overlap partly. beam_translation gives a good
382 Real beam_trans = Stem_tremolo::get_beam_translation (t_flag);
383 /* the obvious choice is (durlog - 2) here, but we need a bit more space. */
384 minlen += 2 * (durlog - 1.5) * beam_trans;
386 /* up-stems need even a little more space to avoid collisions. This
387 needs to be in sync with the tremolo positioning code in
388 Stem_tremolo::print */
390 minlen += beam_trans;
392 length = max (length, minlen + 1.0);
395 return scm_from_double (length);
397 /* The log of the duration (Number of hooks on the flag minus two) */
399 Stem::duration_log (Grob *me)
401 SCM s = me->get_property ("duration-log");
402 return (scm_is_number (s)) ? scm_to_int (s) : 2;
405 MAKE_SCHEME_CALLBACK(Stem, calc_positioning_done, 1);
407 Stem::calc_positioning_done (SCM smob)
409 Grob *me = unsmob_grob (smob);
410 if (!head_count (me))
413 extract_grob_set (me, "note-heads", ro_heads);
414 vector<Grob*> heads (ro_heads);
415 vector_sort (heads, position_less);
416 Direction dir = get_grob_direction (me);
421 Real thick = thickness (me);
423 Grob *hed = support_head (me);
426 programming_error ("Stem dir must be up or down.");
428 set_grob_direction (me, dir);
431 bool is_harmonic_centered = false;
432 for (vsize i = 0; i < heads.size (); i++)
433 is_harmonic_centered = is_harmonic_centered
434 || heads[i]->get_property ("style") == ly_symbol2scm ("harmonic");
435 is_harmonic_centered = is_harmonic_centered && is_invisible (me);
437 Real w = hed->extent (hed, X_AXIS)[dir];
438 for (vsize i = 0; i < heads.size (); i++)
440 Real amount = w - heads[i]->extent (heads[i], X_AXIS)[dir];
442 if (is_harmonic_centered)
444 hed->extent (hed, X_AXIS).linear_combination (CENTER)
445 - heads[i]->extent (heads[i], X_AXIS).linear_combination (CENTER);
447 heads[i]->translate_axis (amount, X_AXIS);
450 Real lastpos = Real (Staff_symbol_referencer::get_position (heads[0]));
451 for (vsize i = 1; i < heads.size (); i++)
453 Real p = Staff_symbol_referencer::get_position (heads[i]);
454 Real dy = fabs (lastpos- p);
457 dy should always be 0.5, 0.0, 1.0, but provide safety margin
464 Real ell = heads[i]->extent (heads[i], X_AXIS).length ();
466 Direction d = get_grob_direction (me);
468 Reversed head should be shifted ell-thickness, but this
469 looks too crowded, so we only shift ell-0.5*thickness.
471 This leads to assymetry: Normal heads overlap the
472 stem 100% whereas reversed heads only overlaps the
476 Real reverse_overlap = 0.5;
477 heads[i]->translate_axis ((ell - thick * reverse_overlap) * d,
480 if (is_invisible (me))
481 heads[i]->translate_axis (-thick * (2 - reverse_overlap) * d,
486 For some cases we should kern some more: when the
487 distance between the next or prev note is too large, we'd
488 get large white gaps, eg.
509 MAKE_SCHEME_CALLBACK(Stem, calc_direction, 1);
511 Stem::calc_direction (SCM smob)
513 Grob *me = unsmob_grob (smob);
514 Direction dir = CENTER;
515 if (Grob *beam = unsmob_grob (me->get_object ("beam")))
517 SCM ignore_me = beam->get_property ("direction");
519 dir = get_grob_direction (me);
523 SCM dd = me->get_property ("default-direction");
526 return me->get_property ("neutral-direction");
529 return scm_from_int (dir);
532 MAKE_SCHEME_CALLBACK(Stem, calc_default_direction, 1);
534 Stem::calc_default_direction (SCM smob)
536 Grob *me = unsmob_grob (smob);
538 Direction dir = CENTER;
539 int staff_center = 0;
540 Interval hp = head_positions (me);
543 int udistance = (int) (UP * hp[UP] - staff_center);
544 int ddistance = (int) (DOWN * hp[DOWN] - staff_center);
546 dir = Direction (sign (ddistance - udistance));
549 return scm_from_int (dir);
553 MAKE_SCHEME_CALLBACK (Stem, height, 1);
555 Stem::height (SCM smob)
557 Grob *me = unsmob_grob (smob);
559 Direction dir = get_grob_direction (me);
563 UGH. Should be automatic
565 Grob *beam = get_beam (me);
568 /* trigger set-stem-lengths. */
569 beam->get_property ("quantized-positions");
573 Can't get_stencil(), since that would cache stencils too early.
574 This causes problems with beams.
576 Stencil *stencil = unsmob_stencil (print (smob));
577 Interval iv = stencil ? stencil->extent (Y_AXIS) : Interval();
582 programming_error ("no stem direction");
585 iv[dir] += dir * Beam::get_thickness (beam) * 0.5;
588 return ly_interval2scm (iv);
592 Stem::stem_end_position (Grob *me)
594 return robust_scm2double (me->get_property ("stem-end-position"), 0);
598 Stem::flag (Grob *me)
600 int log = duration_log (me);
602 || unsmob_grob (me->get_object ("beam")))
606 TODO: maybe property stroke-style should take different values,
607 e.g. "" (i.e. no stroke), "single" and "double" (currently, it's
611 SCM flag_style_scm = me->get_property ("flag-style");
612 if (scm_is_symbol (flag_style_scm))
613 flag_style = ly_symbol2string (flag_style_scm);
615 if (flag_style == "no-flag")
620 string staffline_offs;
621 if (flag_style == "mensural")
622 /* Mensural notation: For notes on staff lines, use different
623 flags than for notes between staff lines. The idea is that
624 flags are always vertically aligned with the staff lines,
625 regardless if the note head is on a staff line or between two
626 staff lines. In other words, the inner end of a flag always
627 touches a staff line.
632 int p = (int) (rint (stem_end_position (me)));
634 = Staff_symbol_referencer::on_line (me, p) ? "0" : "1";
637 staffline_offs = "2";
642 char dir = (get_grob_direction (me) == UP) ? 'u' : 'd';
643 string font_char = flag_style
644 + to_string (dir) + staffline_offs + to_string (log);
645 Font_metric *fm = Font_interface::get_default_font (me);
646 Stencil flag = fm->find_by_name ("flags." + font_char);
647 if (flag.is_empty ())
648 me->warning (_f ("flag `%s' not found", font_char));
650 SCM stroke_style_scm = me->get_property ("stroke-style");
651 if (scm_is_string (stroke_style_scm))
653 string stroke_style = ly_scm2string (stroke_style_scm);
654 if (!stroke_style.empty ())
656 string font_char = to_string (dir) + stroke_style;
657 Stencil stroke = fm->find_by_name ("flags." + font_char);
658 if (stroke.is_empty ())
659 me->warning (_f ("flag stroke `%s' not found", font_char));
661 flag.add_stencil (stroke);
668 MAKE_SCHEME_CALLBACK (Stem, width, 1);
672 Grob *me = unsmob_grob (e);
676 if (is_invisible (me))
678 else if (unsmob_grob (me->get_object ("beam"))
679 || abs (duration_log (me)) <= 2)
681 r = Interval (-1, 1);
682 r *= thickness (me) / 2;
686 r = Interval (-1, 1) * thickness (me) * 0.5;
687 r.unite (flag (me).extent (X_AXIS));
689 return ly_interval2scm (r);
693 Stem::thickness (Grob *me)
695 return scm_to_double (me->get_property ("thickness"))
696 * Staff_symbol_referencer::line_thickness (me);
699 MAKE_SCHEME_CALLBACK (Stem, print, 1);
701 Stem::print (SCM smob)
703 Grob *me = unsmob_grob (smob);
704 Grob *beam = get_beam (me);
707 Direction d = get_grob_direction (me);
709 Real stemlet_length = robust_scm2double (me->get_property ("stemlet-length"),
711 bool stemlet = stemlet_length > 0.0;
713 /* TODO: make the stem start a direction ?
714 This is required to avoid stems passing in tablature chords. */
716 = to_boolean (me->get_property ("avoid-note-head"))
723 if (!lh && stemlet && !beam)
726 if (is_invisible (me))
729 Real y2 = robust_scm2double (me->get_property ("stem-end-position"), 0.0);
731 Real half_space = Staff_symbol_referencer::staff_space (me) * 0.5;
734 y2 = Staff_symbol_referencer::get_position (lh);
737 Real beam_translation = Beam::get_beam_translation (beam);
738 Real beam_thickness = Beam::get_thickness (beam);
739 int beam_count = beam_multiplicity (me).length () + 1;
742 * (0.5 * beam_thickness
743 + beam_translation * max (0, (beam_count - 1))
744 + stemlet_length) / half_space;
747 Interval stem_y (min (y1, y2), max (y2, y1));
749 if (Grob *head = support_head (me))
752 must not take ledgers into account.
754 Interval head_height = head->extent (head, Y_AXIS);
755 Real y_attach = Note_head::stem_attachment_coordinate (head, Y_AXIS);
757 y_attach = head_height.linear_combination (y_attach);
758 stem_y[Direction (-d)] += d * y_attach / half_space;
762 Real stem_width = thickness (me);
764 = me->layout ()->get_dimension (ly_symbol2scm ("blot-diameter"));
766 Box b = Box (Interval (-stem_width / 2, stem_width / 2),
767 Interval (stem_y[DOWN] * half_space, stem_y[UP] * half_space));
769 Stencil ss = Lookup::round_filled_box (b, blot);
770 mol.add_stencil (ss);
772 mol.add_stencil (get_translated_flag (me));
774 return mol.smobbed_copy ();
778 Stem::get_translated_flag (Grob *me)
780 Stencil fl = flag (me);
783 Direction d = get_grob_direction (me);
785 = me->layout ()->get_dimension (ly_symbol2scm ("blot-diameter"));
786 Real stem_width = thickness (me);
787 Real half_space = Staff_symbol_referencer::staff_space (me) * 0.5;
788 Real y2 = robust_scm2double (me->get_property ("stem-end-position"), 0.0);
789 fl.translate_axis (y2 * half_space - d * blot / 2, Y_AXIS);
790 fl.translate_axis (stem_width / 2, X_AXIS);
797 move the stem to right of the notehead if it is up.
799 MAKE_SCHEME_CALLBACK (Stem, offset_callback, 1);
801 Stem::offset_callback (SCM smob)
803 Grob *me = unsmob_grob (smob);
806 if (Grob *f = first_head (me))
808 Interval head_wid = f->extent (f, X_AXIS);
811 if (is_invisible (me))
814 attach = Note_head::stem_attachment_coordinate (f, X_AXIS);
816 Direction d = get_grob_direction (me);
817 Real real_attach = head_wid.linear_combination (d * attach);
820 /* If not centered: correct for stem thickness. */
823 Real rule_thick = thickness (me);
824 r += -d * rule_thick * 0.5;
829 extract_grob_set (me, "rests", rests);
832 Grob *rest = rests.back ();
833 r = rest->extent (rest, X_AXIS).center ();
836 return scm_from_double (r);
840 Stem::get_beam (Grob *me)
842 SCM b = me->get_object ("beam");
843 return dynamic_cast<Spanner *> (unsmob_grob (b));
847 Stem::get_stem_info (Grob *me)
850 si.dir_ = get_grob_direction (me);
852 SCM scm_info = me->get_property ("stem-info");
853 si.ideal_y_ = scm_to_double (scm_car (scm_info));
854 si.shortest_y_ = scm_to_double (scm_cadr (scm_info));
858 MAKE_SCHEME_CALLBACK(Stem, calc_stem_info, 1);
860 Stem::calc_stem_info (SCM smob)
862 Grob *me = unsmob_grob (smob);
863 Direction my_dir = get_grob_direction (me);
867 programming_error ("no stem dir set");
871 Real staff_space = Staff_symbol_referencer::staff_space (me);
872 Grob *beam = get_beam (me);
876 (void) beam->get_property ("beaming");
879 Real beam_translation = Beam::get_beam_translation (beam);
880 Real beam_thickness = Beam::get_thickness (beam);
881 int beam_count = Beam::get_direction_beam_count (beam, my_dir);
883 = robust_scm2double (me->get_property ("length-fraction"), 1.0);
885 /* Simple standard stem length */
886 SCM details = me->get_property ("details");
887 SCM lengths = scm_cdr (scm_assq (ly_symbol2scm ("beamed-lengths"), details));
890 = scm_to_double (robust_list_ref (beam_count - 1, lengths))
894 /* stem only extends to center of beam
896 - 0.5 * beam_thickness;
898 /* Condition: sane minimum free stem length (chord to beams) */
899 lengths = scm_cdr (scm_assq (ly_symbol2scm ("beamed-minimum-free-lengths"), details));
901 Real ideal_minimum_free
902 = scm_to_double (robust_list_ref (beam_count - 1, lengths))
906 Real height_of_my_trem = 0.0;
907 Grob *trem = unsmob_grob (me->get_object ("tremolo-flag"));
909 height_of_my_trem = trem->extent (trem, Y_AXIS).length ()
910 /* hack a bit of space around the trem. */
914 It seems that also for ideal minimum length, we must use
915 the maximum beam count (for this direction):
917 \score{ \notes\relative c''{ [a8 a32] }}
919 must be horizontal. */
920 Real height_of_my_beams = beam_thickness
921 + (beam_count - 1) * beam_translation;
923 Real ideal_minimum_length = ideal_minimum_free
926 /* stem only extends to center of beam */
927 - 0.5 * beam_thickness;
929 ideal_length = max (ideal_length, ideal_minimum_length);
931 /* Convert to Y position, calculate for dir == UP */
933 = /* staff positions */
934 head_positions (me)[my_dir] * 0.5
935 * my_dir * staff_space;
936 Real ideal_y = note_start + ideal_length;
938 /* Conditions for Y position */
940 /* Lowest beam of (UP) beam must never be lower than second staffline
944 Although this (additional) rule is probably correct,
945 I expect that highest beam (UP) should also never be lower
946 than middle staffline, just as normal stems.
950 Obviously not for grace beams.
952 Also, not for knees. Seems to be a good thing. */
953 bool no_extend_b = to_boolean (me->get_property ("no-stem-extend"));
954 bool is_knee = to_boolean (beam->get_property ("knee"));
955 if (!no_extend_b && !is_knee)
957 /* Highest beam of (UP) beam must never be lower than middle
959 ideal_y = max (ideal_y, 0.0);
960 /* Lowest beam of (UP) beam must never be lower than second staffline */
961 ideal_y = max (ideal_y, (-staff_space
962 - beam_thickness + height_of_my_beams));
965 ideal_y -= robust_scm2double (beam->get_property ("shorten"), 0);
967 SCM bemfl = scm_cdr (scm_assq (ly_symbol2scm ("beamed-extreme-minimum-free-lengths"),
971 = scm_to_double (robust_list_ref (beam_count - 1, bemfl))
975 Real minimum_length = max (minimum_free, height_of_my_trem)
977 /* stem only extends to center of beam */
978 - 0.5 * beam_thickness;
981 Real minimum_y = note_start + minimum_length;
982 Real shortest_y = minimum_y * my_dir;
984 return scm_list_2 (scm_from_double (ideal_y),
985 scm_from_double (shortest_y));
989 Stem::beam_multiplicity (Grob *stem)
991 SCM beaming = stem->get_property ("beaming");
992 Slice le = int_list_to_slice (scm_car (beaming));
993 Slice ri = int_list_to_slice (scm_cdr (beaming));
998 /* FIXME: Too many properties */
1000 "The stem represent the graphical stem. "
1001 "In addition, it internally connects note heads, beams and"
1003 "Rests and whole notes have invisible stems."
1005 "\n\nThe following properties may be set in the details list."
1007 "@item beamed-lengths \n"
1008 "list of stem lengths given beam multiplicity. \n"
1009 "@item beamed-minimum-free-lengths \n"
1010 "list of normal minimum free stem lengths (chord to beams) given beam multiplicity.\n"
1011 "@item beamed-extreme-minimum-free-lengths\n"
1012 "list of extreme minimum free stem lengths (chord to beams) given beam multiplicity.\n"
1014 "Default stem lengths. The list gives a length for each flag-count.\n"
1015 "@item stem-shorten\n"
1016 "How much a stem in a forced direction "
1017 "should be shortened. The list gives an amount depending on the number "
1026 "default-direction "
1035 "neutral-direction "
1040 "stem-end-position "
1048 /****************************************************************/
1050 Stem_info::Stem_info ()
1052 ideal_y_ = shortest_y_ = 0;
1057 Stem_info::scale (Real x)