2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 1996--2011 Han-Wen Nienhuys <hanwen@xs4all.nl>
5 Jan Nieuwenhuizen <janneke@gnu.org>
7 TODO: This is way too hairy
11 Stem-end, chord-start, etc. is all confusing naming.
13 LilyPond is free software: you can redistribute it and/or modify
14 it under the terms of the GNU General Public License as published by
15 the Free Software Foundation, either version 3 of the License, or
16 (at your option) any later version.
18 LilyPond is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 GNU General Public License for more details.
23 You should have received a copy of the GNU General Public License
24 along with LilyPond. If not, see <http://www.gnu.org/licenses/>.
30 #include <cmath> // rint
34 #include "directional-element-interface.hh"
35 #include "dot-column.hh"
36 #include "font-interface.hh"
37 #include "international.hh"
40 #include "note-head.hh"
41 #include "output-def.hh"
42 #include "paper-column.hh"
43 #include "pointer-group-interface.hh"
45 #include "rhythmic-head.hh"
46 #include "side-position-interface.hh"
47 #include "staff-symbol-referencer.hh"
48 #include "stem-tremolo.hh"
52 Stem::set_beaming (Grob *me, int beam_count, Direction d)
54 SCM pair = me->get_property ("beaming");
56 if (!scm_is_pair (pair))
58 pair = scm_cons (SCM_EOL, SCM_EOL);
59 me->set_property ("beaming", pair);
62 SCM lst = index_get_cell (pair, d);
64 for (int i = 0; i < beam_count; i++)
65 lst = scm_cons (scm_from_int (i), lst);
69 index_set_cell (pair, d, lst);
73 Stem::get_beaming (Grob *me, Direction d)
75 SCM pair = me->get_property ("beaming");
76 if (!scm_is_pair (pair))
79 SCM lst = index_get_cell (pair, d);
81 int len = scm_ilength (lst);
86 Stem::head_positions (Grob *me)
90 Drul_array<Grob *> e (extremal_heads (me));
91 return Interval (Staff_symbol_referencer::get_position (e[DOWN]),
92 Staff_symbol_referencer::get_position (e[UP]));
98 Stem::chord_start_y (Grob *me)
100 Interval hp = head_positions (me);
102 return hp[get_grob_direction (me)] * Staff_symbol_referencer::staff_space (me)
108 Stem::set_stemend (Grob *me, Real se)
111 Direction d = get_grob_direction (me);
113 if (d && d * head_positions (me)[get_grob_direction (me)] >= se * d)
114 me->warning (_ ("weird stem size, check for narrow beams"));
116 me->set_property ("stem-end-position", scm_from_double (se));
119 /* Note head that determines hshift for upstems
120 WARNING: triggers direction */
122 Stem::support_head (Grob *me)
124 extract_grob_set (me, "note-heads", heads);
125 if (heads.size () == 1)
128 return first_head (me);
132 Stem::head_count (Grob *me)
134 return Pointer_group_interface::count (me, ly_symbol2scm ("note-heads"));
137 /* The note head which forms one end of the stem.
138 WARNING: triggers direction */
140 Stem::first_head (Grob *me)
142 Direction d = get_grob_direction (me);
144 return extremal_heads (me)[-d];
148 /* The note head opposite to the first head. */
150 Stem::last_head (Grob *me)
152 Direction d = get_grob_direction (me);
154 return extremal_heads (me)[d];
159 START is part where stem reaches `last' head.
161 This function returns a drul with (bottom-head, top-head).
164 Stem::extremal_heads (Grob *me)
166 const int inf = INT_MAX;
167 Drul_array<int> extpos;
171 Drul_array<Grob *> exthead (0, 0);
172 extract_grob_set (me, "note-heads", heads);
174 for (vsize i = heads.size (); i--;)
177 int p = Staff_symbol_referencer::get_rounded_position (n);
182 if (d * p > d * extpos[d])
188 while (flip (&d) != DOWN);
193 /* The positions, in ascending order. */
195 Stem::note_head_positions (Grob *me)
198 extract_grob_set (me, "note-heads", heads);
200 for (vsize i = heads.size (); i--;)
203 int p = Staff_symbol_referencer::get_rounded_position (n);
208 vector_sort (ps, less<int> ());
213 Stem::add_head (Grob *me, Grob *n)
215 n->set_object ("stem", me->self_scm ());
217 if (Note_head::has_interface (n))
218 Pointer_group_interface::add_grob (me, ly_symbol2scm ("note-heads"), n);
219 else if (Rest::has_interface (n))
220 Pointer_group_interface::add_grob (me, ly_symbol2scm ("rests"), n);
224 Stem::is_invisible (Grob *me)
226 return !is_normal_stem (me)
227 && (robust_scm2double (me->get_property ("stemlet-length"),
232 Stem::is_normal_stem (Grob *me)
234 return head_count (me) && scm_to_int (me->get_property ("duration-log")) >= 1;
237 MAKE_SCHEME_CALLBACK (Stem, pure_height, 3)
239 Stem::pure_height (SCM smob,
243 Grob *me = unsmob_grob (smob);
246 if (!is_normal_stem (me))
247 return ly_interval2scm (iv);
249 Real ss = Staff_symbol_referencer::staff_space (me);
250 Real rad = Staff_symbol_referencer::staff_radius (me);
252 if (!to_boolean (me->get_property ("cross-staff")))
254 Real len_in_halfspaces;
255 SCM user_set_len_scm = me->get_property_data ("length");
256 if (scm_is_number (user_set_len_scm))
257 len_in_halfspaces = scm_to_double (user_set_len_scm);
259 len_in_halfspaces = scm_to_double (calc_length (smob));
260 Real len = len_in_halfspaces * ss / 2;
261 Direction dir = get_grob_direction (me);
263 Interval hp = head_positions (me);
265 iv = Interval (0, len);
267 iv = Interval (-len, 0);
271 iv.translate (hp[dir] * ss / 2);
272 iv.add_point (hp[-dir] * ss / 2);
275 /* extend the stem (away from the head) to cover the staff */
277 iv[UP] = max (iv[UP], rad * ss);
279 iv[DOWN] = min (iv[DOWN], -rad * ss);
282 iv = Interval (-rad * ss, rad * ss);
284 return ly_interval2scm (iv);
287 MAKE_SCHEME_CALLBACK (Stem, calc_stem_end_position, 1)
289 Stem::calc_stem_end_position (SCM smob)
291 Grob *me = unsmob_grob (smob);
293 if (!head_count (me))
294 return scm_from_double (0.0);
296 if (Grob *beam = get_beam (me))
298 (void) beam->get_property ("quantized-positions");
299 return me->get_property ("stem-end-position");
304 /* WARNING: IN HALF SPACES */
305 Real length = robust_scm2double (me->get_property ("length"), 7);
307 Direction dir = get_grob_direction (me);
308 Interval hp = head_positions (me);
309 Real stem_end = dir ? hp[dir] + dir * length : 0;
311 /* TODO: change name to extend-stems to staff/center/'() */
312 bool no_extend = to_boolean (me->get_property ("no-stem-extend"));
313 if (!no_extend && dir * stem_end < 0)
316 return scm_from_double (stem_end);
319 /* Length is in half-spaces (or: positions) here. */
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);
330 Real staff_rad = Staff_symbol_referencer::staff_radius (me);
332 SCM s = ly_assoc_get (ly_symbol2scm ("lengths"), details, SCM_EOL);
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 = ly_assoc_get (ly_symbol2scm ("stem-shorten"), details, SCM_EOL);
344 SCM scm_shorten = scm_is_pair (sshorten)
345 ? robust_list_ref (max (duration_log (me) - 2, 0), sshorten) : SCM_EOL;
346 Real shorten_property = 2 * robust_scm2double (scm_shorten, 0);
347 /* change in length between full-size and shortened stems is executed gradually.
348 "transition area" = stems between full-sized and fully-shortened.
350 Real quarter_stem_length = 2 * scm_to_double (robust_list_ref (0, s));
351 /* shortening_step = difference in length between consecutive stem lengths
352 in transition area. The bigger the difference between full-sized
353 and shortened stems, the bigger shortening_step is.
354 (but not greater than 1/2 and not smaller than 1/4).
355 value 6 is heuristic; it determines the suggested transition slope steepnesas.
357 Real shortening_step = min (max (0.25, (shorten_property / 6)), 0.5);
358 /* Shortening of unflagged stems should begin on the first stem that sticks
359 more than 1 staffspace (2 units) out of the staff.
360 Shortening of flagged stems begins in the same moment as unflagged ones,
361 but not earlier than on the middle line note.
363 Real which_step = (min (1.0, quarter_stem_length - (2 * staff_rad) - 2.0)) + abs (hp[dir]);
364 Real shorten = min (max (0.0, (shortening_step * which_step)), shorten_property);
369 length *= robust_scm2double (me->get_property ("length-fraction"), 1.0);
372 Grob *t_flag = unsmob_grob (me->get_object ("tremolo-flag"));
373 if (t_flag && !unsmob_grob (me->get_object ("beam")))
375 /* Crude hack: add extra space if tremolo flag is there.
377 We can't do this for the beam, since we get into a loop
378 (Stem_tremolo::raw_stencil () looks at the beam.) --hwn */
381 + 2 * Stem_tremolo::vertical_length (t_flag) / ss;
383 /* We don't want to add the whole extent of the flag because the trem
384 and the flag can overlap partly. beam_translation gives a good
388 Real beam_trans = Stem_tremolo::get_beam_translation (t_flag);
389 /* the obvious choice is (durlog - 2) here, but we need a bit more space. */
390 minlen += 2 * (durlog - 1.5) * beam_trans;
392 /* up-stems need even a little more space to avoid collisions. This
393 needs to be in sync with the tremolo positioning code in
394 Stem_tremolo::print */
396 minlen += beam_trans;
398 length = max (length, minlen + 1.0);
401 return scm_from_double (length);
403 /* The log of the duration (Number of hooks on the flag minus two) */
405 Stem::duration_log (Grob *me)
407 SCM s = me->get_property ("duration-log");
408 return (scm_is_number (s)) ? scm_to_int (s) : 2;
411 MAKE_SCHEME_CALLBACK (Stem, calc_positioning_done, 1);
413 Stem::calc_positioning_done (SCM smob)
415 Grob *me = unsmob_grob (smob);
416 if (!head_count (me))
419 me->set_property ("positioning-done", SCM_BOOL_T);
421 extract_grob_set (me, "note-heads", ro_heads);
422 vector<Grob *> heads (ro_heads);
423 vector_sort (heads, position_less);
424 Direction dir = get_grob_direction (me);
429 Real thick = thickness (me);
431 Grob *hed = support_head (me);
434 programming_error ("Stem dir must be up or down.");
436 set_grob_direction (me, dir);
439 bool is_harmonic_centered = false;
440 for (vsize i = 0; i < heads.size (); i++)
441 is_harmonic_centered = is_harmonic_centered
442 || heads[i]->get_property ("style") == ly_symbol2scm ("harmonic");
443 is_harmonic_centered = is_harmonic_centered && is_invisible (me);
445 Real w = hed->extent (hed, X_AXIS)[dir];
446 for (vsize i = 0; i < heads.size (); i++)
448 Real amount = w - heads[i]->extent (heads[i], X_AXIS)[dir];
450 if (is_harmonic_centered)
452 = hed->extent (hed, X_AXIS).linear_combination (CENTER)
453 - heads[i]->extent (heads[i], X_AXIS).linear_combination (CENTER);
455 heads[i]->translate_axis (amount, X_AXIS);
458 Real lastpos = Real (Staff_symbol_referencer::get_position (heads[0]));
459 for (vsize i = 1; i < heads.size (); i++)
461 Real p = Staff_symbol_referencer::get_position (heads[i]);
462 Real dy = fabs (lastpos - p);
465 dy should always be 0.5, 0.0, 1.0, but provide safety margin
472 Real ell = heads[i]->extent (heads[i], X_AXIS).length ();
474 Direction d = get_grob_direction (me);
476 Reversed head should be shifted ell-thickness, but this
477 looks too crowded, so we only shift ell-0.5*thickness.
479 This leads to assymetry: Normal heads overlap the
480 stem 100% whereas reversed heads only overlaps the
484 Real reverse_overlap = 0.5;
485 heads[i]->translate_axis ((ell - thick * reverse_overlap) * d,
488 if (is_invisible (me))
489 heads[i]->translate_axis (-thick * (2 - reverse_overlap) * d,
494 For some cases we should kern some more: when the
495 distance between the next or prev note is too large, we'd
496 get large white gaps, eg.
517 MAKE_SCHEME_CALLBACK (Stem, calc_direction, 1);
519 Stem::calc_direction (SCM smob)
521 Grob *me = unsmob_grob (smob);
522 Direction dir = CENTER;
523 if (Grob *beam = unsmob_grob (me->get_object ("beam")))
525 SCM ignore_me = beam->get_property ("direction");
527 dir = get_grob_direction (me);
531 SCM dd = me->get_property ("default-direction");
534 return me->get_property ("neutral-direction");
537 return scm_from_int (dir);
540 MAKE_SCHEME_CALLBACK (Stem, calc_default_direction, 1);
542 Stem::calc_default_direction (SCM smob)
544 Grob *me = unsmob_grob (smob);
546 Direction dir = CENTER;
547 int staff_center = 0;
548 Interval hp = head_positions (me);
551 int udistance = (int) (UP * hp[UP] - staff_center);
552 int ddistance = (int) (DOWN * hp[DOWN] - staff_center);
554 dir = Direction (sign (ddistance - udistance));
557 return scm_from_int (dir);
560 MAKE_SCHEME_CALLBACK (Stem, height, 1);
562 Stem::height (SCM smob)
564 Grob *me = unsmob_grob (smob);
565 if (!is_normal_stem (me))
566 return ly_interval2scm (Interval ());
568 Direction dir = get_grob_direction (me);
570 Grob *beam = get_beam (me);
573 /* trigger set-stem-lengths. */
574 beam->get_property ("quantized-positions");
578 Can't get_stencil (), since that would cache stencils too early.
579 This causes problems with beams.
581 Stencil *stencil = unsmob_stencil (print (smob));
582 Interval iv = stencil ? stencil->extent (Y_AXIS) : Interval ();
587 programming_error ("no stem direction");
590 iv[dir] += dir * Beam::get_beam_thickness (beam) * 0.5;
593 return ly_interval2scm (iv);
597 Stem::stem_end_position (Grob *me)
599 return robust_scm2double (me->get_property ("stem-end-position"), 0);
602 MAKE_SCHEME_CALLBACK (Stem, width, 1);
606 Grob *me = unsmob_grob (e);
610 if (is_invisible (me))
614 r = Interval (-1, 1);
615 r *= thickness (me) / 2;
618 return ly_interval2scm (r);
622 Stem::thickness (Grob *me)
624 return scm_to_double (me->get_property ("thickness"))
625 * Staff_symbol_referencer::line_thickness (me);
628 MAKE_SCHEME_CALLBACK (Stem, calc_stem_begin_position, 1);
630 Stem::calc_stem_begin_position (SCM smob)
632 Grob *me = unsmob_grob (smob);
633 Direction d = get_grob_direction (me);
634 Real half_space = Staff_symbol_referencer::staff_space (me) * 0.5;
636 = to_boolean (me->get_property ("avoid-note-head"))
640 Real pos = Staff_symbol_referencer::get_position (lh);
642 if (Grob *head = support_head (me))
644 Interval head_height = head->extent (head, Y_AXIS);
645 Real y_attach = Note_head::stem_attachment_coordinate (head, Y_AXIS);
647 y_attach = head_height.linear_combination (y_attach);
648 pos += d * y_attach / half_space;
651 return scm_from_double (pos);
654 MAKE_SCHEME_CALLBACK (Stem, print, 1);
656 Stem::print (SCM smob)
658 Grob *me = unsmob_grob (smob);
659 Grob *beam = get_beam (me);
662 Direction d = get_grob_direction (me);
664 Real stemlet_length = robust_scm2double (me->get_property ("stemlet-length"),
666 bool stemlet = stemlet_length > 0.0;
668 /* TODO: make the stem start a direction ?
669 This is required to avoid stems passing in tablature chords. */
671 = to_boolean (me->get_property ("avoid-note-head"))
678 if (!lh && stemlet && !beam)
681 if (lh && robust_scm2int (lh->get_property ("duration-log"), 0) < 1)
684 if (is_invisible (me))
687 Real y2 = robust_scm2double (me->get_property ("stem-end-position"), 0.0);
689 Real half_space = Staff_symbol_referencer::staff_space (me) * 0.5;
692 y2 = robust_scm2double (me->get_property ("stem-begin-position"), 0.0);
695 Real beam_translation = Beam::get_beam_translation (beam);
696 Real beam_thickness = Beam::get_beam_thickness (beam);
697 int beam_count = beam_multiplicity (me).length () + 1;
700 * (0.5 * beam_thickness
701 + beam_translation * max (0, (beam_count - 1))
702 + stemlet_length) / half_space;
705 Interval stem_y (min (y1, y2), max (y2, y1));
708 Real stem_width = thickness (me);
710 = me->layout ()->get_dimension (ly_symbol2scm ("blot-diameter"));
712 Box b = Box (Interval (-stem_width / 2, stem_width / 2),
713 Interval (stem_y[DOWN] * half_space, stem_y[UP] * half_space));
715 Stencil ss = Lookup::round_filled_box (b, blot);
716 mol.add_stencil (ss);
718 return mol.smobbed_copy ();
722 move the stem to right of the notehead if it is up.
724 MAKE_SCHEME_CALLBACK (Stem, offset_callback, 1);
726 Stem::offset_callback (SCM smob)
728 Grob *me = unsmob_grob (smob);
730 extract_grob_set (me, "rests", rests);
733 Grob *rest = rests.back ();
734 Real r = rest->extent (rest, X_AXIS).center ();
735 return scm_from_double (r);
738 if (Grob *f = first_head (me))
740 Interval head_wid = f->extent (f, X_AXIS);
743 if (is_invisible (me))
746 attach = Note_head::stem_attachment_coordinate (f, X_AXIS);
748 Direction d = get_grob_direction (me);
749 Real real_attach = head_wid.linear_combination (d * attach);
750 Real r = real_attach;
752 /* If not centered: correct for stem thickness. */
755 Real rule_thick = thickness (me);
756 r += -d * rule_thick * 0.5;
758 return scm_from_double (r);
761 programming_error ("Weird stem.");
762 return scm_from_double (0.0);
766 Stem::get_beam (Grob *me)
768 SCM b = me->get_object ("beam");
769 return dynamic_cast<Spanner *> (unsmob_grob (b));
773 Stem::get_stem_info (Grob *me)
776 si.dir_ = get_grob_direction (me);
778 SCM scm_info = me->get_property ("stem-info");
779 si.ideal_y_ = scm_to_double (scm_car (scm_info));
780 si.shortest_y_ = scm_to_double (scm_cadr (scm_info));
784 MAKE_SCHEME_CALLBACK (Stem, calc_stem_info, 1);
786 Stem::calc_stem_info (SCM smob)
788 Grob *me = unsmob_grob (smob);
789 Direction my_dir = get_grob_direction (me);
793 programming_error ("no stem dir set");
797 Real staff_space = Staff_symbol_referencer::staff_space (me);
798 Grob *beam = get_beam (me);
802 (void) beam->get_property ("beaming");
805 Real beam_translation = Beam::get_beam_translation (beam);
806 Real beam_thickness = Beam::get_beam_thickness (beam);
807 int beam_count = Beam::get_direction_beam_count (beam, my_dir);
809 = robust_scm2double (me->get_property ("length-fraction"), 1.0);
811 /* Simple standard stem length */
812 SCM details = me->get_property ("details");
813 SCM lengths = ly_assoc_get (ly_symbol2scm ("beamed-lengths"), details, SCM_EOL);
816 = (scm_is_pair (lengths)
817 ? (scm_to_double (robust_list_ref (beam_count - 1, lengths))
821 stem only extends to center of beam
823 - 0.5 * beam_thickness)
826 /* Condition: sane minimum free stem length (chord to beams) */
827 lengths = ly_assoc_get (ly_symbol2scm ("beamed-minimum-free-lengths"),
830 Real ideal_minimum_free
831 = (scm_is_pair (lengths)
832 ? (scm_to_double (robust_list_ref (beam_count - 1, lengths))
837 Real height_of_my_trem = 0.0;
838 Grob *trem = unsmob_grob (me->get_object ("tremolo-flag"));
842 = Stem_tremolo::vertical_length (trem)
843 /* hack a bit of space around the trem. */
848 It seems that also for ideal minimum length, we must use
849 the maximum beam count (for this direction):
851 \score { \relative c'' { a8[ a32] } }
853 must be horizontal. */
854 Real height_of_my_beams = beam_thickness
855 + (beam_count - 1) * beam_translation;
857 Real ideal_minimum_length = ideal_minimum_free
860 /* stem only extends to center of beam */
861 - 0.5 * beam_thickness;
863 ideal_length = max (ideal_length, ideal_minimum_length);
865 /* Convert to Y position, calculate for dir == UP */
867 = /* staff positions */
868 head_positions (me)[my_dir] * 0.5
869 * my_dir * staff_space;
870 Real ideal_y = note_start + ideal_length;
872 /* Conditions for Y position */
874 /* Lowest beam of (UP) beam must never be lower than second staffline
878 Although this (additional) rule is probably correct,
879 I expect that highest beam (UP) should also never be lower
880 than middle staffline, just as normal stems.
884 Obviously not for grace beams.
886 Also, not for knees. Seems to be a good thing. */
887 bool no_extend = to_boolean (me->get_property ("no-stem-extend"));
888 bool is_knee = to_boolean (beam->get_property ("knee"));
889 if (!no_extend && !is_knee)
891 /* Highest beam of (UP) beam must never be lower than middle
893 ideal_y = max (ideal_y, 0.0);
894 /* Lowest beam of (UP) beam must never be lower than second staffline */
895 ideal_y = max (ideal_y, (-staff_space
896 - beam_thickness + height_of_my_beams));
899 ideal_y -= robust_scm2double (beam->get_property ("shorten"), 0);
901 SCM bemfl = ly_assoc_get (ly_symbol2scm ("beamed-extreme-minimum-free-lengths"),
905 = (scm_is_pair (bemfl)
906 ? (scm_to_double (robust_list_ref (beam_count - 1, bemfl))
911 Real minimum_length = max (minimum_free, height_of_my_trem)
913 /* stem only extends to center of beam */
914 - 0.5 * beam_thickness;
917 Real minimum_y = note_start + minimum_length;
918 Real shortest_y = minimum_y * my_dir;
920 return scm_list_2 (scm_from_double (ideal_y),
921 scm_from_double (shortest_y));
925 Stem::beam_multiplicity (Grob *stem)
927 SCM beaming = stem->get_property ("beaming");
928 Slice le = int_list_to_slice (scm_car (beaming));
929 Slice ri = int_list_to_slice (scm_cdr (beaming));
935 Stem::is_cross_staff (Grob *stem)
937 Grob *beam = unsmob_grob (stem->get_object ("beam"));
938 return beam && Beam::is_cross_staff (beam);
941 MAKE_SCHEME_CALLBACK (Stem, calc_cross_staff, 1)
943 Stem::calc_cross_staff (SCM smob)
945 return scm_from_bool (is_cross_staff (unsmob_grob (smob)));
949 Stem::flag (Grob *me)
951 return unsmob_grob (me->get_object ("flag"));
954 /* FIXME: Too many properties */
956 "The stem represents the graphical stem. In addition, it"
957 " internally connects note heads, beams, and tremolos. Rests"
958 " and whole notes have invisible stems.\n"
960 "The following properties may be set in the @code{details}"
964 "@item beamed-lengths\n"
965 "List of stem lengths given beam multiplicity.\n"
966 "@item beamed-minimum-free-lengths\n"
967 "List of normal minimum free stem lengths (chord to beams)"
968 " given beam multiplicity.\n"
969 "@item beamed-extreme-minimum-free-lengths\n"
970 "List of extreme minimum free stem lengths (chord to beams)"
971 " given beam multiplicity.\n"
973 "Default stem lengths. The list gives a length for each"
975 "@item stem-shorten\n"
976 "How much a stem in a forced direction should be shortened."
977 " The list gives an amount depending on the number of flags"
985 "beamlet-default-length "
986 "beamlet-max-length-proportion "
1001 "stem-begin-position "
1002 "stem-end-position "
1009 /****************************************************************/
1011 Stem_info::Stem_info ()
1013 ideal_y_ = shortest_y_ = 0;
1018 Stem_info::scale (Real x)