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);
68 return scm_ilength (lst);
72 Stem::head_positions (Grob *me)
76 Drul_array<Grob *> e (extremal_heads (me));
77 return Interval (Staff_symbol_referencer::get_position (e[DOWN]),
78 Staff_symbol_referencer::get_position (e[UP]));
84 Stem::chord_start_y (Grob *me)
86 Interval hp = head_positions (me);
88 return hp[get_grob_direction (me)] * Staff_symbol_referencer::staff_space (me)
96 Stem::set_stemend (Grob *me, Real se)
99 Direction d = get_grob_direction (me);
101 if (d && d * head_positions (me)[get_grob_direction (me)] >= se * d)
102 me->warning (_ ("weird stem size, check for narrow beams"));
104 me->set_property ("stem-end-position", scm_from_double (se));
107 /* Note head that determines hshift for upstems
108 WARNING: triggers direction */
110 Stem::support_head (Grob *me)
112 extract_grob_set (me, "note-heads", heads);
113 if (heads.size () == 1)
116 return first_head (me);
120 Stem::head_count (Grob *me)
122 return Pointer_group_interface::count (me, ly_symbol2scm ("note-heads"));
125 /* The note head which forms one end of the stem.
126 WARNING: triggers direction */
128 Stem::first_head (Grob *me)
130 Direction d = get_grob_direction (me);
132 return extremal_heads (me)[-d];
136 /* The note head opposite to the first head. */
138 Stem::last_head (Grob *me)
140 Direction d = get_grob_direction (me);
142 return extremal_heads (me)[d];
147 START is part where stem reaches `last' head.
149 This function returns a drul with (bottom-head, top-head).
152 Stem::extremal_heads (Grob *me)
154 const int inf = 1000000;
155 Drul_array<int> extpos;
159 Drul_array<Grob *> exthead (0, 0);
160 extract_grob_set (me, "note-heads", heads);
162 for (vsize i = heads.size (); i--;)
165 int p = Staff_symbol_referencer::get_rounded_position (n);
170 if (d * p > d * extpos[d])
176 while (flip (&d) != DOWN);
182 integer_compare (int const &a, int const &b)
187 /* The positions, in ascending order. */
189 Stem::note_head_positions (Grob *me)
192 extract_grob_set (me, "note-heads", heads);
194 for (vsize i = heads.size (); i--;)
197 int p = Staff_symbol_referencer::get_rounded_position (n);
202 vector_sort (ps, integer_compare);
207 Stem::add_head (Grob *me, Grob *n)
209 n->set_object ("stem", me->self_scm ());
211 if (Note_head::has_interface (n))
212 Pointer_group_interface::add_grob (me, ly_symbol2scm ("note-heads"), n);
213 else if (Rest::has_interface (n))
214 Pointer_group_interface::add_grob (me, ly_symbol2scm ("rests"), n);
218 Stem::is_invisible (Grob *me)
220 Real stemlet_length = robust_scm2double (me->get_property ("stemlet-length"),
223 return !((head_count (me)
224 || stemlet_length > 0.0)
225 && scm_to_int (me->get_property ("duration-log")) >= 1);
228 MAKE_SCHEME_CALLBACK (Stem, pure_height, 3)
230 Stem::pure_height (SCM smob, SCM start, SCM end)
236 Grob *me = unsmob_grob (smob);
237 Real ss = Staff_symbol_referencer::staff_space (me);
238 Real len = scm_to_double (calc_length (smob)) * ss / 2;
239 Direction dir = get_grob_direction (me);
242 Interval hp = head_positions (me);
244 iv = Interval (0, len);
246 iv = Interval (-len, 0);
249 iv.translate (hp[dir] * ss / 2);
251 return ly_interval2scm (iv);
254 MAKE_SCHEME_CALLBACK (Stem, calc_stem_end_position, 1)
256 Stem::calc_stem_end_position (SCM smob)
258 Grob *me = unsmob_grob (smob);
260 if (!head_count (me))
261 return scm_from_double (0.0);
263 if (Grob *beam = get_beam (me))
265 (void) beam->get_property ("quantized-positions");
266 return me->get_property ("stem-end-position");
269 Real ss = Staff_symbol_referencer::staff_space (me);
270 int durlog = duration_log (me);
273 /* WARNING: IN HALF SPACES */
274 Real length = robust_scm2double (me->get_property ("length"), 7);
276 Direction dir = get_grob_direction (me);
277 Interval hp = head_positions (me);
278 Real stem_end = dir ? hp[dir] + dir * length : 0;
280 /* TODO: change name to extend-stems to staff/center/'() */
281 bool no_extend_b = to_boolean (me->get_property ("no-stem-extend"));
282 if (!no_extend_b && dir * stem_end < 0)
286 /* Make a little room if we have a upflag and there is a dot.
287 previous approach was to lengthen the stem. This is not
288 good typesetting practice. */
289 if (!get_beam (me) && dir == UP
292 Grob *closest_to_flag = extremal_heads (me)[dir];
293 Grob *dots = closest_to_flag
294 ? Rhythmic_head::get_dots (closest_to_flag) : 0;
298 Real dp = Staff_symbol_referencer::get_position (dots);
299 Interval flag_yext = flag (me).extent (Y_AXIS) * (2 / ss) + stem_end;
301 /* Very gory: add myself to the X-support of the parent,
302 which should be a dot-column. */
304 if (flag_yext.distance (dp) < 0.5)
306 Grob *par = dots->get_parent (X_AXIS);
308 if (Dot_column::has_interface (par))
310 Side_position_interface::add_support (par, me);
312 /* TODO: apply some better logic here. The flag is
313 curved inwards, so this will typically be too
320 return scm_from_double (stem_end);
324 MAKE_SCHEME_CALLBACK (Stem, calc_length, 1)
326 Stem::calc_length (SCM smob)
328 Grob *me = unsmob_grob (smob);
330 SCM details = me->get_property ("details");
331 int durlog = duration_log (me);
333 Real ss = Staff_symbol_referencer::staff_space (me);
335 SCM s = scm_cdr (scm_assq (ly_symbol2scm ("lengths"), details));
337 length = 2 * scm_to_double (robust_list_ref (durlog - 2, s));
339 Direction dir = get_grob_direction (me);
341 /* Stems in unnatural (forced) direction should be shortened,
342 according to [Roush & Gourlay] */
343 Interval hp = head_positions (me);
344 if (dir && dir * hp[dir] >= 0)
346 SCM sshorten = scm_cdr (scm_assq (ly_symbol2scm ("stem-shorten"), details));
347 SCM scm_shorten = scm_is_pair (sshorten)
348 ? robust_list_ref (max (duration_log (me) - 2, 0), sshorten) : SCM_EOL;
349 Real shorten = 2* robust_scm2double (scm_shorten, 0);
351 /* On boundary: shorten only half */
352 if (abs (head_positions (me)[dir]) <= 1)
358 length *= robust_scm2double (me->get_property ("length-fraction"), 1.0);
361 Grob *t_flag = unsmob_grob (me->get_object ("tremolo-flag"));
362 if (t_flag && !unsmob_grob (me->get_object ("beam")))
364 /* Crude hack: add extra space if tremolo flag is there.
366 We can't do this for the beam, since we get into a loop
367 (Stem_tremolo::raw_stencil () looks at the beam.) --hwn */
370 + 2 * t_flag->extent (t_flag, Y_AXIS).length ()
373 /* We don't want to add the whole extent of the flag because the trem
374 and the flag can overlap partly. beam_translation gives a good
378 Real beam_trans = Stem_tremolo::get_beam_translation (t_flag);
379 /* the obvious choice is (durlog - 2) here, but we need a bit more space. */
380 minlen += 2 * (durlog - 1.5) * beam_trans;
382 /* up-stems need even a little more space to avoid collisions. This
383 needs to be in sync with the tremolo positioning code in
384 Stem_tremolo::print */
386 minlen += beam_trans;
388 length = max (length, minlen + 1.0);
391 return scm_from_double (length);
393 /* The log of the duration (Number of hooks on the flag minus two) */
395 Stem::duration_log (Grob *me)
397 SCM s = me->get_property ("duration-log");
398 return (scm_is_number (s)) ? scm_to_int (s) : 2;
401 MAKE_SCHEME_CALLBACK(Stem, calc_positioning_done, 1);
403 Stem::calc_positioning_done (SCM smob)
405 Grob *me = unsmob_grob (smob);
406 if (!head_count (me))
409 extract_grob_set (me, "note-heads", ro_heads);
410 vector<Grob*> heads (ro_heads);
411 vector_sort (heads, compare_position);
412 Direction dir = get_grob_direction (me);
417 Real thick = thickness (me);
419 Grob *hed = support_head (me);
422 programming_error ("Stem dir must be up or down.");
424 set_grob_direction (me, dir);
427 Real w = hed->extent (hed, X_AXIS)[dir];
428 for (vsize i = 0; i < heads.size (); i++)
429 heads[i]->translate_axis (w - heads[i]->extent (heads[i], X_AXIS)[dir],
433 Real lastpos = Real (Staff_symbol_referencer::get_position (heads[0]));
434 for (vsize i = 1; i < heads.size (); i++)
436 Real p = Staff_symbol_referencer::get_position (heads[i]);
437 Real dy = fabs (lastpos- p);
440 dy should always be 0.5, 0.0, 1.0, but provide safety margin
447 Real ell = heads[i]->extent (heads[i], X_AXIS).length ();
449 Direction d = get_grob_direction (me);
451 Reversed head should be shifted ell-thickness, but this
452 looks too crowded, so we only shift ell-0.5*thickness.
454 This leads to assymetry: Normal heads overlap the
455 stem 100% whereas reversed heads only overlaps the
459 Real reverse_overlap = 0.5;
460 heads[i]->translate_axis ((ell - thick * reverse_overlap) * d,
463 if (is_invisible (me))
464 heads[i]->translate_axis (-thick * (2 - reverse_overlap) * d,
469 For some cases we should kern some more: when the
470 distance between the next or prev note is too large, we'd
471 get large white gaps, eg.
492 MAKE_SCHEME_CALLBACK(Stem, calc_direction, 1);
494 Stem::calc_direction (SCM smob)
496 Grob *me = unsmob_grob (smob);
497 Direction dir = CENTER;
498 if (Grob *beam = unsmob_grob (me->get_object ("beam")))
500 SCM ignore_me = beam->get_property ("direction");
502 dir = get_grob_direction (me);
506 SCM dd = me->get_property ("default-direction");
509 return me->get_property ("neutral-direction");
512 return scm_from_int (dir);
515 MAKE_SCHEME_CALLBACK(Stem, calc_default_direction, 1);
517 Stem::calc_default_direction (SCM smob)
519 Grob *me = unsmob_grob (smob);
521 Direction dir = CENTER;
522 int staff_center = 0;
523 Interval hp = head_positions (me);
526 int udistance = (int) (UP * hp[UP] - staff_center);
527 int ddistance = (int) (DOWN * hp[DOWN] - staff_center);
529 dir = Direction (sign (ddistance - udistance));
532 return scm_from_int (dir);
536 MAKE_SCHEME_CALLBACK (Stem, height, 1);
538 Stem::height (SCM smob)
540 Grob *me = unsmob_grob (smob);
542 Direction dir = get_grob_direction (me);
546 UGH. Should be automatic
548 Grob *beam = get_beam (me);
551 /* trigger set-stem-lengths. */
552 beam->get_property ("quantized-positions");
556 Can't get_stencil(), since that would cache stencils too early.
557 This causes problems with beams.
559 Stencil *stencil = unsmob_stencil (print (smob));
560 Interval iv = stencil ? stencil->extent (Y_AXIS) : Interval();
565 programming_error ("no stem direction");
568 iv[dir] += dir * Beam::get_thickness (beam) * 0.5;
571 return ly_interval2scm (iv);
575 Stem::stem_end_position (Grob *me)
577 return robust_scm2double (me->get_property ("stem-end-position"), 0);
581 Stem::flag (Grob *me)
583 int log = duration_log (me);
585 || unsmob_grob (me->get_object ("beam")))
589 TODO: maybe property stroke-style should take different values,
590 e.g. "" (i.e. no stroke), "single" and "double" (currently, it's
594 SCM flag_style_scm = me->get_property ("flag-style");
595 if (scm_is_symbol (flag_style_scm))
596 flag_style = ly_symbol2string (flag_style_scm);
598 if (flag_style == "no-flag")
603 string staffline_offs;
604 if (flag_style == "mensural")
605 /* Mensural notation: For notes on staff lines, use different
606 flags than for notes between staff lines. The idea is that
607 flags are always vertically aligned with the staff lines,
608 regardless if the note head is on a staff line or between two
609 staff lines. In other words, the inner end of a flag always
610 touches a staff line.
615 int p = (int) (rint (stem_end_position (me)));
617 = Staff_symbol_referencer::on_line (me, p) ? "0" : "1";
620 staffline_offs = "2";
625 char dir = (get_grob_direction (me) == UP) ? 'u' : 'd';
626 string font_char = flag_style
627 + to_string (dir) + staffline_offs + to_string (log);
628 Font_metric *fm = Font_interface::get_default_font (me);
629 Stencil flag = fm->find_by_name ("flags." + font_char);
630 if (flag.is_empty ())
631 me->warning (_f ("flag `%s' not found", font_char));
633 SCM stroke_style_scm = me->get_property ("stroke-style");
634 if (scm_is_string (stroke_style_scm))
636 string stroke_style = ly_scm2string (stroke_style_scm);
637 if (!stroke_style.empty ())
639 string font_char = to_string (dir) + stroke_style;
640 Stencil stroke = fm->find_by_name ("flags." + font_char);
641 if (stroke.is_empty ())
642 me->warning (_f ("flag stroke `%s' not found", font_char));
644 flag.add_stencil (stroke);
651 MAKE_SCHEME_CALLBACK (Stem, width, 1);
655 Grob *me = unsmob_grob (e);
659 if (is_invisible (me))
661 else if (unsmob_grob (me->get_object ("beam"))
662 || abs (duration_log (me)) <= 2)
664 r = Interval (-1, 1);
665 r *= thickness (me) / 2;
669 r = Interval (-1, 1) * thickness (me) * 0.5;
670 r.unite (flag (me).extent (X_AXIS));
672 return ly_interval2scm (r);
676 Stem::thickness (Grob *me)
678 return scm_to_double (me->get_property ("thickness"))
679 * Staff_symbol_referencer::line_thickness (me);
682 MAKE_SCHEME_CALLBACK (Stem, print, 1);
684 Stem::print (SCM smob)
686 Grob *me = unsmob_grob (smob);
687 Grob *beam = get_beam (me);
690 Direction d = get_grob_direction (me);
692 Real stemlet_length = robust_scm2double (me->get_property ("stemlet-length"),
694 bool stemlet = stemlet_length > 0.0;
696 /* TODO: make the stem start a direction ?
697 This is required to avoid stems passing in tablature chords. */
699 = to_boolean (me->get_property ("avoid-note-head"))
706 if (!lh && stemlet && !beam)
709 if (is_invisible (me))
712 Real y2 = robust_scm2double (me->get_property ("stem-end-position"), 0.0);
714 Real half_space = Staff_symbol_referencer::staff_space (me) * 0.5;
717 y2 = Staff_symbol_referencer::get_position (lh);
720 Real beam_translation = Beam::get_beam_translation (beam);
721 Real beam_thickness = Beam::get_thickness (beam);
722 int beam_count = beam_multiplicity (me).length () + 1;
725 * (0.5 * beam_thickness
726 + beam_translation * max (0, (beam_count - 1))
727 + stemlet_length) / half_space;
730 Interval stem_y (min (y1, y2), max (y2, y1));
732 if (Grob *head = support_head (me))
735 must not take ledgers into account.
737 Interval head_height = head->extent (head, Y_AXIS);
738 Real y_attach = Note_head::stem_attachment_coordinate (head, Y_AXIS);
740 y_attach = head_height.linear_combination (y_attach);
741 stem_y[Direction (-d)] += d * y_attach / half_space;
745 Real stem_width = thickness (me);
747 = me->layout ()->get_dimension (ly_symbol2scm ("blot-diameter"));
749 Box b = Box (Interval (-stem_width / 2, stem_width / 2),
750 Interval (stem_y[DOWN] * half_space, stem_y[UP] * half_space));
752 Stencil ss = Lookup::round_filled_box (b, blot);
753 mol.add_stencil (ss);
755 mol.add_stencil (get_translated_flag (me));
757 return mol.smobbed_copy ();
761 Stem::get_translated_flag (Grob *me)
763 Stencil fl = flag (me);
766 Direction d = get_grob_direction (me);
768 = me->layout ()->get_dimension (ly_symbol2scm ("blot-diameter"));
769 Real stem_width = thickness (me);
770 Real half_space = Staff_symbol_referencer::staff_space (me) * 0.5;
771 Real y2 = robust_scm2double (me->get_property ("stem-end-position"), 0.0);
772 fl.translate_axis (y2 * half_space - d * blot / 2, Y_AXIS);
773 fl.translate_axis (stem_width / 2, X_AXIS);
780 move the stem to right of the notehead if it is up.
782 MAKE_SCHEME_CALLBACK (Stem, offset_callback, 1);
784 Stem::offset_callback (SCM smob)
786 Grob *me = unsmob_grob (smob);
789 if (Grob *f = first_head (me))
791 Interval head_wid = f->extent (f, X_AXIS);
794 if (is_invisible (me))
797 attach = Note_head::stem_attachment_coordinate (f, X_AXIS);
799 Direction d = get_grob_direction (me);
800 Real real_attach = head_wid.linear_combination (d * attach);
803 /* If not centered: correct for stem thickness. */
806 Real rule_thick = thickness (me);
807 r += -d * rule_thick * 0.5;
812 extract_grob_set (me, "rests", rests);
815 Grob *rest = rests.back ();
816 r = rest->extent (rest, X_AXIS).center ();
819 return scm_from_double (r);
823 Stem::get_beam (Grob *me)
825 SCM b = me->get_object ("beam");
826 return dynamic_cast<Spanner *> (unsmob_grob (b));
830 Stem::get_stem_info (Grob *me)
833 si.dir_ = get_grob_direction (me);
835 SCM scm_info = me->get_property ("stem-info");
836 si.ideal_y_ = scm_to_double (scm_car (scm_info));
837 si.shortest_y_ = scm_to_double (scm_cadr (scm_info));
841 MAKE_SCHEME_CALLBACK(Stem, calc_stem_info, 1);
843 Stem::calc_stem_info (SCM smob)
845 Grob *me = unsmob_grob (smob);
846 Direction my_dir = get_grob_direction (me);
850 programming_error ("no stem dir set");
854 Real staff_space = Staff_symbol_referencer::staff_space (me);
855 Grob *beam = get_beam (me);
859 (void) beam->get_property ("beaming");
862 Real beam_translation = Beam::get_beam_translation (beam);
863 Real beam_thickness = Beam::get_thickness (beam);
864 int beam_count = Beam::get_direction_beam_count (beam, my_dir);
866 = robust_scm2double (me->get_property ("length-fraction"), 1.0);
868 /* Simple standard stem length */
869 SCM details = me->get_property ("details");
870 SCM lengths = scm_cdr (scm_assq (ly_symbol2scm ("beamed-lengths"), details));
873 = scm_to_double (robust_list_ref (beam_count - 1, lengths))
877 /* stem only extends to center of beam
879 - 0.5 * beam_thickness;
881 /* Condition: sane minimum free stem length (chord to beams) */
882 lengths = scm_cdr (scm_assq (ly_symbol2scm ("beamed-minimum-free-lengths"), details));
884 Real ideal_minimum_free
885 = scm_to_double (robust_list_ref (beam_count - 1, lengths))
889 Real height_of_my_trem = 0.0;
890 Grob *trem = unsmob_grob (me->get_object ("tremolo-flag"));
892 height_of_my_trem = trem->extent (trem, Y_AXIS).length ()
893 /* hack a bit of space around the trem. */
897 It seems that also for ideal minimum length, we must use
898 the maximum beam count (for this direction):
900 \score{ \notes\relative c''{ [a8 a32] }}
902 must be horizontal. */
903 Real height_of_my_beams = beam_thickness
904 + (beam_count - 1) * beam_translation;
906 Real ideal_minimum_length = ideal_minimum_free
909 /* stem only extends to center of beam */
910 - 0.5 * beam_thickness;
912 ideal_length = max (ideal_length, ideal_minimum_length);
914 /* Convert to Y position, calculate for dir == UP */
916 = /* staff positions */
917 head_positions (me)[my_dir] * 0.5
918 * my_dir * staff_space;
919 Real ideal_y = note_start + ideal_length;
921 /* Conditions for Y position */
923 /* Lowest beam of (UP) beam must never be lower than second staffline
927 Although this (additional) rule is probably correct,
928 I expect that highest beam (UP) should also never be lower
929 than middle staffline, just as normal stems.
933 Obviously not for grace beams.
935 Also, not for knees. Seems to be a good thing. */
936 bool no_extend_b = to_boolean (me->get_property ("no-stem-extend"));
937 bool is_knee = to_boolean (beam->get_property ("knee"));
938 if (!no_extend_b && !is_knee)
940 /* Highest beam of (UP) beam must never be lower than middle
942 ideal_y = max (ideal_y, 0.0);
943 /* Lowest beam of (UP) beam must never be lower than second staffline */
944 ideal_y = max (ideal_y, (-staff_space
945 - beam_thickness + height_of_my_beams));
948 ideal_y -= robust_scm2double (beam->get_property ("shorten"), 0);
950 SCM bemfl = scm_cdr (scm_assq (ly_symbol2scm ("beamed-extreme-minimum-free-lengths"),
954 = scm_to_double (robust_list_ref (beam_count - 1, bemfl))
958 Real minimum_length = max (minimum_free, height_of_my_trem)
960 /* stem only extends to center of beam */
961 - 0.5 * beam_thickness;
964 Real minimum_y = note_start + minimum_length;
965 Real shortest_y = minimum_y * my_dir;
967 return scm_list_2 (scm_from_double (ideal_y),
968 scm_from_double (shortest_y));
972 Stem::beam_multiplicity (Grob *stem)
974 SCM beaming = stem->get_property ("beaming");
975 Slice le = int_list_to_slice (scm_car (beaming));
976 Slice ri = int_list_to_slice (scm_cdr (beaming));
981 /* FIXME: Too many properties */
982 ADD_INTERFACE (Stem, "stem-interface",
983 "The stem represent the graphical stem. "
984 "In addition, it internally connects note heads, beams and"
986 "Rests and whole notes have invisible stems."
988 "\n\nThe following properties may be set in the details list."
990 "@item beamed-lengths \n"
991 "list of stem lengths given beam multiplicity. \n"
992 "@item beamed-minimum-free-lengths \n"
993 "list of normal minimum free stem lengths (chord to beams) given beam multiplicity.\n"
994 "@item beamed-extreme-minimum-free-lengths\n"
995 "list of extreme minimum free stem lengths (chord to beams) given beam multiplicity.\n"
997 "Default stem lengths. The list gives a length for each flag-count.\n"
998 "@item stem-shorten\n"
999 "How much a stem in a forced direction "
1000 "should be shortened. The list gives an amount depending on the number "
1009 "default-direction "
1018 "neutral-direction "
1023 "stem-end-position "
1031 /****************************************************************/
1033 Stem_info::Stem_info ()
1035 ideal_y_ = shortest_y_ = 0;
1040 Stem_info::scale (Real x)