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.
18 #include <cmath> // rint
22 #include "directional-element-interface.hh"
23 #include "dot-column.hh"
24 #include "font-interface.hh"
25 #include "international.hh"
28 #include "note-head.hh"
29 #include "output-def.hh"
30 #include "paper-column.hh"
31 #include "pointer-group-interface.hh"
33 #include "rhythmic-head.hh"
34 #include "side-position-interface.hh"
35 #include "staff-symbol-referencer.hh"
36 #include "stem-tremolo.hh"
40 Stem::set_beaming (Grob *me, int beam_count, Direction d)
42 SCM pair = me->get_property ("beaming");
44 if (!scm_is_pair (pair))
46 pair = scm_cons (SCM_EOL, SCM_EOL);
47 me->set_property ("beaming", pair);
50 SCM lst = index_get_cell (pair, d);
52 for (int i = 0; i < beam_count; i++)
53 lst = scm_cons (scm_from_int (i), lst);
57 index_set_cell (pair, d, lst);
61 Stem::get_beaming (Grob *me, Direction d)
63 SCM pair = me->get_property ("beaming");
64 if (!scm_is_pair (pair))
67 SCM lst = index_get_cell (pair, d);
69 int len = scm_ilength (lst);
74 Stem::head_positions (Grob *me)
78 Drul_array<Grob *> e (extremal_heads (me));
79 return Interval (Staff_symbol_referencer::get_position (e[DOWN]),
80 Staff_symbol_referencer::get_position (e[UP]));
86 Stem::chord_start_y (Grob *me)
88 Interval hp = head_positions (me);
90 return hp[get_grob_direction (me)] * Staff_symbol_referencer::staff_space (me)
98 Stem::set_stemend (Grob *me, Real se)
101 Direction d = get_grob_direction (me);
103 if (d && d * head_positions (me)[get_grob_direction (me)] >= se * d)
104 me->warning (_ ("weird stem size, check for narrow beams"));
106 me->set_property ("stem-end-position", scm_from_double (se));
109 /* Note head that determines hshift for upstems
110 WARNING: triggers direction */
112 Stem::support_head (Grob *me)
114 extract_grob_set (me, "note-heads", heads);
115 if (heads.size () == 1)
118 return first_head (me);
122 Stem::head_count (Grob *me)
124 return Pointer_group_interface::count (me, ly_symbol2scm ("note-heads"));
127 /* The note head which forms one end of the stem.
128 WARNING: triggers direction */
130 Stem::first_head (Grob *me)
132 Direction d = get_grob_direction (me);
134 return extremal_heads (me)[-d];
138 /* The note head opposite to the first head. */
140 Stem::last_head (Grob *me)
142 Direction d = get_grob_direction (me);
144 return extremal_heads (me)[d];
149 START is part where stem reaches `last' head.
151 This function returns a drul with (bottom-head, top-head).
154 Stem::extremal_heads (Grob *me)
156 const int inf = 1000000;
157 Drul_array<int> extpos;
161 Drul_array<Grob *> exthead (0, 0);
162 extract_grob_set (me, "note-heads", heads);
164 for (vsize i = heads.size (); i--;)
167 int p = Staff_symbol_referencer::get_rounded_position (n);
172 if (d * p > d * extpos[d])
178 while (flip (&d) != DOWN);
184 integer_compare (int const &a, int const &b)
189 /* The positions, in ascending order. */
191 Stem::note_head_positions (Grob *me)
194 extract_grob_set (me, "note-heads", heads);
196 for (vsize i = heads.size (); i--;)
199 int p = Staff_symbol_referencer::get_rounded_position (n);
204 vector_sort (ps, integer_compare);
209 Stem::add_head (Grob *me, Grob *n)
211 n->set_object ("stem", me->self_scm ());
213 if (Note_head::has_interface (n))
214 Pointer_group_interface::add_grob (me, ly_symbol2scm ("note-heads"), n);
215 else if (Rest::has_interface (n))
216 Pointer_group_interface::add_grob (me, ly_symbol2scm ("rests"), n);
220 Stem::is_invisible (Grob *me)
222 Real stemlet_length = robust_scm2double (me->get_property ("stemlet-length"),
225 return !((head_count (me)
226 || stemlet_length > 0.0)
227 && 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)
238 Grob *me = unsmob_grob (smob);
239 Real ss = Staff_symbol_referencer::staff_space (me);
240 Real len = scm_to_double (calc_length (smob)) * ss / 2;
241 Direction dir = get_grob_direction (me);
244 Interval hp = head_positions (me);
246 iv = Interval (0, len);
248 iv = Interval (-len, 0);
251 iv.translate (hp[dir] * ss / 2);
253 return ly_interval2scm (iv);
256 MAKE_SCHEME_CALLBACK (Stem, calc_stem_end_position, 1)
258 Stem::calc_stem_end_position (SCM smob)
260 Grob *me = unsmob_grob (smob);
262 if (!head_count (me))
263 return scm_from_double (0.0);
265 if (Grob *beam = get_beam (me))
267 (void) beam->get_property ("quantized-positions");
268 return me->get_property ("stem-end-position");
271 Real ss = Staff_symbol_referencer::staff_space (me);
272 int durlog = duration_log (me);
275 /* WARNING: IN HALF SPACES */
276 Real length = robust_scm2double (me->get_property ("length"), 7);
278 Direction dir = get_grob_direction (me);
279 Interval hp = head_positions (me);
280 Real stem_end = dir ? hp[dir] + dir * length : 0;
282 /* TODO: change name to extend-stems to staff/center/'() */
283 bool no_extend_b = to_boolean (me->get_property ("no-stem-extend"));
284 if (!no_extend_b && dir * stem_end < 0)
288 /* Make a little room if we have a upflag and there is a dot.
289 previous approach was to lengthen the stem. This is not
290 good typesetting practice. */
291 if (!get_beam (me) && dir == UP
294 Grob *closest_to_flag = extremal_heads (me)[dir];
295 Grob *dots = closest_to_flag
296 ? Rhythmic_head::get_dots (closest_to_flag) : 0;
300 Real dp = Staff_symbol_referencer::get_position (dots);
301 Interval flag_yext = flag (me).extent (Y_AXIS) * (2 / ss) + stem_end;
303 /* Very gory: add myself to the X-support of the parent,
304 which should be a dot-column. */
306 if (flag_yext.distance (dp) < 0.5)
308 Grob *par = dots->get_parent (X_AXIS);
310 if (Dot_column::has_interface (par))
312 Side_position_interface::add_support (par, me);
314 /* TODO: apply some better logic here. The flag is
315 curved inwards, so this will typically be too
322 return scm_from_double (stem_end);
326 MAKE_SCHEME_CALLBACK (Stem, calc_length, 1)
328 Stem::calc_length (SCM smob)
330 Grob *me = unsmob_grob (smob);
332 SCM details = me->get_property ("details");
333 int durlog = duration_log (me);
335 Real ss = Staff_symbol_referencer::staff_space (me);
337 SCM s = scm_cdr (scm_assq (ly_symbol2scm ("lengths"), details));
339 length = 2 * scm_to_double (robust_list_ref (durlog - 2, s));
341 Direction dir = get_grob_direction (me);
343 /* Stems in unnatural (forced) direction should be shortened,
344 according to [Roush & Gourlay] */
345 Interval hp = head_positions (me);
346 if (dir && dir * hp[dir] >= 0)
348 SCM sshorten = scm_cdr (scm_assq (ly_symbol2scm ("stem-shorten"), details));
349 SCM scm_shorten = scm_is_pair (sshorten)
350 ? robust_list_ref (max (duration_log (me) - 2, 0), sshorten) : SCM_EOL;
351 Real shorten = 2* robust_scm2double (scm_shorten, 0);
353 /* On boundary: shorten only half */
354 if (abs (head_positions (me)[dir]) <= 1)
360 length *= robust_scm2double (me->get_property ("length-fraction"), 1.0);
363 Grob *t_flag = unsmob_grob (me->get_object ("tremolo-flag"));
364 if (t_flag && !unsmob_grob (me->get_object ("beam")))
366 /* Crude hack: add extra space if tremolo flag is there.
368 We can't do this for the beam, since we get into a loop
369 (Stem_tremolo::raw_stencil () looks at the beam.) --hwn */
372 + 2 * t_flag->extent (t_flag, Y_AXIS).length ()
375 /* We don't want to add the whole extent of the flag because the trem
376 and the flag can overlap partly. beam_translation gives a good
380 Real beam_trans = Stem_tremolo::get_beam_translation (t_flag);
381 /* the obvious choice is (durlog - 2) here, but we need a bit more space. */
382 minlen += 2 * (durlog - 1.5) * beam_trans;
384 /* up-stems need even a little more space to avoid collisions. This
385 needs to be in sync with the tremolo positioning code in
386 Stem_tremolo::print */
388 minlen += beam_trans;
390 length = max (length, minlen + 1.0);
393 return scm_from_double (length);
395 /* The log of the duration (Number of hooks on the flag minus two) */
397 Stem::duration_log (Grob *me)
399 SCM s = me->get_property ("duration-log");
400 return (scm_is_number (s)) ? scm_to_int (s) : 2;
403 MAKE_SCHEME_CALLBACK(Stem, calc_positioning_done, 1);
405 Stem::calc_positioning_done (SCM smob)
407 Grob *me = unsmob_grob (smob);
408 if (!head_count (me))
411 extract_grob_set (me, "note-heads", ro_heads);
412 vector<Grob*> heads (ro_heads);
413 vector_sort (heads, compare_position);
414 Direction dir = get_grob_direction (me);
419 Real thick = thickness (me);
421 Grob *hed = support_head (me);
424 programming_error ("Stem dir must be up or down.");
426 set_grob_direction (me, dir);
429 Real w = hed->extent (hed, X_AXIS)[dir];
430 for (vsize i = 0; i < heads.size (); i++)
431 heads[i]->translate_axis (w - heads[i]->extent (heads[i], X_AXIS)[dir],
435 Real lastpos = Real (Staff_symbol_referencer::get_position (heads[0]));
436 for (vsize i = 1; i < heads.size (); i++)
438 Real p = Staff_symbol_referencer::get_position (heads[i]);
439 Real dy = fabs (lastpos- p);
442 dy should always be 0.5, 0.0, 1.0, but provide safety margin
449 Real ell = heads[i]->extent (heads[i], X_AXIS).length ();
451 Direction d = get_grob_direction (me);
453 Reversed head should be shifted ell-thickness, but this
454 looks too crowded, so we only shift ell-0.5*thickness.
456 This leads to assymetry: Normal heads overlap the
457 stem 100% whereas reversed heads only overlaps the
461 Real reverse_overlap = 0.5;
462 heads[i]->translate_axis ((ell - thick * reverse_overlap) * d,
465 if (is_invisible (me))
466 heads[i]->translate_axis (-thick * (2 - reverse_overlap) * d,
471 For some cases we should kern some more: when the
472 distance between the next or prev note is too large, we'd
473 get large white gaps, eg.
494 MAKE_SCHEME_CALLBACK(Stem, calc_direction, 1);
496 Stem::calc_direction (SCM smob)
498 Grob *me = unsmob_grob (smob);
499 Direction dir = CENTER;
500 if (Grob *beam = unsmob_grob (me->get_object ("beam")))
502 SCM ignore_me = beam->get_property ("direction");
504 dir = get_grob_direction (me);
508 SCM dd = me->get_property ("default-direction");
511 return me->get_property ("neutral-direction");
514 return scm_from_int (dir);
517 MAKE_SCHEME_CALLBACK(Stem, calc_default_direction, 1);
519 Stem::calc_default_direction (SCM smob)
521 Grob *me = unsmob_grob (smob);
523 Direction dir = CENTER;
524 int staff_center = 0;
525 Interval hp = head_positions (me);
528 int udistance = (int) (UP * hp[UP] - staff_center);
529 int ddistance = (int) (DOWN * hp[DOWN] - staff_center);
531 dir = Direction (sign (ddistance - udistance));
534 return scm_from_int (dir);
538 MAKE_SCHEME_CALLBACK (Stem, height, 1);
540 Stem::height (SCM smob)
542 Grob *me = unsmob_grob (smob);
544 Direction dir = get_grob_direction (me);
548 UGH. Should be automatic
550 Grob *beam = get_beam (me);
553 /* trigger set-stem-lengths. */
554 beam->get_property ("quantized-positions");
558 Can't get_stencil(), since that would cache stencils too early.
559 This causes problems with beams.
561 Stencil *stencil = unsmob_stencil (print (smob));
562 Interval iv = stencil ? stencil->extent (Y_AXIS) : Interval();
567 programming_error ("no stem direction");
570 iv[dir] += dir * Beam::get_thickness (beam) * 0.5;
573 return ly_interval2scm (iv);
577 Stem::stem_end_position (Grob *me)
579 return robust_scm2double (me->get_property ("stem-end-position"), 0);
583 Stem::flag (Grob *me)
585 int log = duration_log (me);
587 || unsmob_grob (me->get_object ("beam")))
591 TODO: maybe property stroke-style should take different values,
592 e.g. "" (i.e. no stroke), "single" and "double" (currently, it's
596 SCM flag_style_scm = me->get_property ("flag-style");
597 if (scm_is_symbol (flag_style_scm))
598 flag_style = ly_symbol2string (flag_style_scm);
600 if (flag_style == "no-flag")
605 string staffline_offs;
606 if (flag_style == "mensural")
607 /* Mensural notation: For notes on staff lines, use different
608 flags than for notes between staff lines. The idea is that
609 flags are always vertically aligned with the staff lines,
610 regardless if the note head is on a staff line or between two
611 staff lines. In other words, the inner end of a flag always
612 touches a staff line.
617 int p = (int) (rint (stem_end_position (me)));
619 = Staff_symbol_referencer::on_line (me, p) ? "0" : "1";
622 staffline_offs = "2";
627 char dir = (get_grob_direction (me) == UP) ? 'u' : 'd';
628 string font_char = flag_style
629 + to_string (dir) + staffline_offs + to_string (log);
630 Font_metric *fm = Font_interface::get_default_font (me);
631 Stencil flag = fm->find_by_name ("flags." + font_char);
632 if (flag.is_empty ())
633 me->warning (_f ("flag `%s' not found", font_char));
635 SCM stroke_style_scm = me->get_property ("stroke-style");
636 if (scm_is_string (stroke_style_scm))
638 string stroke_style = ly_scm2string (stroke_style_scm);
639 if (!stroke_style.empty ())
641 string font_char = to_string (dir) + stroke_style;
642 Stencil stroke = fm->find_by_name ("flags." + font_char);
643 if (stroke.is_empty ())
644 me->warning (_f ("flag stroke `%s' not found", font_char));
646 flag.add_stencil (stroke);
653 MAKE_SCHEME_CALLBACK (Stem, width, 1);
657 Grob *me = unsmob_grob (e);
661 if (is_invisible (me))
663 else if (unsmob_grob (me->get_object ("beam"))
664 || abs (duration_log (me)) <= 2)
666 r = Interval (-1, 1);
667 r *= thickness (me) / 2;
671 r = Interval (-1, 1) * thickness (me) * 0.5;
672 r.unite (flag (me).extent (X_AXIS));
674 return ly_interval2scm (r);
678 Stem::thickness (Grob *me)
680 return scm_to_double (me->get_property ("thickness"))
681 * Staff_symbol_referencer::line_thickness (me);
684 MAKE_SCHEME_CALLBACK (Stem, print, 1);
686 Stem::print (SCM smob)
688 Grob *me = unsmob_grob (smob);
689 Grob *beam = get_beam (me);
692 Direction d = get_grob_direction (me);
694 Real stemlet_length = robust_scm2double (me->get_property ("stemlet-length"),
696 bool stemlet = stemlet_length > 0.0;
698 /* TODO: make the stem start a direction ?
699 This is required to avoid stems passing in tablature chords. */
701 = to_boolean (me->get_property ("avoid-note-head"))
708 if (!lh && stemlet && !beam)
711 if (is_invisible (me))
714 Real y2 = robust_scm2double (me->get_property ("stem-end-position"), 0.0);
716 Real half_space = Staff_symbol_referencer::staff_space (me) * 0.5;
719 y2 = Staff_symbol_referencer::get_position (lh);
722 Real beam_translation = Beam::get_beam_translation (beam);
723 Real beam_thickness = Beam::get_thickness (beam);
724 int beam_count = beam_multiplicity (me).length () + 1;
727 * (0.5 * beam_thickness
728 + beam_translation * max (0, (beam_count - 1))
729 + stemlet_length) / half_space;
732 Interval stem_y (min (y1, y2), max (y2, y1));
734 if (Grob *head = support_head (me))
737 must not take ledgers into account.
739 Interval head_height = head->extent (head, Y_AXIS);
740 Real y_attach = Note_head::stem_attachment_coordinate (head, Y_AXIS);
742 y_attach = head_height.linear_combination (y_attach);
743 stem_y[Direction (-d)] += d * y_attach / half_space;
747 Real stem_width = thickness (me);
749 = me->layout ()->get_dimension (ly_symbol2scm ("blot-diameter"));
751 Box b = Box (Interval (-stem_width / 2, stem_width / 2),
752 Interval (stem_y[DOWN] * half_space, stem_y[UP] * half_space));
754 Stencil ss = Lookup::round_filled_box (b, blot);
755 mol.add_stencil (ss);
757 mol.add_stencil (get_translated_flag (me));
759 return mol.smobbed_copy ();
763 Stem::get_translated_flag (Grob *me)
765 Stencil fl = flag (me);
768 Direction d = get_grob_direction (me);
770 = me->layout ()->get_dimension (ly_symbol2scm ("blot-diameter"));
771 Real stem_width = thickness (me);
772 Real half_space = Staff_symbol_referencer::staff_space (me) * 0.5;
773 Real y2 = robust_scm2double (me->get_property ("stem-end-position"), 0.0);
774 fl.translate_axis (y2 * half_space - d * blot / 2, Y_AXIS);
775 fl.translate_axis (stem_width / 2, X_AXIS);
782 move the stem to right of the notehead if it is up.
784 MAKE_SCHEME_CALLBACK (Stem, offset_callback, 1);
786 Stem::offset_callback (SCM smob)
788 Grob *me = unsmob_grob (smob);
791 if (Grob *f = first_head (me))
793 Interval head_wid = f->extent (f, X_AXIS);
796 if (is_invisible (me))
799 attach = Note_head::stem_attachment_coordinate (f, X_AXIS);
801 Direction d = get_grob_direction (me);
802 Real real_attach = head_wid.linear_combination (d * attach);
805 /* If not centered: correct for stem thickness. */
808 Real rule_thick = thickness (me);
809 r += -d * rule_thick * 0.5;
814 extract_grob_set (me, "rests", rests);
817 Grob *rest = rests.back ();
818 r = rest->extent (rest, X_AXIS).center ();
821 return scm_from_double (r);
825 Stem::get_beam (Grob *me)
827 SCM b = me->get_object ("beam");
828 return dynamic_cast<Spanner *> (unsmob_grob (b));
832 Stem::get_stem_info (Grob *me)
835 si.dir_ = get_grob_direction (me);
837 SCM scm_info = me->get_property ("stem-info");
838 si.ideal_y_ = scm_to_double (scm_car (scm_info));
839 si.shortest_y_ = scm_to_double (scm_cadr (scm_info));
843 MAKE_SCHEME_CALLBACK(Stem, calc_stem_info, 1);
845 Stem::calc_stem_info (SCM smob)
847 Grob *me = unsmob_grob (smob);
848 Direction my_dir = get_grob_direction (me);
852 programming_error ("no stem dir set");
856 Real staff_space = Staff_symbol_referencer::staff_space (me);
857 Grob *beam = get_beam (me);
861 (void) beam->get_property ("beaming");
864 Real beam_translation = Beam::get_beam_translation (beam);
865 Real beam_thickness = Beam::get_thickness (beam);
866 int beam_count = Beam::get_direction_beam_count (beam, my_dir);
868 = robust_scm2double (me->get_property ("length-fraction"), 1.0);
870 /* Simple standard stem length */
871 SCM details = me->get_property ("details");
872 SCM lengths = scm_cdr (scm_assq (ly_symbol2scm ("beamed-lengths"), details));
875 = scm_to_double (robust_list_ref (beam_count - 1, lengths))
879 /* stem only extends to center of beam
881 - 0.5 * beam_thickness;
883 /* Condition: sane minimum free stem length (chord to beams) */
884 lengths = scm_cdr (scm_assq (ly_symbol2scm ("beamed-minimum-free-lengths"), details));
886 Real ideal_minimum_free
887 = scm_to_double (robust_list_ref (beam_count - 1, lengths))
891 Real height_of_my_trem = 0.0;
892 Grob *trem = unsmob_grob (me->get_object ("tremolo-flag"));
894 height_of_my_trem = trem->extent (trem, Y_AXIS).length ()
895 /* hack a bit of space around the trem. */
899 It seems that also for ideal minimum length, we must use
900 the maximum beam count (for this direction):
902 \score{ \notes\relative c''{ [a8 a32] }}
904 must be horizontal. */
905 Real height_of_my_beams = beam_thickness
906 + (beam_count - 1) * beam_translation;
908 Real ideal_minimum_length = ideal_minimum_free
911 /* stem only extends to center of beam */
912 - 0.5 * beam_thickness;
914 ideal_length = max (ideal_length, ideal_minimum_length);
916 /* Convert to Y position, calculate for dir == UP */
918 = /* staff positions */
919 head_positions (me)[my_dir] * 0.5
920 * my_dir * staff_space;
921 Real ideal_y = note_start + ideal_length;
923 /* Conditions for Y position */
925 /* Lowest beam of (UP) beam must never be lower than second staffline
929 Although this (additional) rule is probably correct,
930 I expect that highest beam (UP) should also never be lower
931 than middle staffline, just as normal stems.
935 Obviously not for grace beams.
937 Also, not for knees. Seems to be a good thing. */
938 bool no_extend_b = to_boolean (me->get_property ("no-stem-extend"));
939 bool is_knee = to_boolean (beam->get_property ("knee"));
940 if (!no_extend_b && !is_knee)
942 /* Highest beam of (UP) beam must never be lower than middle
944 ideal_y = max (ideal_y, 0.0);
945 /* Lowest beam of (UP) beam must never be lower than second staffline */
946 ideal_y = max (ideal_y, (-staff_space
947 - beam_thickness + height_of_my_beams));
950 ideal_y -= robust_scm2double (beam->get_property ("shorten"), 0);
952 SCM bemfl = scm_cdr (scm_assq (ly_symbol2scm ("beamed-extreme-minimum-free-lengths"),
956 = scm_to_double (robust_list_ref (beam_count - 1, bemfl))
960 Real minimum_length = max (minimum_free, height_of_my_trem)
962 /* stem only extends to center of beam */
963 - 0.5 * beam_thickness;
966 Real minimum_y = note_start + minimum_length;
967 Real shortest_y = minimum_y * my_dir;
969 return scm_list_2 (scm_from_double (ideal_y),
970 scm_from_double (shortest_y));
974 Stem::beam_multiplicity (Grob *stem)
976 SCM beaming = stem->get_property ("beaming");
977 Slice le = int_list_to_slice (scm_car (beaming));
978 Slice ri = int_list_to_slice (scm_cdr (beaming));
983 /* FIXME: Too many properties */
984 ADD_INTERFACE (Stem, "stem-interface",
985 "The stem represent the graphical stem. "
986 "In addition, it internally connects note heads, beams and"
988 "Rests and whole notes have invisible stems."
990 "\n\nThe following properties may be set in the details list."
992 "@item beamed-lengths \n"
993 "list of stem lengths given beam multiplicity. \n"
994 "@item beamed-minimum-free-lengths \n"
995 "list of normal minimum free stem lengths (chord to beams) given beam multiplicity.\n"
996 "@item beamed-extreme-minimum-free-lengths\n"
997 "list of extreme minimum free stem lengths (chord to beams) given beam multiplicity.\n"
999 "Default stem lengths. The list gives a length for each flag-count.\n"
1000 "@item stem-shorten\n"
1001 "How much a stem in a forced direction "
1002 "should be shortened. The list gives an amount depending on the number "
1011 "default-direction "
1020 "neutral-direction "
1025 "stem-end-position "
1033 /****************************************************************/
1035 Stem_info::Stem_info ()
1037 ideal_y_ = shortest_y_ = 0;
1042 Stem_info::scale (Real x)