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 Real flagy = flag (me).extent (Y_AXIS)[-dir] * 2 / ss;
266 /* Very gory: add myself to the X-support of the parent,
267 which should be a dot-column. */
268 if (dir * (stem_end + flagy - dp) < 0.5)
270 Grob *par = dots->get_parent (X_AXIS);
272 if (Dot_column::has_interface (par))
274 Side_position_interface::add_support (par, me);
276 /* TODO: apply some better logic here. The flag is
277 curved inwards, so this will typically be too
284 return scm_from_double (stem_end);
288 MAKE_SCHEME_CALLBACK (Stem, calc_length, 1)
290 Stem::calc_length (SCM smob)
292 Grob *me = unsmob_grob (smob);
294 SCM details = me->get_property ("details");
295 int durlog = duration_log (me);
297 Real ss = Staff_symbol_referencer::staff_space (me);
299 SCM s = scm_cdr (scm_assq (ly_symbol2scm ("lengths"), details));
301 length = 2 * scm_to_double (robust_list_ref (durlog - 2, s));
303 Direction dir = get_grob_direction (me);
305 /* Stems in unnatural (forced) direction should be shortened,
306 according to [Roush & Gourlay] */
307 Interval hp = head_positions (me);
308 if (dir && dir * hp[dir] >= 0)
310 SCM sshorten = scm_cdr (scm_assq (ly_symbol2scm ("stem-shorten"), details));
311 SCM scm_shorten = scm_is_pair (sshorten)
312 ? robust_list_ref (max (duration_log (me) - 2, 0), sshorten) : SCM_EOL;
313 Real shorten = 2* robust_scm2double (scm_shorten, 0);
315 /* On boundary: shorten only half */
316 if (abs (head_positions (me)[dir]) <= 1)
322 length *= robust_scm2double (me->get_property ("length-fraction"), 1.0);
325 Grob *t_flag = unsmob_grob (me->get_object ("tremolo-flag"));
326 if (t_flag && !unsmob_grob (me->get_object ("beam")))
328 /* Crude hack: add extra space if tremolo flag is there.
330 We can't do this for the beam, since we get into a loop
331 (Stem_tremolo::raw_stencil () looks at the beam.) --hwn */
334 + 2 * Stem_tremolo::raw_stencil (t_flag).extent (Y_AXIS).length ()
339 Interval flag_ext = flag (me).extent (Y_AXIS);
340 if (!flag_ext.is_empty ())
341 minlen += 2 * flag_ext.length () / ss;
343 /* The clash is smaller for down stems (since the tremolo is
348 length = max (length, minlen + 1.0);
351 return scm_from_double (length);
353 /* The log of the duration (Number of hooks on the flag minus two) */
355 Stem::duration_log (Grob *me)
357 SCM s = me->get_property ("duration-log");
358 return (scm_is_number (s)) ? scm_to_int (s) : 2;
361 MAKE_SCHEME_CALLBACK(Stem, calc_positioning_done, 1);
363 Stem::calc_positioning_done (SCM smob)
365 Grob *me = unsmob_grob (smob);
366 if (!head_count (me))
369 extract_grob_set (me, "note-heads", ro_heads);
370 Link_array__Grob_ heads (ro_heads);
371 vector_sort (heads, compare_position);
372 Direction dir = get_grob_direction (me);
377 Real thick = thickness (me);
379 Grob *hed = support_head (me);
382 programming_error ("Stem dir must be up or down.");
384 set_grob_direction (me, dir);
387 Real w = hed->extent (hed, X_AXIS)[dir];
388 for (vsize i = 0; i < heads.size (); i++)
389 heads[i]->translate_axis (w - heads[i]->extent (heads[i], X_AXIS)[dir],
393 Real lastpos = Real (Staff_symbol_referencer::get_position (heads[0]));
394 for (vsize i = 1; i < heads.size (); i++)
396 Real p = Staff_symbol_referencer::get_position (heads[i]);
397 Real dy = fabs (lastpos- p);
400 dy should always be 0.5, 0.0, 1.0, but provide safety margin
407 Real ell = heads[i]->extent (heads[i], X_AXIS).length ();
409 Direction d = get_grob_direction (me);
411 Reversed head should be shifted ell-thickness, but this
412 looks too crowded, so we only shift ell-0.5*thickness.
414 This leads to assymetry: Normal heads overlap the
415 stem 100% whereas reversed heads only overlaps the
419 Real reverse_overlap = 0.5;
420 heads[i]->translate_axis ((ell - thick * reverse_overlap) * d,
423 if (is_invisible (me))
424 heads[i]->translate_axis (-thick * (2 - reverse_overlap) * d,
429 For some cases we should kern some more: when the
430 distance between the next or prev note is too large, we'd
431 get large white gaps, eg.
452 MAKE_SCHEME_CALLBACK(Stem, calc_direction, 1);
454 Stem::calc_direction (SCM smob)
456 Grob *me = unsmob_grob (smob);
457 Direction dir = CENTER;
458 if (Grob *beam = unsmob_grob (me->get_object ("beam")))
460 SCM ignore_me = beam->get_property ("direction");
462 dir = get_grob_direction (me);
466 SCM dd = me->get_property ("default-direction");
469 return me->get_property ("neutral-direction");
472 return scm_from_int (dir);
475 MAKE_SCHEME_CALLBACK(Stem, calc_default_direction, 1);
477 Stem::calc_default_direction (SCM smob)
479 Grob *me = unsmob_grob (smob);
481 Direction dir = CENTER;
482 int staff_center = 0;
483 Interval hp = head_positions (me);
486 int udistance = (int) (UP * hp[UP] - staff_center);
487 int ddistance = (int) (DOWN * hp[DOWN] - staff_center);
489 dir = Direction (sign (ddistance - udistance));
492 return scm_from_int (dir);
496 MAKE_SCHEME_CALLBACK (Stem, height, 1);
498 Stem::height (SCM smob)
500 Grob *me = unsmob_grob (smob);
502 Direction dir = get_grob_direction (me);
506 UGH. Should be automatic
508 Grob *beam = get_beam (me);
511 beam->get_property ("positions");
515 Can't get_stencil(), since that would cache stencils too early.
516 This causes problems with beams.
518 Stencil *stencil = unsmob_stencil (print (smob));
519 Interval iv = stencil ? stencil->extent (Y_AXIS) : Interval();
524 programming_error ("no stem direction");
527 iv[dir] += dir * Beam::get_thickness (beam) * 0.5;
530 return ly_interval2scm (iv);
534 Stem::stem_end_position (Grob *me)
536 return robust_scm2double (me->get_property ("stem-end-position"), 0);
540 Stem::flag (Grob *me)
542 int log = duration_log (me);
544 || unsmob_grob (me->get_object ("beam")))
548 TODO: maybe property stroke-style should take different values,
549 e.g. "" (i.e. no stroke), "single" and "double" (currently, it's
551 std::string flag_style;
553 SCM flag_style_scm = me->get_property ("flag-style");
554 if (scm_is_symbol (flag_style_scm))
555 flag_style = ly_symbol2string (flag_style_scm);
557 if (flag_style == "no-flag")
562 std::string staffline_offs;
563 if (flag_style == "mensural")
564 /* Mensural notation: For notes on staff lines, use different
565 flags than for notes between staff lines. The idea is that
566 flags are always vertically aligned with the staff lines,
567 regardless if the note head is on a staff line or between two
568 staff lines. In other words, the inner end of a flag always
569 touches a staff line.
574 int p = (int) (rint (stem_end_position (me)));
576 = Staff_symbol_referencer::on_line (me, p) ? "0" : "1";
579 staffline_offs = "2";
584 char dir = (get_grob_direction (me) == UP) ? 'u' : 'd';
585 std::string font_char = flag_style
586 + to_string (dir) + staffline_offs + to_string (log);
587 Font_metric *fm = Font_interface::get_default_font (me);
588 Stencil flag = fm->find_by_name ("flags." + font_char);
589 if (flag.is_empty ())
590 me->warning (_f ("flag `%s' not found", font_char));
592 SCM stroke_style_scm = me->get_property ("stroke-style");
593 if (scm_is_string (stroke_style_scm))
595 std::string stroke_style = ly_scm2string (stroke_style_scm);
596 if (!stroke_style.empty ())
598 std::string font_char = to_string (dir) + stroke_style;
599 Stencil stroke = fm->find_by_name ("flags." + font_char);
600 if (stroke.is_empty ())
601 me->warning (_f ("flag stroke `%s' not found", font_char));
603 flag.add_stencil (stroke);
610 MAKE_SCHEME_CALLBACK (Stem, width, 1);
614 Grob *me = unsmob_grob (e);
618 if (is_invisible (me))
620 else if (unsmob_grob (me->get_object ("beam"))
621 || abs (duration_log (me)) <= 2)
623 r = Interval (-1, 1);
624 r *= thickness (me) / 2;
628 r = Interval (-1, 1) * thickness (me) * 0.5;
629 r.unite (flag (me).extent (X_AXIS));
631 return ly_interval2scm (r);
635 Stem::thickness (Grob *me)
637 return scm_to_double (me->get_property ("thickness"))
638 * Staff_symbol_referencer::line_thickness (me);
641 MAKE_SCHEME_CALLBACK (Stem, print, 1);
643 Stem::print (SCM smob)
645 Grob *me = unsmob_grob (smob);
647 Direction d = get_grob_direction (me);
649 Real stemlet_length = robust_scm2double (me->get_property ("stemlet-length"),
651 bool stemlet = stemlet_length > 0.0;
653 /* TODO: make the stem start a direction ?
654 This is required to avoid stems passing in tablature chords. */
656 = to_boolean (me->get_property ("avoid-note-head"))
659 Grob *beam = get_beam (me);
664 if (!lh && stemlet && !beam)
667 if (is_invisible (me))
670 Real y2 = robust_scm2double (me->get_property ("stem-end-position"), 0.0);
672 Real half_space = Staff_symbol_referencer::staff_space (me) * 0.5;
675 y2 = Staff_symbol_referencer::get_position (lh);
678 Real beam_translation = Beam::get_beam_translation (beam);
679 Real beam_thickness = Beam::get_thickness (beam);
680 int beam_count = beam_multiplicity (me).length () + 1;
683 * (0.5 * beam_thickness
684 + beam_translation * max (0, (beam_count - 1))
685 + stemlet_length) / half_space;
688 Interval stem_y (min (y1, y2), max (y2, y1));
690 if (Grob *head = support_head (me))
693 must not take ledgers into account.
695 Interval head_height = head->extent (head, Y_AXIS);
696 Real y_attach = Note_head::stem_attachment_coordinate (head, Y_AXIS);
698 y_attach = head_height.linear_combination (y_attach);
699 stem_y[Direction (-d)] += d * y_attach / half_space;
703 Real stem_width = thickness (me);
705 = me->layout ()->get_dimension (ly_symbol2scm ("blotdiameter"));
707 Box b = Box (Interval (-stem_width / 2, stem_width / 2),
708 Interval (stem_y[DOWN] * half_space, stem_y[UP] * half_space));
710 Stencil ss = Lookup::round_filled_box (b, blot);
711 mol.add_stencil (ss);
713 mol.add_stencil (get_translated_flag (me));
715 return mol.smobbed_copy ();
719 Stem::get_translated_flag (Grob *me)
721 Stencil fl = flag (me);
724 Direction d = get_grob_direction (me);
726 = me->layout ()->get_dimension (ly_symbol2scm ("blotdiameter"));
727 Real stem_width = thickness (me);
728 Real half_space = Staff_symbol_referencer::staff_space (me) * 0.5;
729 Real y2 = robust_scm2double (me->get_property ("stem-end-position"), 0.0);
730 fl.translate_axis (y2 * half_space - d * blot / 2, Y_AXIS);
731 fl.translate_axis (stem_width / 2, X_AXIS);
738 move the stem to right of the notehead if it is up.
740 MAKE_SCHEME_CALLBACK (Stem, offset_callback, 1);
742 Stem::offset_callback (SCM smob)
744 Grob *me = unsmob_grob (smob);
747 if (Grob *f = first_head (me))
749 Interval head_wid = f->extent (f, X_AXIS);
752 if (is_invisible (me))
755 attach = Note_head::stem_attachment_coordinate (f, X_AXIS);
757 Direction d = get_grob_direction (me);
758 Real real_attach = head_wid.linear_combination (d * attach);
761 /* If not centered: correct for stem thickness. */
764 Real rule_thick = thickness (me);
765 r += -d * rule_thick * 0.5;
770 extract_grob_set (me, "rests", rests);
773 Grob *rest = rests.back ();
774 r = rest->extent (rest, X_AXIS).center ();
777 return scm_from_double (r);
781 Stem::get_beam (Grob *me)
783 SCM b = me->get_object ("beam");
784 return dynamic_cast<Spanner *> (unsmob_grob (b));
788 Stem::get_stem_info (Grob *me)
791 si.dir_ = get_grob_direction (me);
793 SCM scm_info = me->get_property ("stem-info");
794 si.ideal_y_ = scm_to_double (scm_car (scm_info));
795 si.shortest_y_ = scm_to_double (scm_cadr (scm_info));
799 /* TODO: add extra space for tremolos! */
800 MAKE_SCHEME_CALLBACK(Stem, calc_stem_info, 1);
802 Stem::calc_stem_info (SCM smob)
804 Grob *me = unsmob_grob (smob);
805 Direction my_dir = get_grob_direction (me);
809 programming_error ("no stem dir set");
813 Real staff_space = Staff_symbol_referencer::staff_space (me);
814 Grob *beam = get_beam (me);
818 (void) beam->get_property ("beaming");
821 Real beam_translation = Beam::get_beam_translation (beam);
822 Real beam_thickness = Beam::get_thickness (beam);
823 int beam_count = Beam::get_direction_beam_count (beam, my_dir);
825 = robust_scm2double (me->get_property ("length-fraction"), 1.0);
827 /* Simple standard stem length */
828 SCM details = me->get_property ("details");
829 SCM lengths = scm_cdr (scm_assq (ly_symbol2scm ("beamed-lengths"), details));
832 = scm_to_double (robust_list_ref (beam_count - 1, lengths))
836 /* stem only extends to center of beam
838 - 0.5 * beam_thickness;
840 /* Condition: sane minimum free stem length (chord to beams) */
841 lengths = scm_cdr (scm_assq (ly_symbol2scm ("beamed-minimum-free-lengths"), details));
843 Real ideal_minimum_free
844 = scm_to_double (robust_list_ref (beam_count - 1, lengths))
849 It seems that also for ideal minimum length, we must use
850 the maximum beam count (for this direction):
852 \score{ \notes\relative c''{ [a8 a32] }}
854 must be horizontal. */
855 Real height_of_my_beams = beam_thickness
856 + (beam_count - 1) * beam_translation;
858 Real ideal_minimum_length = ideal_minimum_free
860 /* stem only extends to center of beam */
861 - 0.5 * beam_thickness;
863 ideal_length = max (ideal_length, ideal_minimum_length);
865 /* Convert to Y position, calculate for dir == UP */
867 = /* staff positions */
868 head_positions (me)[my_dir] * 0.5
869 * my_dir * staff_space;
870 Real ideal_y = note_start + ideal_length;
872 /* Conditions for Y position */
874 /* Lowest beam of (UP) beam must never be lower than second staffline
878 Although this (additional) rule is probably correct,
879 I expect that highest beam (UP) should also never be lower
880 than middle staffline, just as normal stems.
884 Obviously not for grace beams.
886 Also, not for knees. Seems to be a good thing. */
887 bool no_extend_b = to_boolean (me->get_property ("no-stem-extend"));
888 bool is_knee = to_boolean (beam->get_property ("knee"));
889 if (!no_extend_b && !is_knee)
891 /* Highest beam of (UP) beam must never be lower than middle
893 ideal_y = max (ideal_y, 0.0);
894 /* Lowest beam of (UP) beam must never be lower than second staffline */
895 ideal_y = max (ideal_y, (-staff_space
896 - beam_thickness + height_of_my_beams));
899 ideal_y -= robust_scm2double (beam->get_property ("shorten"), 0);
901 SCM bemfl = scm_cdr (scm_assq (ly_symbol2scm ("beamed-extreme-minimum-free-lengths"),
905 = scm_to_double (robust_list_ref (beam_count - 1, bemfl))
909 Real minimum_length = minimum_free
911 /* stem only extends to center of beam */
912 - 0.5 * beam_thickness;
914 if (Grob *tremolo = unsmob_grob (me->get_object ("tremolo-flag")))
916 Interval y_ext = tremolo->extent (tremolo, Y_AXIS);
917 y_ext.widen (0.5); // FIXME. Should be tunable?
918 minimum_length = max (minimum_length, y_ext.length ());
922 Real minimum_y = note_start + minimum_length;
923 Real shortest_y = minimum_y * my_dir;
925 return scm_list_2 (scm_from_double (ideal_y),
926 scm_from_double (shortest_y));
930 Stem::beam_multiplicity (Grob *stem)
932 SCM beaming = stem->get_property ("beaming");
933 Slice le = int_list_to_slice (scm_car (beaming));
934 Slice ri = int_list_to_slice (scm_cdr (beaming));
939 /* FIXME: Too many properties */
940 ADD_INTERFACE (Stem, "stem-interface",
941 "The stem represent the graphical stem. "
942 "In addition, it internally connects note heads, beams and"
944 "Rests and whole notes have invisible stems."
946 "\n\nThe following properties may be set in the details list."
948 "@item beamed-lengths \n"
949 "list of stem lengths given beam multiplicity. \n"
950 "@item beamed-minimum-free-lengths \n"
951 "list of normal minimum free stem lengths (chord to beams) given beam multiplicity.\n"
952 "@item beamed-extreme-minimum-free-lengths\n"
953 "list of extreme minimum free stem lengths (chord to beams) given beam multiplicity.\n"
955 "Default stem lengths. The list gives a length for each flag-count.\n"
956 "@item stem-shorten\n"
957 "How much a stem in a forced direction "
958 "should be shortened. The list gives an amount depending on the number "
988 /****************************************************************/
990 Stem_info::Stem_info ()
992 ideal_y_ = shortest_y_ = 0;
997 Stem_info::scale (Real x)