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, calc_stem_end_position, 1)
230 Stem::calc_stem_end_position (SCM smob)
232 Grob *me = unsmob_grob (smob);
234 if (!head_count (me))
235 return scm_from_double (0.0);
238 Real ss = Staff_symbol_referencer::staff_space (me);
239 int durlog = duration_log (me);
242 /* WARNING: IN HALF SPACES */
243 Real length = robust_scm2double (me->get_property ("length"), 7);
245 Direction dir = get_grob_direction (me);
246 Interval hp = head_positions (me);
247 Real stem_end = dir ? hp[dir] + dir * length : 0;
249 /* TODO: change name to extend-stems to staff/center/'() */
250 bool no_extend_b = to_boolean (me->get_property ("no-stem-extend"));
251 if (!no_extend_b && dir * stem_end < 0)
255 /* Make a little room if we have a upflag and there is a dot.
256 previous approach was to lengthen the stem. This is not
257 good typesetting practice. */
258 if (!get_beam (me) && dir == UP
261 Grob *closest_to_flag = extremal_heads (me)[dir];
262 Grob *dots = closest_to_flag
263 ? Rhythmic_head::get_dots (closest_to_flag) : 0;
267 Real dp = Staff_symbol_referencer::get_position (dots);
268 Interval flag_yext = flag (me).extent (Y_AXIS) * (2 / ss) + stem_end;
270 /* Very gory: add myself to the X-support of the parent,
271 which should be a dot-column. */
273 if (flag_yext.distance (dp) < 0.5)
275 Grob *par = dots->get_parent (X_AXIS);
277 if (Dot_column::has_interface (par))
279 Side_position_interface::add_support (par, me);
281 /* TODO: apply some better logic here. The flag is
282 curved inwards, so this will typically be too
289 return scm_from_double (stem_end);
293 MAKE_SCHEME_CALLBACK (Stem, calc_length, 1)
295 Stem::calc_length (SCM smob)
297 Grob *me = unsmob_grob (smob);
299 SCM details = me->get_property ("details");
300 int durlog = duration_log (me);
302 Real ss = Staff_symbol_referencer::staff_space (me);
304 SCM s = scm_cdr (scm_assq (ly_symbol2scm ("lengths"), details));
306 length = 2 * scm_to_double (robust_list_ref (durlog - 2, s));
308 Direction dir = get_grob_direction (me);
310 /* Stems in unnatural (forced) direction should be shortened,
311 according to [Roush & Gourlay] */
312 Interval hp = head_positions (me);
313 if (dir && dir * hp[dir] >= 0)
315 SCM sshorten = scm_cdr (scm_assq (ly_symbol2scm ("stem-shorten"), details));
316 SCM scm_shorten = scm_is_pair (sshorten)
317 ? robust_list_ref (max (duration_log (me) - 2, 0), sshorten) : SCM_EOL;
318 Real shorten = 2* robust_scm2double (scm_shorten, 0);
320 /* On boundary: shorten only half */
321 if (abs (head_positions (me)[dir]) <= 1)
327 length *= robust_scm2double (me->get_property ("length-fraction"), 1.0);
330 Grob *t_flag = unsmob_grob (me->get_object ("tremolo-flag"));
331 if (t_flag && !unsmob_grob (me->get_object ("beam")))
333 /* Crude hack: add extra space if tremolo flag is there.
335 We can't do this for the beam, since we get into a loop
336 (Stem_tremolo::raw_stencil () looks at the beam.) --hwn */
339 + 2 * t_flag->extent (t_flag, Y_AXIS).length ()
342 /* We don't want to add the whole extent of the flag because the trem
343 and the flag can overlap partly. beam_translation gives a good
347 Real beam_trans = Stem_tremolo::get_beam_translation (t_flag);
348 /* the obvious choice is (durlog - 2) here, but we need a bit more space. */
349 minlen += 2 * (durlog - 1.5) * beam_trans;
351 /* up-stems need even a little more space to avoid collisions. This
352 needs to be in sync with the tremolo positioning code in
353 Stem_tremolo::print */
355 minlen += beam_trans;
357 length = max (length, minlen + 1.0);
360 return scm_from_double (length);
362 /* The log of the duration (Number of hooks on the flag minus two) */
364 Stem::duration_log (Grob *me)
366 SCM s = me->get_property ("duration-log");
367 return (scm_is_number (s)) ? scm_to_int (s) : 2;
370 MAKE_SCHEME_CALLBACK(Stem, calc_positioning_done, 1);
372 Stem::calc_positioning_done (SCM smob)
374 Grob *me = unsmob_grob (smob);
375 if (!head_count (me))
378 extract_grob_set (me, "note-heads", ro_heads);
379 vector<Grob*> heads (ro_heads);
380 vector_sort (heads, compare_position);
381 Direction dir = get_grob_direction (me);
386 Real thick = thickness (me);
388 Grob *hed = support_head (me);
391 programming_error ("Stem dir must be up or down.");
393 set_grob_direction (me, dir);
396 Real w = hed->extent (hed, X_AXIS)[dir];
397 for (vsize i = 0; i < heads.size (); i++)
398 heads[i]->translate_axis (w - heads[i]->extent (heads[i], X_AXIS)[dir],
402 Real lastpos = Real (Staff_symbol_referencer::get_position (heads[0]));
403 for (vsize i = 1; i < heads.size (); i++)
405 Real p = Staff_symbol_referencer::get_position (heads[i]);
406 Real dy = fabs (lastpos- p);
409 dy should always be 0.5, 0.0, 1.0, but provide safety margin
416 Real ell = heads[i]->extent (heads[i], X_AXIS).length ();
418 Direction d = get_grob_direction (me);
420 Reversed head should be shifted ell-thickness, but this
421 looks too crowded, so we only shift ell-0.5*thickness.
423 This leads to assymetry: Normal heads overlap the
424 stem 100% whereas reversed heads only overlaps the
428 Real reverse_overlap = 0.5;
429 heads[i]->translate_axis ((ell - thick * reverse_overlap) * d,
432 if (is_invisible (me))
433 heads[i]->translate_axis (-thick * (2 - reverse_overlap) * d,
438 For some cases we should kern some more: when the
439 distance between the next or prev note is too large, we'd
440 get large white gaps, eg.
461 MAKE_SCHEME_CALLBACK(Stem, calc_direction, 1);
463 Stem::calc_direction (SCM smob)
465 Grob *me = unsmob_grob (smob);
466 Direction dir = CENTER;
467 if (Grob *beam = unsmob_grob (me->get_object ("beam")))
469 SCM ignore_me = beam->get_property ("direction");
471 dir = get_grob_direction (me);
475 SCM dd = me->get_property ("default-direction");
478 return me->get_property ("neutral-direction");
481 return scm_from_int (dir);
484 MAKE_SCHEME_CALLBACK(Stem, calc_default_direction, 1);
486 Stem::calc_default_direction (SCM smob)
488 Grob *me = unsmob_grob (smob);
490 Direction dir = CENTER;
491 int staff_center = 0;
492 Interval hp = head_positions (me);
495 int udistance = (int) (UP * hp[UP] - staff_center);
496 int ddistance = (int) (DOWN * hp[DOWN] - staff_center);
498 dir = Direction (sign (ddistance - udistance));
501 return scm_from_int (dir);
505 MAKE_SCHEME_CALLBACK (Stem, height, 1);
507 Stem::height (SCM smob)
509 Grob *me = unsmob_grob (smob);
511 Direction dir = get_grob_direction (me);
515 UGH. Should be automatic
517 Grob *beam = get_beam (me);
520 /* trigger set-stem-lengths. */
521 beam->get_property ("quantized-positions");
525 Can't get_stencil(), since that would cache stencils too early.
526 This causes problems with beams.
528 Stencil *stencil = unsmob_stencil (print (smob));
529 Interval iv = stencil ? stencil->extent (Y_AXIS) : Interval();
534 programming_error ("no stem direction");
537 iv[dir] += dir * Beam::get_thickness (beam) * 0.5;
540 return ly_interval2scm (iv);
544 Stem::stem_end_position (Grob *me)
546 return robust_scm2double (me->get_property ("stem-end-position"), 0);
550 Stem::flag (Grob *me)
552 int log = duration_log (me);
554 || unsmob_grob (me->get_object ("beam")))
558 TODO: maybe property stroke-style should take different values,
559 e.g. "" (i.e. no stroke), "single" and "double" (currently, it's
563 SCM flag_style_scm = me->get_property ("flag-style");
564 if (scm_is_symbol (flag_style_scm))
565 flag_style = ly_symbol2string (flag_style_scm);
567 if (flag_style == "no-flag")
572 string staffline_offs;
573 if (flag_style == "mensural")
574 /* Mensural notation: For notes on staff lines, use different
575 flags than for notes between staff lines. The idea is that
576 flags are always vertically aligned with the staff lines,
577 regardless if the note head is on a staff line or between two
578 staff lines. In other words, the inner end of a flag always
579 touches a staff line.
584 int p = (int) (rint (stem_end_position (me)));
586 = Staff_symbol_referencer::on_line (me, p) ? "0" : "1";
589 staffline_offs = "2";
594 char dir = (get_grob_direction (me) == UP) ? 'u' : 'd';
595 string font_char = flag_style
596 + to_string (dir) + staffline_offs + to_string (log);
597 Font_metric *fm = Font_interface::get_default_font (me);
598 Stencil flag = fm->find_by_name ("flags." + font_char);
599 if (flag.is_empty ())
600 me->warning (_f ("flag `%s' not found", font_char));
602 SCM stroke_style_scm = me->get_property ("stroke-style");
603 if (scm_is_string (stroke_style_scm))
605 string stroke_style = ly_scm2string (stroke_style_scm);
606 if (!stroke_style.empty ())
608 string font_char = to_string (dir) + stroke_style;
609 Stencil stroke = fm->find_by_name ("flags." + font_char);
610 if (stroke.is_empty ())
611 me->warning (_f ("flag stroke `%s' not found", font_char));
613 flag.add_stencil (stroke);
620 MAKE_SCHEME_CALLBACK (Stem, width, 1);
624 Grob *me = unsmob_grob (e);
628 if (is_invisible (me))
630 else if (unsmob_grob (me->get_object ("beam"))
631 || abs (duration_log (me)) <= 2)
633 r = Interval (-1, 1);
634 r *= thickness (me) / 2;
638 r = Interval (-1, 1) * thickness (me) * 0.5;
639 r.unite (flag (me).extent (X_AXIS));
641 return ly_interval2scm (r);
645 Stem::thickness (Grob *me)
647 return scm_to_double (me->get_property ("thickness"))
648 * Staff_symbol_referencer::line_thickness (me);
651 MAKE_SCHEME_CALLBACK (Stem, print, 1);
653 Stem::print (SCM smob)
655 Grob *me = unsmob_grob (smob);
657 Direction d = get_grob_direction (me);
659 Real stemlet_length = robust_scm2double (me->get_property ("stemlet-length"),
661 bool stemlet = stemlet_length > 0.0;
663 /* TODO: make the stem start a direction ?
664 This is required to avoid stems passing in tablature chords. */
666 = to_boolean (me->get_property ("avoid-note-head"))
669 Grob *beam = get_beam (me);
674 if (!lh && stemlet && !beam)
677 if (is_invisible (me))
680 Real y2 = robust_scm2double (me->get_property ("stem-end-position"), 0.0);
682 Real half_space = Staff_symbol_referencer::staff_space (me) * 0.5;
685 y2 = Staff_symbol_referencer::get_position (lh);
688 Real beam_translation = Beam::get_beam_translation (beam);
689 Real beam_thickness = Beam::get_thickness (beam);
690 int beam_count = beam_multiplicity (me).length () + 1;
693 * (0.5 * beam_thickness
694 + beam_translation * max (0, (beam_count - 1))
695 + stemlet_length) / half_space;
698 Interval stem_y (min (y1, y2), max (y2, y1));
700 if (Grob *head = support_head (me))
703 must not take ledgers into account.
705 Interval head_height = head->extent (head, Y_AXIS);
706 Real y_attach = Note_head::stem_attachment_coordinate (head, Y_AXIS);
708 y_attach = head_height.linear_combination (y_attach);
709 stem_y[Direction (-d)] += d * y_attach / half_space;
713 Real stem_width = thickness (me);
715 = me->layout ()->get_dimension (ly_symbol2scm ("blot-diameter"));
717 Box b = Box (Interval (-stem_width / 2, stem_width / 2),
718 Interval (stem_y[DOWN] * half_space, stem_y[UP] * half_space));
720 Stencil ss = Lookup::round_filled_box (b, blot);
721 mol.add_stencil (ss);
723 mol.add_stencil (get_translated_flag (me));
725 return mol.smobbed_copy ();
729 Stem::get_translated_flag (Grob *me)
731 Stencil fl = flag (me);
734 Direction d = get_grob_direction (me);
736 = me->layout ()->get_dimension (ly_symbol2scm ("blot-diameter"));
737 Real stem_width = thickness (me);
738 Real half_space = Staff_symbol_referencer::staff_space (me) * 0.5;
739 Real y2 = robust_scm2double (me->get_property ("stem-end-position"), 0.0);
740 fl.translate_axis (y2 * half_space - d * blot / 2, Y_AXIS);
741 fl.translate_axis (stem_width / 2, X_AXIS);
748 move the stem to right of the notehead if it is up.
750 MAKE_SCHEME_CALLBACK (Stem, offset_callback, 1);
752 Stem::offset_callback (SCM smob)
754 Grob *me = unsmob_grob (smob);
757 if (Grob *f = first_head (me))
759 Interval head_wid = f->extent (f, X_AXIS);
762 if (is_invisible (me))
765 attach = Note_head::stem_attachment_coordinate (f, X_AXIS);
767 Direction d = get_grob_direction (me);
768 Real real_attach = head_wid.linear_combination (d * attach);
771 /* If not centered: correct for stem thickness. */
774 Real rule_thick = thickness (me);
775 r += -d * rule_thick * 0.5;
780 extract_grob_set (me, "rests", rests);
783 Grob *rest = rests.back ();
784 r = rest->extent (rest, X_AXIS).center ();
787 return scm_from_double (r);
791 Stem::get_beam (Grob *me)
793 SCM b = me->get_object ("beam");
794 return dynamic_cast<Spanner *> (unsmob_grob (b));
798 Stem::get_stem_info (Grob *me)
801 si.dir_ = get_grob_direction (me);
803 SCM scm_info = me->get_property ("stem-info");
804 si.ideal_y_ = scm_to_double (scm_car (scm_info));
805 si.shortest_y_ = scm_to_double (scm_cadr (scm_info));
809 MAKE_SCHEME_CALLBACK(Stem, calc_stem_info, 1);
811 Stem::calc_stem_info (SCM smob)
813 Grob *me = unsmob_grob (smob);
814 Direction my_dir = get_grob_direction (me);
818 programming_error ("no stem dir set");
822 Real staff_space = Staff_symbol_referencer::staff_space (me);
823 Grob *beam = get_beam (me);
827 (void) beam->get_property ("beaming");
830 Real beam_translation = Beam::get_beam_translation (beam);
831 Real beam_thickness = Beam::get_thickness (beam);
832 int beam_count = Beam::get_direction_beam_count (beam, my_dir);
834 = robust_scm2double (me->get_property ("length-fraction"), 1.0);
836 /* Simple standard stem length */
837 SCM details = me->get_property ("details");
838 SCM lengths = scm_cdr (scm_assq (ly_symbol2scm ("beamed-lengths"), details));
841 = scm_to_double (robust_list_ref (beam_count - 1, lengths))
845 /* stem only extends to center of beam
847 - 0.5 * beam_thickness;
849 /* Condition: sane minimum free stem length (chord to beams) */
850 lengths = scm_cdr (scm_assq (ly_symbol2scm ("beamed-minimum-free-lengths"), details));
852 Real ideal_minimum_free
853 = scm_to_double (robust_list_ref (beam_count - 1, lengths))
857 Real height_of_my_trem = 0.0;
858 Grob *trem = unsmob_grob (me->get_object ("tremolo-flag"));
860 height_of_my_trem = trem->extent (trem, Y_AXIS).length ()
861 /* hack a bit of space around the trem. */
865 It seems that also for ideal minimum length, we must use
866 the maximum beam count (for this direction):
868 \score{ \notes\relative c''{ [a8 a32] }}
870 must be horizontal. */
871 Real height_of_my_beams = beam_thickness
872 + (beam_count - 1) * beam_translation;
874 Real ideal_minimum_length = ideal_minimum_free
877 /* stem only extends to center of beam */
878 - 0.5 * beam_thickness;
880 ideal_length = max (ideal_length, ideal_minimum_length);
882 /* Convert to Y position, calculate for dir == UP */
884 = /* staff positions */
885 head_positions (me)[my_dir] * 0.5
886 * my_dir * staff_space;
887 Real ideal_y = note_start + ideal_length;
889 /* Conditions for Y position */
891 /* Lowest beam of (UP) beam must never be lower than second staffline
895 Although this (additional) rule is probably correct,
896 I expect that highest beam (UP) should also never be lower
897 than middle staffline, just as normal stems.
901 Obviously not for grace beams.
903 Also, not for knees. Seems to be a good thing. */
904 bool no_extend_b = to_boolean (me->get_property ("no-stem-extend"));
905 bool is_knee = to_boolean (beam->get_property ("knee"));
906 if (!no_extend_b && !is_knee)
908 /* Highest beam of (UP) beam must never be lower than middle
910 ideal_y = max (ideal_y, 0.0);
911 /* Lowest beam of (UP) beam must never be lower than second staffline */
912 ideal_y = max (ideal_y, (-staff_space
913 - beam_thickness + height_of_my_beams));
916 ideal_y -= robust_scm2double (beam->get_property ("shorten"), 0);
918 SCM bemfl = scm_cdr (scm_assq (ly_symbol2scm ("beamed-extreme-minimum-free-lengths"),
922 = scm_to_double (robust_list_ref (beam_count - 1, bemfl))
926 Real minimum_length = max (minimum_free, height_of_my_trem)
928 /* stem only extends to center of beam */
929 - 0.5 * beam_thickness;
932 Real minimum_y = note_start + minimum_length;
933 Real shortest_y = minimum_y * my_dir;
935 return scm_list_2 (scm_from_double (ideal_y),
936 scm_from_double (shortest_y));
940 Stem::beam_multiplicity (Grob *stem)
942 SCM beaming = stem->get_property ("beaming");
943 Slice le = int_list_to_slice (scm_car (beaming));
944 Slice ri = int_list_to_slice (scm_cdr (beaming));
949 /* FIXME: Too many properties */
950 ADD_INTERFACE (Stem, "stem-interface",
951 "The stem represent the graphical stem. "
952 "In addition, it internally connects note heads, beams and"
954 "Rests and whole notes have invisible stems."
956 "\n\nThe following properties may be set in the details list."
958 "@item beamed-lengths \n"
959 "list of stem lengths given beam multiplicity. \n"
960 "@item beamed-minimum-free-lengths \n"
961 "list of normal minimum free stem lengths (chord to beams) given beam multiplicity.\n"
962 "@item beamed-extreme-minimum-free-lengths\n"
963 "list of extreme minimum free stem lengths (chord to beams) given beam multiplicity.\n"
965 "Default stem lengths. The list gives a length for each flag-count.\n"
966 "@item stem-shorten\n"
967 "How much a stem in a forced direction "
968 "should be shortened. The list gives an amount depending on the number "
999 /****************************************************************/
1001 Stem_info::Stem_info ()
1003 ideal_y_ = shortest_y_ = 0;
1008 Stem_info::scale (Real x)