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);
560 if (!is_normal_stem (me))
561 return ly_interval2scm (Interval ());
563 Direction dir = get_grob_direction (me);
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);
805 extract_grob_set (me, "rests", rests);
808 Grob *rest = rests.back ();
809 Real r = rest->extent (rest, X_AXIS).center ();
810 return scm_from_double (r);
814 if (Grob *f = first_head (me))
816 Interval head_wid = f->extent (f, X_AXIS);
819 if (is_invisible (me))
822 attach = Note_head::stem_attachment_coordinate (f, X_AXIS);
824 Direction d = get_grob_direction (me);
825 Real real_attach = head_wid.linear_combination (d * attach);
826 Real r = real_attach;
828 /* If not centered: correct for stem thickness. */
831 Real rule_thick = thickness (me);
832 r += -d * rule_thick * 0.5;
834 return scm_from_double (r);
837 programming_error ("Weird stem.");
838 return scm_from_double (0.0);
842 Stem::get_beam (Grob *me)
844 SCM b = me->get_object ("beam");
845 return dynamic_cast<Spanner *> (unsmob_grob (b));
849 Stem::get_stem_info (Grob *me)
852 si.dir_ = get_grob_direction (me);
854 SCM scm_info = me->get_property ("stem-info");
855 si.ideal_y_ = scm_to_double (scm_car (scm_info));
856 si.shortest_y_ = scm_to_double (scm_cadr (scm_info));
860 MAKE_SCHEME_CALLBACK(Stem, calc_stem_info, 1);
862 Stem::calc_stem_info (SCM smob)
864 Grob *me = unsmob_grob (smob);
865 Direction my_dir = get_grob_direction (me);
869 programming_error ("no stem dir set");
873 Real staff_space = Staff_symbol_referencer::staff_space (me);
874 Grob *beam = get_beam (me);
878 (void) beam->get_property ("beaming");
881 Real beam_translation = Beam::get_beam_translation (beam);
882 Real beam_thickness = Beam::get_thickness (beam);
883 int beam_count = Beam::get_direction_beam_count (beam, my_dir);
885 = robust_scm2double (me->get_property ("length-fraction"), 1.0);
887 /* Simple standard stem length */
888 SCM details = me->get_property ("details");
889 SCM lengths = scm_cdr (scm_assq (ly_symbol2scm ("beamed-lengths"), details));
892 = scm_to_double (robust_list_ref (beam_count - 1, lengths))
896 /* stem only extends to center of beam
898 - 0.5 * beam_thickness;
900 /* Condition: sane minimum free stem length (chord to beams) */
901 lengths = scm_cdr (scm_assq (ly_symbol2scm ("beamed-minimum-free-lengths"), details));
903 Real ideal_minimum_free
904 = scm_to_double (robust_list_ref (beam_count - 1, lengths))
908 Real height_of_my_trem = 0.0;
909 Grob *trem = unsmob_grob (me->get_object ("tremolo-flag"));
913 = Stem_tremolo::vertical_length (trem)
914 /* hack a bit of space around the trem. */
920 It seems that also for ideal minimum length, we must use
921 the maximum beam count (for this direction):
923 \score{ \notes\relative c''{ [a8 a32] }}
925 must be horizontal. */
926 Real height_of_my_beams = beam_thickness
927 + (beam_count - 1) * beam_translation;
929 Real ideal_minimum_length = ideal_minimum_free
932 /* stem only extends to center of beam */
933 - 0.5 * beam_thickness;
935 ideal_length = max (ideal_length, ideal_minimum_length);
937 /* Convert to Y position, calculate for dir == UP */
939 = /* staff positions */
940 head_positions (me)[my_dir] * 0.5
941 * my_dir * staff_space;
942 Real ideal_y = note_start + ideal_length;
944 /* Conditions for Y position */
946 /* Lowest beam of (UP) beam must never be lower than second staffline
950 Although this (additional) rule is probably correct,
951 I expect that highest beam (UP) should also never be lower
952 than middle staffline, just as normal stems.
956 Obviously not for grace beams.
958 Also, not for knees. Seems to be a good thing. */
959 bool no_extend_b = to_boolean (me->get_property ("no-stem-extend"));
960 bool is_knee = to_boolean (beam->get_property ("knee"));
961 if (!no_extend_b && !is_knee)
963 /* Highest beam of (UP) beam must never be lower than middle
965 ideal_y = max (ideal_y, 0.0);
966 /* Lowest beam of (UP) beam must never be lower than second staffline */
967 ideal_y = max (ideal_y, (-staff_space
968 - beam_thickness + height_of_my_beams));
971 ideal_y -= robust_scm2double (beam->get_property ("shorten"), 0);
973 SCM bemfl = scm_cdr (scm_assq (ly_symbol2scm ("beamed-extreme-minimum-free-lengths"),
977 = scm_to_double (robust_list_ref (beam_count - 1, bemfl))
981 Real minimum_length = max (minimum_free, height_of_my_trem)
983 /* stem only extends to center of beam */
984 - 0.5 * beam_thickness;
987 Real minimum_y = note_start + minimum_length;
988 Real shortest_y = minimum_y * my_dir;
990 return scm_list_2 (scm_from_double (ideal_y),
991 scm_from_double (shortest_y));
995 Stem::beam_multiplicity (Grob *stem)
997 SCM beaming = stem->get_property ("beaming");
998 Slice le = int_list_to_slice (scm_car (beaming));
999 Slice ri = int_list_to_slice (scm_cdr (beaming));
1004 /* FIXME: Too many properties */
1005 ADD_INTERFACE (Stem,
1006 "The stem represent the graphical stem. "
1007 "In addition, it internally connects note heads, beams and"
1009 "Rests and whole notes have invisible stems."
1011 "\n\nThe following properties may be set in the details list."
1013 "@item beamed-lengths \n"
1014 "list of stem lengths given beam multiplicity. \n"
1015 "@item beamed-minimum-free-lengths \n"
1016 "list of normal minimum free stem lengths (chord to beams) given beam multiplicity.\n"
1017 "@item beamed-extreme-minimum-free-lengths\n"
1018 "list of extreme minimum free stem lengths (chord to beams) given beam multiplicity.\n"
1020 "Default stem lengths. The list gives a length for each flag-count.\n"
1021 "@item stem-shorten\n"
1022 "How much a stem in a forced direction "
1023 "should be shortened. The list gives an amount depending on the number "
1032 "default-direction "
1041 "neutral-direction "
1046 "stem-end-position "
1054 /****************************************************************/
1056 Stem_info::Stem_info ()
1058 ideal_y_ = shortest_y_ = 0;
1063 Stem_info::scale (Real x)