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);
51 for (int i = 0; i < beam_count; i++)
52 lst = scm_cons (scm_from_int (i), lst);
53 index_set_cell (pair, d, lst);
57 Stem::get_beaming (Grob *me, Direction d)
59 SCM pair = me->get_property ("beaming");
60 if (!scm_is_pair (pair))
63 SCM lst = index_get_cell (pair, d);
64 return scm_ilength (lst);
68 Stem::head_positions (Grob *me)
72 Drul_array<Grob *> e (extremal_heads (me));
73 return Interval (Staff_symbol_referencer::get_position (e[DOWN]),
74 Staff_symbol_referencer::get_position (e[UP]));
80 Stem::chord_start_y (Grob *me)
82 Interval hp = head_positions (me);
84 return hp[get_grob_direction (me)] * Staff_symbol_referencer::staff_space (me)
92 Stem::set_stemend (Grob *me, Real se)
95 Direction d = get_grob_direction (me);
97 if (d && d * head_positions (me)[get_grob_direction (me)] >= se * d)
98 me->warning (_ ("weird stem size, check for narrow beams"));
100 me->set_property ("stem-end-position", scm_from_double (se));
103 /* Note head that determines hshift for upstems
104 WARNING: triggers direction */
106 Stem::support_head (Grob *me)
108 extract_grob_set (me, "note-heads", heads);
109 if (heads.size () == 1)
112 return first_head (me);
116 Stem::head_count (Grob *me)
118 return Pointer_group_interface::count (me, ly_symbol2scm ("note-heads"));
121 /* The note head which forms one end of the stem.
122 WARNING: triggers direction */
124 Stem::first_head (Grob *me)
126 Direction d = get_grob_direction (me);
128 return extremal_heads (me)[-d];
132 /* The note head opposite to the first head. */
134 Stem::last_head (Grob *me)
136 Direction d = get_grob_direction (me);
138 return extremal_heads (me)[d];
143 START is part where stem reaches `last' head.
145 This function returns a drul with (bottom-head, top-head).
148 Stem::extremal_heads (Grob *me)
150 const int inf = 1000000;
151 Drul_array<int> extpos;
155 Drul_array<Grob *> exthead (0, 0);
156 extract_grob_set (me, "note-heads", heads);
158 for (vsize i = heads.size (); i--;)
161 int p = Staff_symbol_referencer::get_rounded_position (n);
166 if (d * p > d * extpos[d])
172 while (flip (&d) != DOWN);
178 integer_compare (int const &a, int const &b)
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, integer_compare);
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, calc_stem_end_position, 1)
226 Stem::calc_stem_end_position (SCM smob)
228 Grob *me = unsmob_grob (smob);
230 if (!head_count (me))
231 return scm_from_double (0.0);
234 Real ss = Staff_symbol_referencer::staff_space (me);
235 int durlog = duration_log (me);
238 /* WARNING: IN HALF SPACES */
239 Real length = robust_scm2double (me->get_property ("length"), 7);
241 Direction dir = get_grob_direction (me);
242 Interval hp = head_positions (me);
243 Real stem_end = dir ? hp[dir] + dir * length : 0;
245 /* TODO: change name to extend-stems to staff/center/'() */
246 bool no_extend_b = to_boolean (me->get_property ("no-stem-extend"));
247 if (!no_extend_b && dir * stem_end < 0)
251 /* Make a little room if we have a upflag and there is a dot.
252 previous approach was to lengthen the stem. This is not
253 good typesetting practice. */
254 if (!get_beam (me) && dir == UP
257 Grob *closest_to_flag = extremal_heads (me)[dir];
258 Grob *dots = closest_to_flag
259 ? Rhythmic_head::get_dots (closest_to_flag) : 0;
263 Real dp = Staff_symbol_referencer::get_position (dots);
264 Interval flag_yext = flag (me).extent (Y_AXIS) * (2 / ss) + stem_end;
266 /* Very gory: add myself to the X-support of the parent,
267 which should be a dot-column. */
269 if (flag_yext.distance (dp) < 0.5)
271 Grob *par = dots->get_parent (X_AXIS);
273 if (Dot_column::has_interface (par))
275 Side_position_interface::add_support (par, me);
277 /* TODO: apply some better logic here. The flag is
278 curved inwards, so this will typically be too
285 return scm_from_double (stem_end);
289 MAKE_SCHEME_CALLBACK (Stem, calc_length, 1)
291 Stem::calc_length (SCM smob)
293 Grob *me = unsmob_grob (smob);
295 SCM details = me->get_property ("details");
296 int durlog = duration_log (me);
298 Real ss = Staff_symbol_referencer::staff_space (me);
300 SCM s = scm_cdr (scm_assq (ly_symbol2scm ("lengths"), details));
302 length = 2 * scm_to_double (robust_list_ref (durlog - 2, s));
304 Direction dir = get_grob_direction (me);
306 /* Stems in unnatural (forced) direction should be shortened,
307 according to [Roush & Gourlay] */
308 Interval hp = head_positions (me);
309 if (dir && dir * hp[dir] >= 0)
311 SCM sshorten = scm_cdr (scm_assq (ly_symbol2scm ("stem-shorten"), details));
312 SCM scm_shorten = scm_is_pair (sshorten)
313 ? robust_list_ref (max (duration_log (me) - 2, 0), sshorten) : SCM_EOL;
314 Real shorten = 2* robust_scm2double (scm_shorten, 0);
316 /* On boundary: shorten only half */
317 if (abs (head_positions (me)[dir]) <= 1)
323 length *= robust_scm2double (me->get_property ("length-fraction"), 1.0);
326 Grob *t_flag = unsmob_grob (me->get_object ("tremolo-flag"));
327 if (t_flag && !unsmob_grob (me->get_object ("beam")))
329 /* Crude hack: add extra space if tremolo flag is there.
331 We can't do this for the beam, since we get into a loop
332 (Stem_tremolo::raw_stencil () looks at the beam.) --hwn */
335 + 2 * t_flag->extent (t_flag, Y_AXIS).length ()
338 /* We don't want to add the whole extent of the flag because the trem
339 and the flag can overlap partly. beam_translation gives a good
343 Real beam_trans = Stem_tremolo::get_beam_translation (t_flag);
344 /* the obvious choice is (durlog - 2) here, but we need a bit more space. */
345 minlen += 2 * (durlog - 1.5) * beam_trans;
347 /* up-stems need even a little more space to avoid collisions. This
348 needs to be in sync with the tremolo positioning code in
349 Stem_tremolo::print */
351 minlen += beam_trans;
353 length = max (length, minlen + 1.0);
356 return scm_from_double (length);
358 /* The log of the duration (Number of hooks on the flag minus two) */
360 Stem::duration_log (Grob *me)
362 SCM s = me->get_property ("duration-log");
363 return (scm_is_number (s)) ? scm_to_int (s) : 2;
366 MAKE_SCHEME_CALLBACK(Stem, calc_positioning_done, 1);
368 Stem::calc_positioning_done (SCM smob)
370 Grob *me = unsmob_grob (smob);
371 if (!head_count (me))
374 extract_grob_set (me, "note-heads", ro_heads);
375 vector<Grob*> heads (ro_heads);
376 vector_sort (heads, compare_position);
377 Direction dir = get_grob_direction (me);
382 Real thick = thickness (me);
384 Grob *hed = support_head (me);
387 programming_error ("Stem dir must be up or down.");
389 set_grob_direction (me, dir);
392 Real w = hed->extent (hed, X_AXIS)[dir];
393 for (vsize i = 0; i < heads.size (); i++)
394 heads[i]->translate_axis (w - heads[i]->extent (heads[i], X_AXIS)[dir],
398 Real lastpos = Real (Staff_symbol_referencer::get_position (heads[0]));
399 for (vsize i = 1; i < heads.size (); i++)
401 Real p = Staff_symbol_referencer::get_position (heads[i]);
402 Real dy = fabs (lastpos- p);
405 dy should always be 0.5, 0.0, 1.0, but provide safety margin
412 Real ell = heads[i]->extent (heads[i], X_AXIS).length ();
414 Direction d = get_grob_direction (me);
416 Reversed head should be shifted ell-thickness, but this
417 looks too crowded, so we only shift ell-0.5*thickness.
419 This leads to assymetry: Normal heads overlap the
420 stem 100% whereas reversed heads only overlaps the
424 Real reverse_overlap = 0.5;
425 heads[i]->translate_axis ((ell - thick * reverse_overlap) * d,
428 if (is_invisible (me))
429 heads[i]->translate_axis (-thick * (2 - reverse_overlap) * d,
434 For some cases we should kern some more: when the
435 distance between the next or prev note is too large, we'd
436 get large white gaps, eg.
457 MAKE_SCHEME_CALLBACK(Stem, calc_direction, 1);
459 Stem::calc_direction (SCM smob)
461 Grob *me = unsmob_grob (smob);
462 Direction dir = CENTER;
463 if (Grob *beam = unsmob_grob (me->get_object ("beam")))
465 SCM ignore_me = beam->get_property ("direction");
467 dir = get_grob_direction (me);
471 SCM dd = me->get_property ("default-direction");
474 return me->get_property ("neutral-direction");
477 return scm_from_int (dir);
480 MAKE_SCHEME_CALLBACK(Stem, calc_default_direction, 1);
482 Stem::calc_default_direction (SCM smob)
484 Grob *me = unsmob_grob (smob);
486 Direction dir = CENTER;
487 int staff_center = 0;
488 Interval hp = head_positions (me);
491 int udistance = (int) (UP * hp[UP] - staff_center);
492 int ddistance = (int) (DOWN * hp[DOWN] - staff_center);
494 dir = Direction (sign (ddistance - udistance));
497 return scm_from_int (dir);
501 MAKE_SCHEME_CALLBACK (Stem, height, 1);
503 Stem::height (SCM smob)
505 Grob *me = unsmob_grob (smob);
507 Direction dir = get_grob_direction (me);
511 UGH. Should be automatic
513 Grob *beam = get_beam (me);
516 /* trigger set-stem-lengths. */
517 beam->get_property ("quantized-positions");
521 Can't get_stencil(), since that would cache stencils too early.
522 This causes problems with beams.
524 Stencil *stencil = unsmob_stencil (print (smob));
525 Interval iv = stencil ? stencil->extent (Y_AXIS) : Interval();
530 programming_error ("no stem direction");
533 iv[dir] += dir * Beam::get_thickness (beam) * 0.5;
536 return ly_interval2scm (iv);
540 Stem::stem_end_position (Grob *me)
542 return robust_scm2double (me->get_property ("stem-end-position"), 0);
546 Stem::flag (Grob *me)
548 int log = duration_log (me);
550 || unsmob_grob (me->get_object ("beam")))
554 TODO: maybe property stroke-style should take different values,
555 e.g. "" (i.e. no stroke), "single" and "double" (currently, it's
559 SCM flag_style_scm = me->get_property ("flag-style");
560 if (scm_is_symbol (flag_style_scm))
561 flag_style = ly_symbol2string (flag_style_scm);
563 if (flag_style == "no-flag")
568 string staffline_offs;
569 if (flag_style == "mensural")
570 /* Mensural notation: For notes on staff lines, use different
571 flags than for notes between staff lines. The idea is that
572 flags are always vertically aligned with the staff lines,
573 regardless if the note head is on a staff line or between two
574 staff lines. In other words, the inner end of a flag always
575 touches a staff line.
580 int p = (int) (rint (stem_end_position (me)));
582 = Staff_symbol_referencer::on_line (me, p) ? "0" : "1";
585 staffline_offs = "2";
590 char dir = (get_grob_direction (me) == UP) ? 'u' : 'd';
591 string font_char = flag_style
592 + to_string (dir) + staffline_offs + to_string (log);
593 Font_metric *fm = Font_interface::get_default_font (me);
594 Stencil flag = fm->find_by_name ("flags." + font_char);
595 if (flag.is_empty ())
596 me->warning (_f ("flag `%s' not found", font_char));
598 SCM stroke_style_scm = me->get_property ("stroke-style");
599 if (scm_is_string (stroke_style_scm))
601 string stroke_style = ly_scm2string (stroke_style_scm);
602 if (!stroke_style.empty ())
604 string font_char = to_string (dir) + stroke_style;
605 Stencil stroke = fm->find_by_name ("flags." + font_char);
606 if (stroke.is_empty ())
607 me->warning (_f ("flag stroke `%s' not found", font_char));
609 flag.add_stencil (stroke);
616 MAKE_SCHEME_CALLBACK (Stem, width, 1);
620 Grob *me = unsmob_grob (e);
624 if (is_invisible (me))
626 else if (unsmob_grob (me->get_object ("beam"))
627 || abs (duration_log (me)) <= 2)
629 r = Interval (-1, 1);
630 r *= thickness (me) / 2;
634 r = Interval (-1, 1) * thickness (me) * 0.5;
635 r.unite (flag (me).extent (X_AXIS));
637 return ly_interval2scm (r);
641 Stem::thickness (Grob *me)
643 return scm_to_double (me->get_property ("thickness"))
644 * Staff_symbol_referencer::line_thickness (me);
647 MAKE_SCHEME_CALLBACK (Stem, print, 1);
649 Stem::print (SCM smob)
651 Grob *me = unsmob_grob (smob);
653 Direction d = get_grob_direction (me);
655 Real stemlet_length = robust_scm2double (me->get_property ("stemlet-length"),
657 bool stemlet = stemlet_length > 0.0;
659 /* TODO: make the stem start a direction ?
660 This is required to avoid stems passing in tablature chords. */
662 = to_boolean (me->get_property ("avoid-note-head"))
665 Grob *beam = get_beam (me);
670 if (!lh && stemlet && !beam)
673 if (is_invisible (me))
676 Real y2 = robust_scm2double (me->get_property ("stem-end-position"), 0.0);
678 Real half_space = Staff_symbol_referencer::staff_space (me) * 0.5;
681 y2 = Staff_symbol_referencer::get_position (lh);
684 Real beam_translation = Beam::get_beam_translation (beam);
685 Real beam_thickness = Beam::get_thickness (beam);
686 int beam_count = beam_multiplicity (me).length () + 1;
689 * (0.5 * beam_thickness
690 + beam_translation * max (0, (beam_count - 1))
691 + stemlet_length) / half_space;
694 Interval stem_y (min (y1, y2), max (y2, y1));
696 if (Grob *head = support_head (me))
699 must not take ledgers into account.
701 Interval head_height = head->extent (head, Y_AXIS);
702 Real y_attach = Note_head::stem_attachment_coordinate (head, Y_AXIS);
704 y_attach = head_height.linear_combination (y_attach);
705 stem_y[Direction (-d)] += d * y_attach / half_space;
709 Real stem_width = thickness (me);
711 = me->layout ()->get_dimension (ly_symbol2scm ("blot-diameter"));
713 Box b = Box (Interval (-stem_width / 2, stem_width / 2),
714 Interval (stem_y[DOWN] * half_space, stem_y[UP] * half_space));
716 Stencil ss = Lookup::round_filled_box (b, blot);
717 mol.add_stencil (ss);
719 mol.add_stencil (get_translated_flag (me));
721 return mol.smobbed_copy ();
725 Stem::get_translated_flag (Grob *me)
727 Stencil fl = flag (me);
730 Direction d = get_grob_direction (me);
732 = me->layout ()->get_dimension (ly_symbol2scm ("blot-diameter"));
733 Real stem_width = thickness (me);
734 Real half_space = Staff_symbol_referencer::staff_space (me) * 0.5;
735 Real y2 = robust_scm2double (me->get_property ("stem-end-position"), 0.0);
736 fl.translate_axis (y2 * half_space - d * blot / 2, Y_AXIS);
737 fl.translate_axis (stem_width / 2, X_AXIS);
744 move the stem to right of the notehead if it is up.
746 MAKE_SCHEME_CALLBACK (Stem, offset_callback, 1);
748 Stem::offset_callback (SCM smob)
750 Grob *me = unsmob_grob (smob);
753 if (Grob *f = first_head (me))
755 Interval head_wid = f->extent (f, X_AXIS);
758 if (is_invisible (me))
761 attach = Note_head::stem_attachment_coordinate (f, X_AXIS);
763 Direction d = get_grob_direction (me);
764 Real real_attach = head_wid.linear_combination (d * attach);
767 /* If not centered: correct for stem thickness. */
770 Real rule_thick = thickness (me);
771 r += -d * rule_thick * 0.5;
776 extract_grob_set (me, "rests", rests);
779 Grob *rest = rests.back ();
780 r = rest->extent (rest, X_AXIS).center ();
783 return scm_from_double (r);
787 Stem::get_beam (Grob *me)
789 SCM b = me->get_object ("beam");
790 return dynamic_cast<Spanner *> (unsmob_grob (b));
794 Stem::get_stem_info (Grob *me)
797 si.dir_ = get_grob_direction (me);
799 SCM scm_info = me->get_property ("stem-info");
800 si.ideal_y_ = scm_to_double (scm_car (scm_info));
801 si.shortest_y_ = scm_to_double (scm_cadr (scm_info));
805 MAKE_SCHEME_CALLBACK(Stem, calc_stem_info, 1);
807 Stem::calc_stem_info (SCM smob)
809 Grob *me = unsmob_grob (smob);
810 Direction my_dir = get_grob_direction (me);
814 programming_error ("no stem dir set");
818 Real staff_space = Staff_symbol_referencer::staff_space (me);
819 Grob *beam = get_beam (me);
823 (void) beam->get_property ("beaming");
826 Real beam_translation = Beam::get_beam_translation (beam);
827 Real beam_thickness = Beam::get_thickness (beam);
828 int beam_count = Beam::get_direction_beam_count (beam, my_dir);
830 = robust_scm2double (me->get_property ("length-fraction"), 1.0);
832 /* Simple standard stem length */
833 SCM details = me->get_property ("details");
834 SCM lengths = scm_cdr (scm_assq (ly_symbol2scm ("beamed-lengths"), details));
837 = scm_to_double (robust_list_ref (beam_count - 1, lengths))
841 /* stem only extends to center of beam
843 - 0.5 * beam_thickness;
845 /* Condition: sane minimum free stem length (chord to beams) */
846 lengths = scm_cdr (scm_assq (ly_symbol2scm ("beamed-minimum-free-lengths"), details));
848 Real ideal_minimum_free
849 = scm_to_double (robust_list_ref (beam_count - 1, lengths))
853 Real height_of_my_trem = 0.0;
854 Grob *trem = unsmob_grob (me->get_object ("tremolo-flag"));
856 height_of_my_trem = trem->extent (trem, Y_AXIS).length ()
857 /* hack a bit of space around the trem. */
861 It seems that also for ideal minimum length, we must use
862 the maximum beam count (for this direction):
864 \score{ \notes\relative c''{ [a8 a32] }}
866 must be horizontal. */
867 Real height_of_my_beams = beam_thickness
868 + (beam_count - 1) * beam_translation;
870 Real ideal_minimum_length = ideal_minimum_free
873 /* stem only extends to center of beam */
874 - 0.5 * beam_thickness;
876 ideal_length = max (ideal_length, ideal_minimum_length);
878 /* Convert to Y position, calculate for dir == UP */
880 = /* staff positions */
881 head_positions (me)[my_dir] * 0.5
882 * my_dir * staff_space;
883 Real ideal_y = note_start + ideal_length;
885 /* Conditions for Y position */
887 /* Lowest beam of (UP) beam must never be lower than second staffline
891 Although this (additional) rule is probably correct,
892 I expect that highest beam (UP) should also never be lower
893 than middle staffline, just as normal stems.
897 Obviously not for grace beams.
899 Also, not for knees. Seems to be a good thing. */
900 bool no_extend_b = to_boolean (me->get_property ("no-stem-extend"));
901 bool is_knee = to_boolean (beam->get_property ("knee"));
902 if (!no_extend_b && !is_knee)
904 /* Highest beam of (UP) beam must never be lower than middle
906 ideal_y = max (ideal_y, 0.0);
907 /* Lowest beam of (UP) beam must never be lower than second staffline */
908 ideal_y = max (ideal_y, (-staff_space
909 - beam_thickness + height_of_my_beams));
912 ideal_y -= robust_scm2double (beam->get_property ("shorten"), 0);
914 SCM bemfl = scm_cdr (scm_assq (ly_symbol2scm ("beamed-extreme-minimum-free-lengths"),
918 = scm_to_double (robust_list_ref (beam_count - 1, bemfl))
922 Real minimum_length = max (minimum_free, height_of_my_trem)
924 /* stem only extends to center of beam */
925 - 0.5 * beam_thickness;
928 Real minimum_y = note_start + minimum_length;
929 Real shortest_y = minimum_y * my_dir;
931 return scm_list_2 (scm_from_double (ideal_y),
932 scm_from_double (shortest_y));
936 Stem::beam_multiplicity (Grob *stem)
938 SCM beaming = stem->get_property ("beaming");
939 Slice le = int_list_to_slice (scm_car (beaming));
940 Slice ri = int_list_to_slice (scm_cdr (beaming));
945 /* FIXME: Too many properties */
946 ADD_INTERFACE (Stem, "stem-interface",
947 "The stem represent the graphical stem. "
948 "In addition, it internally connects note heads, beams and"
950 "Rests and whole notes have invisible stems."
952 "\n\nThe following properties may be set in the details list."
954 "@item beamed-lengths \n"
955 "list of stem lengths given beam multiplicity. \n"
956 "@item beamed-minimum-free-lengths \n"
957 "list of normal minimum free stem lengths (chord to beams) given beam multiplicity.\n"
958 "@item beamed-extreme-minimum-free-lengths\n"
959 "list of extreme minimum free stem lengths (chord to beams) given beam multiplicity.\n"
961 "Default stem lengths. The list gives a length for each flag-count.\n"
962 "@item stem-shorten\n"
963 "How much a stem in a forced direction "
964 "should be shortened. The list gives an amount depending on the number "
994 /****************************************************************/
996 Stem_info::Stem_info ()
998 ideal_y_ = shortest_y_ = 0;
1003 Stem_info::scale (Real x)