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);
183 /* The positions, in ascending order. */
185 Stem::note_head_positions (Grob *me)
188 extract_grob_set (me, "note-heads", heads);
190 for (vsize i = heads.size (); i--;)
193 int p = Staff_symbol_referencer::get_rounded_position (n);
198 vector_sort (ps, less<int> ());
203 Stem::add_head (Grob *me, Grob *n)
205 n->set_object ("stem", me->self_scm ());
207 if (Note_head::has_interface (n))
208 Pointer_group_interface::add_grob (me, ly_symbol2scm ("note-heads"), n);
209 else if (Rest::has_interface (n))
210 Pointer_group_interface::add_grob (me, ly_symbol2scm ("rests"), n);
214 Stem::is_invisible (Grob *me)
216 Real stemlet_length = robust_scm2double (me->get_property ("stemlet-length"),
219 return !((head_count (me)
220 || stemlet_length > 0.0)
221 && scm_to_int (me->get_property ("duration-log")) >= 1);
224 MAKE_SCHEME_CALLBACK (Stem, pure_height, 3)
226 Stem::pure_height (SCM smob, SCM start, SCM end)
232 Grob *me = unsmob_grob (smob);
233 Real ss = Staff_symbol_referencer::staff_space (me);
234 Real len = scm_to_double (calc_length (smob)) * ss / 2;
235 Direction dir = get_grob_direction (me);
238 Interval hp = head_positions (me);
240 iv = Interval (0, len);
242 iv = Interval (-len, 0);
245 iv.translate (hp[dir] * ss / 2);
247 return ly_interval2scm (iv);
250 MAKE_SCHEME_CALLBACK (Stem, calc_stem_end_position, 1)
252 Stem::calc_stem_end_position (SCM smob)
254 Grob *me = unsmob_grob (smob);
256 if (!head_count (me))
257 return scm_from_double (0.0);
259 if (Grob *beam = get_beam (me))
261 (void) beam->get_property ("quantized-positions");
262 return me->get_property ("stem-end-position");
265 Real ss = Staff_symbol_referencer::staff_space (me);
266 int durlog = duration_log (me);
269 /* WARNING: IN HALF SPACES */
270 Real length = robust_scm2double (me->get_property ("length"), 7);
272 Direction dir = get_grob_direction (me);
273 Interval hp = head_positions (me);
274 Real stem_end = dir ? hp[dir] + dir * length : 0;
276 /* TODO: change name to extend-stems to staff/center/'() */
277 bool no_extend_b = to_boolean (me->get_property ("no-stem-extend"));
278 if (!no_extend_b && dir * stem_end < 0)
282 /* Make a little room if we have a upflag and there is a dot.
283 previous approach was to lengthen the stem. This is not
284 good typesetting practice. */
285 if (!get_beam (me) && dir == UP
288 Grob *closest_to_flag = extremal_heads (me)[dir];
289 Grob *dots = closest_to_flag
290 ? Rhythmic_head::get_dots (closest_to_flag) : 0;
294 Real dp = Staff_symbol_referencer::get_position (dots);
295 Interval flag_yext = flag (me).extent (Y_AXIS) * (2 / ss) + stem_end;
297 /* Very gory: add myself to the X-support of the parent,
298 which should be a dot-column. */
300 if (flag_yext.distance (dp) < 0.5)
302 Grob *par = dots->get_parent (X_AXIS);
304 if (Dot_column::has_interface (par))
306 Side_position_interface::add_support (par, me);
308 /* TODO: apply some better logic here. The flag is
309 curved inwards, so this will typically be too
316 return scm_from_double (stem_end);
320 MAKE_SCHEME_CALLBACK (Stem, calc_length, 1)
322 Stem::calc_length (SCM smob)
324 Grob *me = unsmob_grob (smob);
326 SCM details = me->get_property ("details");
327 int durlog = duration_log (me);
329 Real ss = Staff_symbol_referencer::staff_space (me);
331 SCM s = scm_cdr (scm_assq (ly_symbol2scm ("lengths"), details));
333 length = 2 * scm_to_double (robust_list_ref (durlog - 2, s));
335 Direction dir = get_grob_direction (me);
337 /* Stems in unnatural (forced) direction should be shortened,
338 according to [Roush & Gourlay] */
339 Interval hp = head_positions (me);
340 if (dir && dir * hp[dir] >= 0)
342 SCM sshorten = scm_cdr (scm_assq (ly_symbol2scm ("stem-shorten"), details));
343 SCM scm_shorten = scm_is_pair (sshorten)
344 ? robust_list_ref (max (duration_log (me) - 2, 0), sshorten) : SCM_EOL;
345 Real shorten = 2* robust_scm2double (scm_shorten, 0);
347 /* On boundary: shorten only half */
348 if (abs (head_positions (me)[dir]) <= 1)
354 length *= robust_scm2double (me->get_property ("length-fraction"), 1.0);
357 Grob *t_flag = unsmob_grob (me->get_object ("tremolo-flag"));
358 if (t_flag && !unsmob_grob (me->get_object ("beam")))
360 /* Crude hack: add extra space if tremolo flag is there.
362 We can't do this for the beam, since we get into a loop
363 (Stem_tremolo::raw_stencil () looks at the beam.) --hwn */
366 + 2 * t_flag->extent (t_flag, Y_AXIS).length ()
369 /* We don't want to add the whole extent of the flag because the trem
370 and the flag can overlap partly. beam_translation gives a good
374 Real beam_trans = Stem_tremolo::get_beam_translation (t_flag);
375 /* the obvious choice is (durlog - 2) here, but we need a bit more space. */
376 minlen += 2 * (durlog - 1.5) * beam_trans;
378 /* up-stems need even a little more space to avoid collisions. This
379 needs to be in sync with the tremolo positioning code in
380 Stem_tremolo::print */
382 minlen += beam_trans;
384 length = max (length, minlen + 1.0);
387 return scm_from_double (length);
389 /* The log of the duration (Number of hooks on the flag minus two) */
391 Stem::duration_log (Grob *me)
393 SCM s = me->get_property ("duration-log");
394 return (scm_is_number (s)) ? scm_to_int (s) : 2;
397 MAKE_SCHEME_CALLBACK(Stem, calc_positioning_done, 1);
399 Stem::calc_positioning_done (SCM smob)
401 Grob *me = unsmob_grob (smob);
402 if (!head_count (me))
405 extract_grob_set (me, "note-heads", ro_heads);
406 vector<Grob*> heads (ro_heads);
407 vector_sort (heads, position_less);
408 Direction dir = get_grob_direction (me);
413 Real thick = thickness (me);
415 Grob *hed = support_head (me);
418 programming_error ("Stem dir must be up or down.");
420 set_grob_direction (me, dir);
423 Real w = hed->extent (hed, X_AXIS)[dir];
424 for (vsize i = 0; i < heads.size (); i++)
425 heads[i]->translate_axis (w - heads[i]->extent (heads[i], X_AXIS)[dir],
429 Real lastpos = Real (Staff_symbol_referencer::get_position (heads[0]));
430 for (vsize i = 1; i < heads.size (); i++)
432 Real p = Staff_symbol_referencer::get_position (heads[i]);
433 Real dy = fabs (lastpos- p);
436 dy should always be 0.5, 0.0, 1.0, but provide safety margin
443 Real ell = heads[i]->extent (heads[i], X_AXIS).length ();
445 Direction d = get_grob_direction (me);
447 Reversed head should be shifted ell-thickness, but this
448 looks too crowded, so we only shift ell-0.5*thickness.
450 This leads to assymetry: Normal heads overlap the
451 stem 100% whereas reversed heads only overlaps the
455 Real reverse_overlap = 0.5;
456 heads[i]->translate_axis ((ell - thick * reverse_overlap) * d,
459 if (is_invisible (me))
460 heads[i]->translate_axis (-thick * (2 - reverse_overlap) * d,
465 For some cases we should kern some more: when the
466 distance between the next or prev note is too large, we'd
467 get large white gaps, eg.
488 MAKE_SCHEME_CALLBACK(Stem, calc_direction, 1);
490 Stem::calc_direction (SCM smob)
492 Grob *me = unsmob_grob (smob);
493 Direction dir = CENTER;
494 if (Grob *beam = unsmob_grob (me->get_object ("beam")))
496 SCM ignore_me = beam->get_property ("direction");
498 dir = get_grob_direction (me);
502 SCM dd = me->get_property ("default-direction");
505 return me->get_property ("neutral-direction");
508 return scm_from_int (dir);
511 MAKE_SCHEME_CALLBACK(Stem, calc_default_direction, 1);
513 Stem::calc_default_direction (SCM smob)
515 Grob *me = unsmob_grob (smob);
517 Direction dir = CENTER;
518 int staff_center = 0;
519 Interval hp = head_positions (me);
522 int udistance = (int) (UP * hp[UP] - staff_center);
523 int ddistance = (int) (DOWN * hp[DOWN] - staff_center);
525 dir = Direction (sign (ddistance - udistance));
528 return scm_from_int (dir);
532 MAKE_SCHEME_CALLBACK (Stem, height, 1);
534 Stem::height (SCM smob)
536 Grob *me = unsmob_grob (smob);
538 Direction dir = get_grob_direction (me);
542 UGH. Should be automatic
544 Grob *beam = get_beam (me);
547 /* trigger set-stem-lengths. */
548 beam->get_property ("quantized-positions");
552 Can't get_stencil(), since that would cache stencils too early.
553 This causes problems with beams.
555 Stencil *stencil = unsmob_stencil (print (smob));
556 Interval iv = stencil ? stencil->extent (Y_AXIS) : Interval();
561 programming_error ("no stem direction");
564 iv[dir] += dir * Beam::get_thickness (beam) * 0.5;
567 return ly_interval2scm (iv);
571 Stem::stem_end_position (Grob *me)
573 return robust_scm2double (me->get_property ("stem-end-position"), 0);
577 Stem::flag (Grob *me)
579 int log = duration_log (me);
581 || unsmob_grob (me->get_object ("beam")))
585 TODO: maybe property stroke-style should take different values,
586 e.g. "" (i.e. no stroke), "single" and "double" (currently, it's
590 SCM flag_style_scm = me->get_property ("flag-style");
591 if (scm_is_symbol (flag_style_scm))
592 flag_style = ly_symbol2string (flag_style_scm);
594 if (flag_style == "no-flag")
599 string staffline_offs;
600 if (flag_style == "mensural")
601 /* Mensural notation: For notes on staff lines, use different
602 flags than for notes between staff lines. The idea is that
603 flags are always vertically aligned with the staff lines,
604 regardless if the note head is on a staff line or between two
605 staff lines. In other words, the inner end of a flag always
606 touches a staff line.
611 int p = (int) (rint (stem_end_position (me)));
613 = Staff_symbol_referencer::on_line (me, p) ? "0" : "1";
616 staffline_offs = "2";
621 char dir = (get_grob_direction (me) == UP) ? 'u' : 'd';
622 string font_char = flag_style
623 + to_string (dir) + staffline_offs + to_string (log);
624 Font_metric *fm = Font_interface::get_default_font (me);
625 Stencil flag = fm->find_by_name ("flags." + font_char);
626 if (flag.is_empty ())
627 me->warning (_f ("flag `%s' not found", font_char));
629 SCM stroke_style_scm = me->get_property ("stroke-style");
630 if (scm_is_string (stroke_style_scm))
632 string stroke_style = ly_scm2string (stroke_style_scm);
633 if (!stroke_style.empty ())
635 string font_char = to_string (dir) + stroke_style;
636 Stencil stroke = fm->find_by_name ("flags." + font_char);
637 if (stroke.is_empty ())
638 me->warning (_f ("flag stroke `%s' not found", font_char));
640 flag.add_stencil (stroke);
647 MAKE_SCHEME_CALLBACK (Stem, width, 1);
651 Grob *me = unsmob_grob (e);
655 if (is_invisible (me))
657 else if (unsmob_grob (me->get_object ("beam"))
658 || abs (duration_log (me)) <= 2)
660 r = Interval (-1, 1);
661 r *= thickness (me) / 2;
665 r = Interval (-1, 1) * thickness (me) * 0.5;
666 r.unite (flag (me).extent (X_AXIS));
668 return ly_interval2scm (r);
672 Stem::thickness (Grob *me)
674 return scm_to_double (me->get_property ("thickness"))
675 * Staff_symbol_referencer::line_thickness (me);
678 MAKE_SCHEME_CALLBACK (Stem, print, 1);
680 Stem::print (SCM smob)
682 Grob *me = unsmob_grob (smob);
683 Grob *beam = get_beam (me);
686 Direction d = get_grob_direction (me);
688 Real stemlet_length = robust_scm2double (me->get_property ("stemlet-length"),
690 bool stemlet = stemlet_length > 0.0;
692 /* TODO: make the stem start a direction ?
693 This is required to avoid stems passing in tablature chords. */
695 = to_boolean (me->get_property ("avoid-note-head"))
702 if (!lh && stemlet && !beam)
705 if (is_invisible (me))
708 Real y2 = robust_scm2double (me->get_property ("stem-end-position"), 0.0);
710 Real half_space = Staff_symbol_referencer::staff_space (me) * 0.5;
713 y2 = Staff_symbol_referencer::get_position (lh);
716 Real beam_translation = Beam::get_beam_translation (beam);
717 Real beam_thickness = Beam::get_thickness (beam);
718 int beam_count = beam_multiplicity (me).length () + 1;
721 * (0.5 * beam_thickness
722 + beam_translation * max (0, (beam_count - 1))
723 + stemlet_length) / half_space;
726 Interval stem_y (min (y1, y2), max (y2, y1));
728 if (Grob *head = support_head (me))
731 must not take ledgers into account.
733 Interval head_height = head->extent (head, Y_AXIS);
734 Real y_attach = Note_head::stem_attachment_coordinate (head, Y_AXIS);
736 y_attach = head_height.linear_combination (y_attach);
737 stem_y[Direction (-d)] += d * y_attach / half_space;
741 Real stem_width = thickness (me);
743 = me->layout ()->get_dimension (ly_symbol2scm ("blot-diameter"));
745 Box b = Box (Interval (-stem_width / 2, stem_width / 2),
746 Interval (stem_y[DOWN] * half_space, stem_y[UP] * half_space));
748 Stencil ss = Lookup::round_filled_box (b, blot);
749 mol.add_stencil (ss);
751 mol.add_stencil (get_translated_flag (me));
753 return mol.smobbed_copy ();
757 Stem::get_translated_flag (Grob *me)
759 Stencil fl = flag (me);
762 Direction d = get_grob_direction (me);
764 = me->layout ()->get_dimension (ly_symbol2scm ("blot-diameter"));
765 Real stem_width = thickness (me);
766 Real half_space = Staff_symbol_referencer::staff_space (me) * 0.5;
767 Real y2 = robust_scm2double (me->get_property ("stem-end-position"), 0.0);
768 fl.translate_axis (y2 * half_space - d * blot / 2, Y_AXIS);
769 fl.translate_axis (stem_width / 2, X_AXIS);
776 move the stem to right of the notehead if it is up.
778 MAKE_SCHEME_CALLBACK (Stem, offset_callback, 1);
780 Stem::offset_callback (SCM smob)
782 Grob *me = unsmob_grob (smob);
785 if (Grob *f = first_head (me))
787 Interval head_wid = f->extent (f, X_AXIS);
790 if (is_invisible (me))
793 attach = Note_head::stem_attachment_coordinate (f, X_AXIS);
795 Direction d = get_grob_direction (me);
796 Real real_attach = head_wid.linear_combination (d * attach);
799 /* If not centered: correct for stem thickness. */
802 Real rule_thick = thickness (me);
803 r += -d * rule_thick * 0.5;
808 extract_grob_set (me, "rests", rests);
811 Grob *rest = rests.back ();
812 r = rest->extent (rest, X_AXIS).center ();
815 return scm_from_double (r);
819 Stem::get_beam (Grob *me)
821 SCM b = me->get_object ("beam");
822 return dynamic_cast<Spanner *> (unsmob_grob (b));
826 Stem::get_stem_info (Grob *me)
829 si.dir_ = get_grob_direction (me);
831 SCM scm_info = me->get_property ("stem-info");
832 si.ideal_y_ = scm_to_double (scm_car (scm_info));
833 si.shortest_y_ = scm_to_double (scm_cadr (scm_info));
837 MAKE_SCHEME_CALLBACK(Stem, calc_stem_info, 1);
839 Stem::calc_stem_info (SCM smob)
841 Grob *me = unsmob_grob (smob);
842 Direction my_dir = get_grob_direction (me);
846 programming_error ("no stem dir set");
850 Real staff_space = Staff_symbol_referencer::staff_space (me);
851 Grob *beam = get_beam (me);
855 (void) beam->get_property ("beaming");
858 Real beam_translation = Beam::get_beam_translation (beam);
859 Real beam_thickness = Beam::get_thickness (beam);
860 int beam_count = Beam::get_direction_beam_count (beam, my_dir);
862 = robust_scm2double (me->get_property ("length-fraction"), 1.0);
864 /* Simple standard stem length */
865 SCM details = me->get_property ("details");
866 SCM lengths = scm_cdr (scm_assq (ly_symbol2scm ("beamed-lengths"), details));
869 = scm_to_double (robust_list_ref (beam_count - 1, lengths))
873 /* stem only extends to center of beam
875 - 0.5 * beam_thickness;
877 /* Condition: sane minimum free stem length (chord to beams) */
878 lengths = scm_cdr (scm_assq (ly_symbol2scm ("beamed-minimum-free-lengths"), details));
880 Real ideal_minimum_free
881 = scm_to_double (robust_list_ref (beam_count - 1, lengths))
885 Real height_of_my_trem = 0.0;
886 Grob *trem = unsmob_grob (me->get_object ("tremolo-flag"));
888 height_of_my_trem = trem->extent (trem, Y_AXIS).length ()
889 /* hack a bit of space around the trem. */
893 It seems that also for ideal minimum length, we must use
894 the maximum beam count (for this direction):
896 \score{ \notes\relative c''{ [a8 a32] }}
898 must be horizontal. */
899 Real height_of_my_beams = beam_thickness
900 + (beam_count - 1) * beam_translation;
902 Real ideal_minimum_length = ideal_minimum_free
905 /* stem only extends to center of beam */
906 - 0.5 * beam_thickness;
908 ideal_length = max (ideal_length, ideal_minimum_length);
910 /* Convert to Y position, calculate for dir == UP */
912 = /* staff positions */
913 head_positions (me)[my_dir] * 0.5
914 * my_dir * staff_space;
915 Real ideal_y = note_start + ideal_length;
917 /* Conditions for Y position */
919 /* Lowest beam of (UP) beam must never be lower than second staffline
923 Although this (additional) rule is probably correct,
924 I expect that highest beam (UP) should also never be lower
925 than middle staffline, just as normal stems.
929 Obviously not for grace beams.
931 Also, not for knees. Seems to be a good thing. */
932 bool no_extend_b = to_boolean (me->get_property ("no-stem-extend"));
933 bool is_knee = to_boolean (beam->get_property ("knee"));
934 if (!no_extend_b && !is_knee)
936 /* Highest beam of (UP) beam must never be lower than middle
938 ideal_y = max (ideal_y, 0.0);
939 /* Lowest beam of (UP) beam must never be lower than second staffline */
940 ideal_y = max (ideal_y, (-staff_space
941 - beam_thickness + height_of_my_beams));
944 ideal_y -= robust_scm2double (beam->get_property ("shorten"), 0);
946 SCM bemfl = scm_cdr (scm_assq (ly_symbol2scm ("beamed-extreme-minimum-free-lengths"),
950 = scm_to_double (robust_list_ref (beam_count - 1, bemfl))
954 Real minimum_length = max (minimum_free, height_of_my_trem)
956 /* stem only extends to center of beam */
957 - 0.5 * beam_thickness;
960 Real minimum_y = note_start + minimum_length;
961 Real shortest_y = minimum_y * my_dir;
963 return scm_list_2 (scm_from_double (ideal_y),
964 scm_from_double (shortest_y));
968 Stem::beam_multiplicity (Grob *stem)
970 SCM beaming = stem->get_property ("beaming");
971 Slice le = int_list_to_slice (scm_car (beaming));
972 Slice ri = int_list_to_slice (scm_cdr (beaming));
977 /* FIXME: Too many properties */
978 ADD_INTERFACE (Stem, "stem-interface",
979 "The stem represent the graphical stem. "
980 "In addition, it internally connects note heads, beams and"
982 "Rests and whole notes have invisible stems."
984 "\n\nThe following properties may be set in the details list."
986 "@item beamed-lengths \n"
987 "list of stem lengths given beam multiplicity. \n"
988 "@item beamed-minimum-free-lengths \n"
989 "list of normal minimum free stem lengths (chord to beams) given beam multiplicity.\n"
990 "@item beamed-extreme-minimum-free-lengths\n"
991 "list of extreme minimum free stem lengths (chord to beams) given beam multiplicity.\n"
993 "Default stem lengths. The list gives a length for each flag-count.\n"
994 "@item stem-shorten\n"
995 "How much a stem in a forced direction "
996 "should be shortened. The list gives an amount depending on the number "
1005 "default-direction "
1014 "neutral-direction "
1019 "stem-end-position "
1027 /****************************************************************/
1029 Stem_info::Stem_info ()
1031 ideal_y_ = shortest_y_ = 0;
1036 Stem_info::scale (Real x)