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 me->set_property ("positioning-done", SCM_BOOL_T);
415 extract_grob_set (me, "note-heads", ro_heads);
416 vector<Grob*> heads (ro_heads);
417 vector_sort (heads, position_less);
418 Direction dir = get_grob_direction (me);
423 Real thick = thickness (me);
425 Grob *hed = support_head (me);
428 programming_error ("Stem dir must be up or down.");
430 set_grob_direction (me, dir);
433 bool is_harmonic_centered = false;
434 for (vsize i = 0; i < heads.size (); i++)
435 is_harmonic_centered = is_harmonic_centered
436 || heads[i]->get_property ("style") == ly_symbol2scm ("harmonic");
437 is_harmonic_centered = is_harmonic_centered && is_invisible (me);
439 Real w = hed->extent (hed, X_AXIS)[dir];
440 for (vsize i = 0; i < heads.size (); i++)
442 Real amount = w - heads[i]->extent (heads[i], X_AXIS)[dir];
444 if (is_harmonic_centered)
446 hed->extent (hed, X_AXIS).linear_combination (CENTER)
447 - heads[i]->extent (heads[i], X_AXIS).linear_combination (CENTER);
449 heads[i]->translate_axis (amount, X_AXIS);
452 Real lastpos = Real (Staff_symbol_referencer::get_position (heads[0]));
453 for (vsize i = 1; i < heads.size (); i++)
455 Real p = Staff_symbol_referencer::get_position (heads[i]);
456 Real dy = fabs (lastpos- p);
459 dy should always be 0.5, 0.0, 1.0, but provide safety margin
466 Real ell = heads[i]->extent (heads[i], X_AXIS).length ();
468 Direction d = get_grob_direction (me);
470 Reversed head should be shifted ell-thickness, but this
471 looks too crowded, so we only shift ell-0.5*thickness.
473 This leads to assymetry: Normal heads overlap the
474 stem 100% whereas reversed heads only overlaps the
478 Real reverse_overlap = 0.5;
479 heads[i]->translate_axis ((ell - thick * reverse_overlap) * d,
482 if (is_invisible (me))
483 heads[i]->translate_axis (-thick * (2 - reverse_overlap) * d,
488 For some cases we should kern some more: when the
489 distance between the next or prev note is too large, we'd
490 get large white gaps, eg.
511 MAKE_SCHEME_CALLBACK(Stem, calc_direction, 1);
513 Stem::calc_direction (SCM smob)
515 Grob *me = unsmob_grob (smob);
516 Direction dir = CENTER;
517 if (Grob *beam = unsmob_grob (me->get_object ("beam")))
519 SCM ignore_me = beam->get_property ("direction");
521 dir = get_grob_direction (me);
525 SCM dd = me->get_property ("default-direction");
528 return me->get_property ("neutral-direction");
531 return scm_from_int (dir);
534 MAKE_SCHEME_CALLBACK(Stem, calc_default_direction, 1);
536 Stem::calc_default_direction (SCM smob)
538 Grob *me = unsmob_grob (smob);
540 Direction dir = CENTER;
541 int staff_center = 0;
542 Interval hp = head_positions (me);
545 int udistance = (int) (UP * hp[UP] - staff_center);
546 int ddistance = (int) (DOWN * hp[DOWN] - staff_center);
548 dir = Direction (sign (ddistance - udistance));
551 return scm_from_int (dir);
555 MAKE_SCHEME_CALLBACK (Stem, height, 1);
557 Stem::height (SCM smob)
559 Grob *me = unsmob_grob (smob);
561 Direction dir = get_grob_direction (me);
565 UGH. Should be automatic
567 Grob *beam = get_beam (me);
570 /* trigger set-stem-lengths. */
571 beam->get_property ("quantized-positions");
575 Can't get_stencil(), since that would cache stencils too early.
576 This causes problems with beams.
578 Stencil *stencil = unsmob_stencil (print (smob));
579 Interval iv = stencil ? stencil->extent (Y_AXIS) : Interval();
584 programming_error ("no stem direction");
587 iv[dir] += dir * Beam::get_thickness (beam) * 0.5;
590 return ly_interval2scm (iv);
594 Stem::stem_end_position (Grob *me)
596 return robust_scm2double (me->get_property ("stem-end-position"), 0);
600 Stem::flag (Grob *me)
602 int log = duration_log (me);
604 || unsmob_grob (me->get_object ("beam")))
608 TODO: maybe property stroke-style should take different values,
609 e.g. "" (i.e. no stroke), "single" and "double" (currently, it's
613 SCM flag_style_scm = me->get_property ("flag-style");
614 if (scm_is_symbol (flag_style_scm))
615 flag_style = ly_symbol2string (flag_style_scm);
617 if (flag_style == "no-flag")
622 string staffline_offs;
623 if (flag_style == "mensural")
624 /* Mensural notation: For notes on staff lines, use different
625 flags than for notes between staff lines. The idea is that
626 flags are always vertically aligned with the staff lines,
627 regardless if the note head is on a staff line or between two
628 staff lines. In other words, the inner end of a flag always
629 touches a staff line.
634 int p = (int) (rint (stem_end_position (me)));
636 = Staff_symbol_referencer::on_line (me, p) ? "0" : "1";
639 staffline_offs = "2";
644 char dir = (get_grob_direction (me) == UP) ? 'u' : 'd';
645 string font_char = flag_style
646 + to_string (dir) + staffline_offs + to_string (log);
647 Font_metric *fm = Font_interface::get_default_font (me);
648 Stencil flag = fm->find_by_name ("flags." + font_char);
649 if (flag.is_empty ())
650 me->warning (_f ("flag `%s' not found", font_char));
652 SCM stroke_style_scm = me->get_property ("stroke-style");
653 if (scm_is_string (stroke_style_scm))
655 string stroke_style = ly_scm2string (stroke_style_scm);
656 if (!stroke_style.empty ())
658 string font_char = to_string (dir) + stroke_style;
659 Stencil stroke = fm->find_by_name ("flags." + font_char);
660 if (stroke.is_empty ())
661 me->warning (_f ("flag stroke `%s' not found", font_char));
663 flag.add_stencil (stroke);
670 MAKE_SCHEME_CALLBACK (Stem, width, 1);
674 Grob *me = unsmob_grob (e);
678 if (is_invisible (me))
680 else if (unsmob_grob (me->get_object ("beam"))
681 || abs (duration_log (me)) <= 2)
683 r = Interval (-1, 1);
684 r *= thickness (me) / 2;
688 r = Interval (-1, 1) * thickness (me) * 0.5;
689 r.unite (flag (me).extent (X_AXIS));
691 return ly_interval2scm (r);
695 Stem::thickness (Grob *me)
697 return scm_to_double (me->get_property ("thickness"))
698 * Staff_symbol_referencer::line_thickness (me);
701 MAKE_SCHEME_CALLBACK (Stem, print, 1);
703 Stem::print (SCM smob)
705 Grob *me = unsmob_grob (smob);
706 Grob *beam = get_beam (me);
709 Direction d = get_grob_direction (me);
711 Real stemlet_length = robust_scm2double (me->get_property ("stemlet-length"),
713 bool stemlet = stemlet_length > 0.0;
715 /* TODO: make the stem start a direction ?
716 This is required to avoid stems passing in tablature chords. */
718 = to_boolean (me->get_property ("avoid-note-head"))
725 if (!lh && stemlet && !beam)
728 if (is_invisible (me))
731 Real y2 = robust_scm2double (me->get_property ("stem-end-position"), 0.0);
733 Real half_space = Staff_symbol_referencer::staff_space (me) * 0.5;
736 y2 = Staff_symbol_referencer::get_position (lh);
739 Real beam_translation = Beam::get_beam_translation (beam);
740 Real beam_thickness = Beam::get_thickness (beam);
741 int beam_count = beam_multiplicity (me).length () + 1;
744 * (0.5 * beam_thickness
745 + beam_translation * max (0, (beam_count - 1))
746 + stemlet_length) / half_space;
749 Interval stem_y (min (y1, y2), max (y2, y1));
751 if (Grob *head = support_head (me))
754 must not take ledgers into account.
756 Interval head_height = head->extent (head, Y_AXIS);
757 Real y_attach = Note_head::stem_attachment_coordinate (head, Y_AXIS);
759 y_attach = head_height.linear_combination (y_attach);
760 stem_y[Direction (-d)] += d * y_attach / half_space;
764 Real stem_width = thickness (me);
766 = me->layout ()->get_dimension (ly_symbol2scm ("blot-diameter"));
768 Box b = Box (Interval (-stem_width / 2, stem_width / 2),
769 Interval (stem_y[DOWN] * half_space, stem_y[UP] * half_space));
771 Stencil ss = Lookup::round_filled_box (b, blot);
772 mol.add_stencil (ss);
774 mol.add_stencil (get_translated_flag (me));
776 return mol.smobbed_copy ();
780 Stem::get_translated_flag (Grob *me)
782 Stencil fl = flag (me);
785 Direction d = get_grob_direction (me);
787 = me->layout ()->get_dimension (ly_symbol2scm ("blot-diameter"));
788 Real stem_width = thickness (me);
789 Real half_space = Staff_symbol_referencer::staff_space (me) * 0.5;
790 Real y2 = robust_scm2double (me->get_property ("stem-end-position"), 0.0);
791 fl.translate_axis (y2 * half_space - d * blot / 2, Y_AXIS);
792 fl.translate_axis (stem_width / 2, X_AXIS);
799 move the stem to right of the notehead if it is up.
801 MAKE_SCHEME_CALLBACK (Stem, offset_callback, 1);
803 Stem::offset_callback (SCM smob)
805 Grob *me = unsmob_grob (smob);
807 extract_grob_set (me, "rests", rests);
810 Grob *rest = rests.back ();
811 Real r = rest->extent (rest, X_AXIS).center ();
812 return scm_from_double (r);
816 if (Grob *f = first_head (me))
818 Interval head_wid = f->extent (f, X_AXIS);
821 if (is_invisible (me))
824 attach = Note_head::stem_attachment_coordinate (f, X_AXIS);
826 Direction d = get_grob_direction (me);
827 Real real_attach = head_wid.linear_combination (d * attach);
828 Real r = real_attach;
830 /* If not centered: correct for stem thickness. */
833 Real rule_thick = thickness (me);
834 r += -d * rule_thick * 0.5;
836 return scm_from_double (r);
839 programming_error ("Weird stem.");
840 return scm_from_double (0.0);
844 Stem::get_beam (Grob *me)
846 SCM b = me->get_object ("beam");
847 return dynamic_cast<Spanner *> (unsmob_grob (b));
851 Stem::get_stem_info (Grob *me)
854 si.dir_ = get_grob_direction (me);
856 SCM scm_info = me->get_property ("stem-info");
857 si.ideal_y_ = scm_to_double (scm_car (scm_info));
858 si.shortest_y_ = scm_to_double (scm_cadr (scm_info));
862 MAKE_SCHEME_CALLBACK(Stem, calc_stem_info, 1);
864 Stem::calc_stem_info (SCM smob)
866 Grob *me = unsmob_grob (smob);
867 Direction my_dir = get_grob_direction (me);
871 programming_error ("no stem dir set");
875 Real staff_space = Staff_symbol_referencer::staff_space (me);
876 Grob *beam = get_beam (me);
880 (void) beam->get_property ("beaming");
883 Real beam_translation = Beam::get_beam_translation (beam);
884 Real beam_thickness = Beam::get_thickness (beam);
885 int beam_count = Beam::get_direction_beam_count (beam, my_dir);
887 = robust_scm2double (me->get_property ("length-fraction"), 1.0);
889 /* Simple standard stem length */
890 SCM details = me->get_property ("details");
891 SCM lengths = scm_cdr (scm_assq (ly_symbol2scm ("beamed-lengths"), details));
894 = scm_to_double (robust_list_ref (beam_count - 1, lengths))
898 /* stem only extends to center of beam
900 - 0.5 * beam_thickness;
902 /* Condition: sane minimum free stem length (chord to beams) */
903 lengths = scm_cdr (scm_assq (ly_symbol2scm ("beamed-minimum-free-lengths"), details));
905 Real ideal_minimum_free
906 = scm_to_double (robust_list_ref (beam_count - 1, lengths))
910 Real height_of_my_trem = 0.0;
911 Grob *trem = unsmob_grob (me->get_object ("tremolo-flag"));
913 height_of_my_trem = trem->extent (trem, Y_AXIS).length ()
914 /* hack a bit of space around the trem. */
918 It seems that also for ideal minimum length, we must use
919 the maximum beam count (for this direction):
921 \score{ \notes\relative c''{ [a8 a32] }}
923 must be horizontal. */
924 Real height_of_my_beams = beam_thickness
925 + (beam_count - 1) * beam_translation;
927 Real ideal_minimum_length = ideal_minimum_free
930 /* stem only extends to center of beam */
931 - 0.5 * beam_thickness;
933 ideal_length = max (ideal_length, ideal_minimum_length);
935 /* Convert to Y position, calculate for dir == UP */
937 = /* staff positions */
938 head_positions (me)[my_dir] * 0.5
939 * my_dir * staff_space;
940 Real ideal_y = note_start + ideal_length;
942 /* Conditions for Y position */
944 /* Lowest beam of (UP) beam must never be lower than second staffline
948 Although this (additional) rule is probably correct,
949 I expect that highest beam (UP) should also never be lower
950 than middle staffline, just as normal stems.
954 Obviously not for grace beams.
956 Also, not for knees. Seems to be a good thing. */
957 bool no_extend_b = to_boolean (me->get_property ("no-stem-extend"));
958 bool is_knee = to_boolean (beam->get_property ("knee"));
959 if (!no_extend_b && !is_knee)
961 /* Highest beam of (UP) beam must never be lower than middle
963 ideal_y = max (ideal_y, 0.0);
964 /* Lowest beam of (UP) beam must never be lower than second staffline */
965 ideal_y = max (ideal_y, (-staff_space
966 - beam_thickness + height_of_my_beams));
969 ideal_y -= robust_scm2double (beam->get_property ("shorten"), 0);
971 SCM bemfl = scm_cdr (scm_assq (ly_symbol2scm ("beamed-extreme-minimum-free-lengths"),
975 = scm_to_double (robust_list_ref (beam_count - 1, bemfl))
979 Real minimum_length = max (minimum_free, height_of_my_trem)
981 /* stem only extends to center of beam */
982 - 0.5 * beam_thickness;
985 Real minimum_y = note_start + minimum_length;
986 Real shortest_y = minimum_y * my_dir;
988 return scm_list_2 (scm_from_double (ideal_y),
989 scm_from_double (shortest_y));
993 Stem::beam_multiplicity (Grob *stem)
995 SCM beaming = stem->get_property ("beaming");
996 Slice le = int_list_to_slice (scm_car (beaming));
997 Slice ri = int_list_to_slice (scm_cdr (beaming));
1002 /* FIXME: Too many properties */
1003 ADD_INTERFACE (Stem,
1004 "The stem represent the graphical stem. "
1005 "In addition, it internally connects note heads, beams and"
1007 "Rests and whole notes have invisible stems."
1009 "\n\nThe following properties may be set in the details list."
1011 "@item beamed-lengths \n"
1012 "list of stem lengths given beam multiplicity. \n"
1013 "@item beamed-minimum-free-lengths \n"
1014 "list of normal minimum free stem lengths (chord to beams) given beam multiplicity.\n"
1015 "@item beamed-extreme-minimum-free-lengths\n"
1016 "list of extreme minimum free stem lengths (chord to beams) given beam multiplicity.\n"
1018 "Default stem lengths. The list gives a length for each flag-count.\n"
1019 "@item stem-shorten\n"
1020 "How much a stem in a forced direction "
1021 "should be shortened. The list gives an amount depending on the number "
1030 "default-direction "
1039 "neutral-direction "
1044 "stem-end-position "
1052 /****************************************************************/
1054 Stem_info::Stem_info ()
1056 ideal_y_ = shortest_y_ = 0;
1061 Stem_info::scale (Real x)