2 stem.cc -- implement Stem
4 source file of the GNU LilyPond music typesetter
6 (c) 1996--2006 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 Real stemlet_length = robust_scm2double (me->get_property ("stemlet-length"),
220 return !((head_count (me)
221 || stemlet_length > 0.0)
222 && scm_to_int (me->get_property ("duration-log")) >= 1);
227 Stem::is_normal_stem (Grob *me)
229 return head_count (me) && scm_to_int (me->get_property ("duration-log")) >= 1;
233 MAKE_SCHEME_CALLBACK (Stem, pure_height, 3)
235 Stem::pure_height (SCM smob, SCM start, SCM end)
240 Grob *me = unsmob_grob (smob);
243 if (!is_normal_stem (me))
244 return ly_interval2scm (iv);
246 Real ss = Staff_symbol_referencer::staff_space (me);
247 Real len = scm_to_double (calc_length (smob)) * ss / 2;
248 Direction dir = get_grob_direction (me);
250 Interval hp = head_positions (me);
252 iv = Interval (0, len);
254 iv = Interval (-len, 0);
257 iv.translate (hp[dir] * ss / 2);
259 return ly_interval2scm (iv);
262 MAKE_SCHEME_CALLBACK (Stem, calc_stem_end_position, 1)
264 Stem::calc_stem_end_position (SCM smob)
266 Grob *me = unsmob_grob (smob);
268 if (!head_count (me))
269 return scm_from_double (0.0);
271 if (Grob *beam = get_beam (me))
273 (void) beam->get_property ("quantized-positions");
274 return me->get_property ("stem-end-position");
277 Real ss = Staff_symbol_referencer::staff_space (me);
278 int durlog = duration_log (me);
281 /* WARNING: IN HALF SPACES */
282 Real length = robust_scm2double (me->get_property ("length"), 7);
284 Direction dir = get_grob_direction (me);
285 Interval hp = head_positions (me);
286 Real stem_end = dir ? hp[dir] + dir * length : 0;
288 /* TODO: change name to extend-stems to staff/center/'() */
289 bool no_extend_b = to_boolean (me->get_property ("no-stem-extend"));
290 if (!no_extend_b && dir * stem_end < 0)
294 /* Make a little room if we have a upflag and there is a dot.
295 previous approach was to lengthen the stem. This is not
296 good typesetting practice. */
297 if (!get_beam (me) && dir == UP
300 Grob *closest_to_flag = extremal_heads (me)[dir];
301 Grob *dots = closest_to_flag
302 ? Rhythmic_head::get_dots (closest_to_flag) : 0;
306 Real dp = Staff_symbol_referencer::get_position (dots);
307 Interval flag_yext = flag (me).extent (Y_AXIS) * (2 / ss) + stem_end;
309 /* Very gory: add myself to the X-support of the parent,
310 which should be a dot-column. */
312 if (flag_yext.distance (dp) < 0.5)
314 Grob *par = dots->get_parent (X_AXIS);
316 if (Dot_column::has_interface (par))
318 Side_position_interface::add_support (par, me);
320 /* TODO: apply some better logic here. The flag is
321 curved inwards, so this will typically be too
328 return scm_from_double (stem_end);
331 /* Length is in half-spaces (or: positions) here. */
332 MAKE_SCHEME_CALLBACK (Stem, calc_length, 1)
334 Stem::calc_length (SCM smob)
336 Grob *me = unsmob_grob (smob);
338 SCM details = me->get_property ("details");
339 int durlog = duration_log (me);
341 Real ss = Staff_symbol_referencer::staff_space (me);
343 SCM s = scm_cdr (scm_assq (ly_symbol2scm ("lengths"), details));
345 length = 2 * scm_to_double (robust_list_ref (durlog - 2, s));
347 Direction dir = get_grob_direction (me);
349 /* Stems in unnatural (forced) direction should be shortened,
350 according to [Roush & Gourlay] */
351 Interval hp = head_positions (me);
352 if (dir && dir * hp[dir] >= 0)
354 SCM sshorten = scm_cdr (scm_assq (ly_symbol2scm ("stem-shorten"), details));
355 SCM scm_shorten = scm_is_pair (sshorten)
356 ? robust_list_ref (max (duration_log (me) - 2, 0), sshorten) : SCM_EOL;
357 Real shorten = 2* robust_scm2double (scm_shorten, 0);
359 /* On boundary: shorten only half */
360 if (abs (head_positions (me)[dir]) <= 1)
366 length *= robust_scm2double (me->get_property ("length-fraction"), 1.0);
369 Grob *t_flag = unsmob_grob (me->get_object ("tremolo-flag"));
370 if (t_flag && !unsmob_grob (me->get_object ("beam")))
372 /* Crude hack: add extra space if tremolo flag is there.
374 We can't do this for the beam, since we get into a loop
375 (Stem_tremolo::raw_stencil () looks at the beam.) --hwn */
378 + 2 * Stem_tremolo::vertical_length (t_flag) / ss;
380 /* We don't want to add the whole extent of the flag because the trem
381 and the flag can overlap partly. beam_translation gives a good
385 Real beam_trans = Stem_tremolo::get_beam_translation (t_flag);
386 /* the obvious choice is (durlog - 2) here, but we need a bit more space. */
387 minlen += 2 * (durlog - 1.5) * beam_trans;
389 /* up-stems need even a little more space to avoid collisions. This
390 needs to be in sync with the tremolo positioning code in
391 Stem_tremolo::print */
393 minlen += beam_trans;
395 length = max (length, minlen + 1.0);
398 return scm_from_double (length);
400 /* The log of the duration (Number of hooks on the flag minus two) */
402 Stem::duration_log (Grob *me)
404 SCM s = me->get_property ("duration-log");
405 return (scm_is_number (s)) ? scm_to_int (s) : 2;
408 MAKE_SCHEME_CALLBACK(Stem, calc_positioning_done, 1);
410 Stem::calc_positioning_done (SCM smob)
412 Grob *me = unsmob_grob (smob);
413 if (!head_count (me))
416 extract_grob_set (me, "note-heads", ro_heads);
417 vector<Grob*> heads (ro_heads);
418 vector_sort (heads, position_less);
419 Direction dir = get_grob_direction (me);
424 Real thick = thickness (me);
426 Grob *hed = support_head (me);
429 programming_error ("Stem dir must be up or down.");
431 set_grob_direction (me, dir);
434 bool is_harmonic_centered = false;
435 for (vsize i = 0; i < heads.size (); i++)
436 is_harmonic_centered = is_harmonic_centered
437 || heads[i]->get_property ("style") == ly_symbol2scm ("harmonic");
438 is_harmonic_centered = is_harmonic_centered && is_invisible (me);
440 Real w = hed->extent (hed, X_AXIS)[dir];
441 for (vsize i = 0; i < heads.size (); i++)
443 Real amount = w - heads[i]->extent (heads[i], X_AXIS)[dir];
445 if (is_harmonic_centered)
447 hed->extent (hed, X_AXIS).linear_combination (CENTER)
448 - heads[i]->extent (heads[i], X_AXIS).linear_combination (CENTER);
450 heads[i]->translate_axis (amount, X_AXIS);
453 Real lastpos = Real (Staff_symbol_referencer::get_position (heads[0]));
454 for (vsize i = 1; i < heads.size (); i++)
456 Real p = Staff_symbol_referencer::get_position (heads[i]);
457 Real dy = fabs (lastpos- p);
460 dy should always be 0.5, 0.0, 1.0, but provide safety margin
467 Real ell = heads[i]->extent (heads[i], X_AXIS).length ();
469 Direction d = get_grob_direction (me);
471 Reversed head should be shifted ell-thickness, but this
472 looks too crowded, so we only shift ell-0.5*thickness.
474 This leads to assymetry: Normal heads overlap the
475 stem 100% whereas reversed heads only overlaps the
479 Real reverse_overlap = 0.5;
480 heads[i]->translate_axis ((ell - thick * reverse_overlap) * d,
483 if (is_invisible (me))
484 heads[i]->translate_axis (-thick * (2 - reverse_overlap) * d,
489 For some cases we should kern some more: when the
490 distance between the next or prev note is too large, we'd
491 get large white gaps, eg.
512 MAKE_SCHEME_CALLBACK(Stem, calc_direction, 1);
514 Stem::calc_direction (SCM smob)
516 Grob *me = unsmob_grob (smob);
517 Direction dir = CENTER;
518 if (Grob *beam = unsmob_grob (me->get_object ("beam")))
520 SCM ignore_me = beam->get_property ("direction");
522 dir = get_grob_direction (me);
526 SCM dd = me->get_property ("default-direction");
529 return me->get_property ("neutral-direction");
532 return scm_from_int (dir);
535 MAKE_SCHEME_CALLBACK(Stem, calc_default_direction, 1);
537 Stem::calc_default_direction (SCM smob)
539 Grob *me = unsmob_grob (smob);
541 Direction dir = CENTER;
542 int staff_center = 0;
543 Interval hp = head_positions (me);
546 int udistance = (int) (UP * hp[UP] - staff_center);
547 int ddistance = (int) (DOWN * hp[DOWN] - staff_center);
549 dir = Direction (sign (ddistance - udistance));
552 return scm_from_int (dir);
556 MAKE_SCHEME_CALLBACK (Stem, height, 1);
558 Stem::height (SCM smob)
560 Grob *me = unsmob_grob (smob);
562 Direction dir = get_grob_direction (me);
566 UGH. Should be automatic
568 Grob *beam = get_beam (me);
571 /* trigger set-stem-lengths. */
572 beam->get_property ("quantized-positions");
576 Can't get_stencil(), since that would cache stencils too early.
577 This causes problems with beams.
579 Stencil *stencil = unsmob_stencil (print (smob));
580 Interval iv = stencil ? stencil->extent (Y_AXIS) : Interval();
585 programming_error ("no stem direction");
588 iv[dir] += dir * Beam::get_thickness (beam) * 0.5;
591 return ly_interval2scm (iv);
595 Stem::stem_end_position (Grob *me)
597 return robust_scm2double (me->get_property ("stem-end-position"), 0);
601 Stem::flag (Grob *me)
603 int log = duration_log (me);
605 || unsmob_grob (me->get_object ("beam")))
609 TODO: maybe property stroke-style should take different values,
610 e.g. "" (i.e. no stroke), "single" and "double" (currently, it's
614 SCM flag_style_scm = me->get_property ("flag-style");
615 if (scm_is_symbol (flag_style_scm))
616 flag_style = ly_symbol2string (flag_style_scm);
618 if (flag_style == "no-flag")
623 string staffline_offs;
624 if (flag_style == "mensural")
625 /* Mensural notation: For notes on staff lines, use different
626 flags than for notes between staff lines. The idea is that
627 flags are always vertically aligned with the staff lines,
628 regardless if the note head is on a staff line or between two
629 staff lines. In other words, the inner end of a flag always
630 touches a staff line.
635 int p = (int) (rint (stem_end_position (me)));
637 = Staff_symbol_referencer::on_line (me, p) ? "0" : "1";
640 staffline_offs = "2";
645 char dir = (get_grob_direction (me) == UP) ? 'u' : 'd';
646 string font_char = flag_style
647 + to_string (dir) + staffline_offs + to_string (log);
648 Font_metric *fm = Font_interface::get_default_font (me);
649 Stencil flag = fm->find_by_name ("flags." + font_char);
650 if (flag.is_empty ())
651 me->warning (_f ("flag `%s' not found", font_char));
653 SCM stroke_style_scm = me->get_property ("stroke-style");
654 if (scm_is_string (stroke_style_scm))
656 string stroke_style = ly_scm2string (stroke_style_scm);
657 if (!stroke_style.empty ())
659 string font_char = to_string (dir) + stroke_style;
660 Stencil stroke = fm->find_by_name ("flags." + font_char);
661 if (stroke.is_empty ())
662 me->warning (_f ("flag stroke `%s' not found", font_char));
664 flag.add_stencil (stroke);
671 MAKE_SCHEME_CALLBACK (Stem, width, 1);
675 Grob *me = unsmob_grob (e);
679 if (is_invisible (me))
681 else if (unsmob_grob (me->get_object ("beam"))
682 || abs (duration_log (me)) <= 2)
684 r = Interval (-1, 1);
685 r *= thickness (me) / 2;
689 r = Interval (-1, 1) * thickness (me) * 0.5;
690 r.unite (flag (me).extent (X_AXIS));
692 return ly_interval2scm (r);
696 Stem::thickness (Grob *me)
698 return scm_to_double (me->get_property ("thickness"))
699 * Staff_symbol_referencer::line_thickness (me);
702 MAKE_SCHEME_CALLBACK (Stem, print, 1);
704 Stem::print (SCM smob)
706 Grob *me = unsmob_grob (smob);
707 Grob *beam = get_beam (me);
710 Direction d = get_grob_direction (me);
712 Real stemlet_length = robust_scm2double (me->get_property ("stemlet-length"),
714 bool stemlet = stemlet_length > 0.0;
716 /* TODO: make the stem start a direction ?
717 This is required to avoid stems passing in tablature chords. */
719 = to_boolean (me->get_property ("avoid-note-head"))
726 if (!lh && stemlet && !beam)
729 if (is_invisible (me))
732 Real y2 = robust_scm2double (me->get_property ("stem-end-position"), 0.0);
734 Real half_space = Staff_symbol_referencer::staff_space (me) * 0.5;
737 y2 = Staff_symbol_referencer::get_position (lh);
740 Real beam_translation = Beam::get_beam_translation (beam);
741 Real beam_thickness = Beam::get_thickness (beam);
742 int beam_count = beam_multiplicity (me).length () + 1;
745 * (0.5 * beam_thickness
746 + beam_translation * max (0, (beam_count - 1))
747 + stemlet_length) / half_space;
750 Interval stem_y (min (y1, y2), max (y2, y1));
752 if (Grob *head = support_head (me))
755 must not take ledgers into account.
757 Interval head_height = head->extent (head, Y_AXIS);
758 Real y_attach = Note_head::stem_attachment_coordinate (head, Y_AXIS);
760 y_attach = head_height.linear_combination (y_attach);
761 stem_y[Direction (-d)] += d * y_attach / half_space;
765 Real stem_width = thickness (me);
767 = me->layout ()->get_dimension (ly_symbol2scm ("blot-diameter"));
769 Box b = Box (Interval (-stem_width / 2, stem_width / 2),
770 Interval (stem_y[DOWN] * half_space, stem_y[UP] * half_space));
772 Stencil ss = Lookup::round_filled_box (b, blot);
773 mol.add_stencil (ss);
775 mol.add_stencil (get_translated_flag (me));
777 return mol.smobbed_copy ();
781 Stem::get_translated_flag (Grob *me)
783 Stencil fl = flag (me);
786 Direction d = get_grob_direction (me);
788 = me->layout ()->get_dimension (ly_symbol2scm ("blot-diameter"));
789 Real stem_width = thickness (me);
790 Real half_space = Staff_symbol_referencer::staff_space (me) * 0.5;
791 Real y2 = robust_scm2double (me->get_property ("stem-end-position"), 0.0);
792 fl.translate_axis (y2 * half_space - d * blot / 2, Y_AXIS);
793 fl.translate_axis (stem_width / 2, X_AXIS);
800 move the stem to right of the notehead if it is up.
802 MAKE_SCHEME_CALLBACK (Stem, offset_callback, 1);
804 Stem::offset_callback (SCM smob)
806 Grob *me = unsmob_grob (smob);
809 if (Grob *f = first_head (me))
811 Interval head_wid = f->extent (f, X_AXIS);
814 if (is_invisible (me))
817 attach = Note_head::stem_attachment_coordinate (f, X_AXIS);
819 Direction d = get_grob_direction (me);
820 Real real_attach = head_wid.linear_combination (d * attach);
823 /* If not centered: correct for stem thickness. */
826 Real rule_thick = thickness (me);
827 r += -d * rule_thick * 0.5;
832 extract_grob_set (me, "rests", rests);
835 Grob *rest = rests.back ();
836 r = rest->extent (rest, X_AXIS).center ();
839 return scm_from_double (r);
843 Stem::get_beam (Grob *me)
845 SCM b = me->get_object ("beam");
846 return dynamic_cast<Spanner *> (unsmob_grob (b));
850 Stem::get_stem_info (Grob *me)
853 si.dir_ = get_grob_direction (me);
855 SCM scm_info = me->get_property ("stem-info");
856 si.ideal_y_ = scm_to_double (scm_car (scm_info));
857 si.shortest_y_ = scm_to_double (scm_cadr (scm_info));
861 MAKE_SCHEME_CALLBACK(Stem, calc_stem_info, 1);
863 Stem::calc_stem_info (SCM smob)
865 Grob *me = unsmob_grob (smob);
866 Direction my_dir = get_grob_direction (me);
870 programming_error ("no stem dir set");
874 Real staff_space = Staff_symbol_referencer::staff_space (me);
875 Grob *beam = get_beam (me);
879 (void) beam->get_property ("beaming");
882 Real beam_translation = Beam::get_beam_translation (beam);
883 Real beam_thickness = Beam::get_thickness (beam);
884 int beam_count = Beam::get_direction_beam_count (beam, my_dir);
886 = robust_scm2double (me->get_property ("length-fraction"), 1.0);
888 /* Simple standard stem length */
889 SCM details = me->get_property ("details");
890 SCM lengths = scm_cdr (scm_assq (ly_symbol2scm ("beamed-lengths"), details));
893 = scm_to_double (robust_list_ref (beam_count - 1, lengths))
897 /* stem only extends to center of beam
899 - 0.5 * beam_thickness;
901 /* Condition: sane minimum free stem length (chord to beams) */
902 lengths = scm_cdr (scm_assq (ly_symbol2scm ("beamed-minimum-free-lengths"), details));
904 Real ideal_minimum_free
905 = scm_to_double (robust_list_ref (beam_count - 1, lengths))
909 Real height_of_my_trem = 0.0;
910 Grob *trem = unsmob_grob (me->get_object ("tremolo-flag"));
912 height_of_my_trem = trem->extent (trem, Y_AXIS).length ()
913 /* hack a bit of space around the trem. */
917 It seems that also for ideal minimum length, we must use
918 the maximum beam count (for this direction):
920 \score{ \notes\relative c''{ [a8 a32] }}
922 must be horizontal. */
923 Real height_of_my_beams = beam_thickness
924 + (beam_count - 1) * beam_translation;
926 Real ideal_minimum_length = ideal_minimum_free
929 /* stem only extends to center of beam */
930 - 0.5 * beam_thickness;
932 ideal_length = max (ideal_length, ideal_minimum_length);
934 /* Convert to Y position, calculate for dir == UP */
936 = /* staff positions */
937 head_positions (me)[my_dir] * 0.5
938 * my_dir * staff_space;
939 Real ideal_y = note_start + ideal_length;
941 /* Conditions for Y position */
943 /* Lowest beam of (UP) beam must never be lower than second staffline
947 Although this (additional) rule is probably correct,
948 I expect that highest beam (UP) should also never be lower
949 than middle staffline, just as normal stems.
953 Obviously not for grace beams.
955 Also, not for knees. Seems to be a good thing. */
956 bool no_extend_b = to_boolean (me->get_property ("no-stem-extend"));
957 bool is_knee = to_boolean (beam->get_property ("knee"));
958 if (!no_extend_b && !is_knee)
960 /* Highest beam of (UP) beam must never be lower than middle
962 ideal_y = max (ideal_y, 0.0);
963 /* Lowest beam of (UP) beam must never be lower than second staffline */
964 ideal_y = max (ideal_y, (-staff_space
965 - beam_thickness + height_of_my_beams));
968 ideal_y -= robust_scm2double (beam->get_property ("shorten"), 0);
970 SCM bemfl = scm_cdr (scm_assq (ly_symbol2scm ("beamed-extreme-minimum-free-lengths"),
974 = scm_to_double (robust_list_ref (beam_count - 1, bemfl))
978 Real minimum_length = max (minimum_free, height_of_my_trem)
980 /* stem only extends to center of beam */
981 - 0.5 * beam_thickness;
984 Real minimum_y = note_start + minimum_length;
985 Real shortest_y = minimum_y * my_dir;
987 return scm_list_2 (scm_from_double (ideal_y),
988 scm_from_double (shortest_y));
992 Stem::beam_multiplicity (Grob *stem)
994 SCM beaming = stem->get_property ("beaming");
995 Slice le = int_list_to_slice (scm_car (beaming));
996 Slice ri = int_list_to_slice (scm_cdr (beaming));
1001 /* FIXME: Too many properties */
1002 ADD_INTERFACE (Stem,
1003 "The stem represent the graphical stem. "
1004 "In addition, it internally connects note heads, beams and"
1006 "Rests and whole notes have invisible stems."
1008 "\n\nThe following properties may be set in the details list."
1010 "@item beamed-lengths \n"
1011 "list of stem lengths given beam multiplicity. \n"
1012 "@item beamed-minimum-free-lengths \n"
1013 "list of normal minimum free stem lengths (chord to beams) given beam multiplicity.\n"
1014 "@item beamed-extreme-minimum-free-lengths\n"
1015 "list of extreme minimum free stem lengths (chord to beams) given beam multiplicity.\n"
1017 "Default stem lengths. The list gives a length for each flag-count.\n"
1018 "@item stem-shorten\n"
1019 "How much a stem in a forced direction "
1020 "should be shortened. The list gives an amount depending on the number "
1029 "default-direction "
1038 "neutral-direction "
1043 "stem-end-position "
1051 /****************************************************************/
1053 Stem_info::Stem_info ()
1055 ideal_y_ = shortest_y_ = 0;
1060 Stem_info::scale (Real x)