2 stem.cc -- implement Stem
4 source file of the GNU LilyPond music typesetter
6 (c) 1996--2004 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7 Jan Nieuwenhuizen <janneke@gnu.org>
9 TODO: This is way too hairy
13 Stem-end, chord-start, etc. is all confusing naming.
16 #include <math.h> // rint
19 #include "directional-element-interface.hh"
20 #include "note-head.hh"
23 #include "paper-def.hh"
24 #include "rhythmic-head.hh"
25 #include "font-interface.hh"
27 #include "paper-column.hh"
31 #include "group-interface.hh"
32 #include "staff-symbol-referencer.hh"
34 #include "side-position-interface.hh"
35 #include "dot-column.hh"
36 #include "stem-tremolo.hh"
39 Stem::set_beaming (Grob*me, int beam_count, Direction d)
41 SCM pair = me->get_property ("beaming");
43 if (!gh_pair_p (pair))
45 pair = gh_cons (SCM_EOL, SCM_EOL);
46 me->set_property ("beaming", pair);
49 SCM l = index_get_cell (pair, d);
50 for( int i = 0; i< beam_count; i++)
52 l = gh_cons (gh_int2scm (i), l);
54 index_set_cell (pair, d, l);
59 Stem::head_positions (Grob*me)
67 Drul_array<Grob*> e (extremal_heads (me));
69 return Interval (Staff_symbol_referencer::get_position (e[DOWN]),
70 Staff_symbol_referencer::get_position (e[UP]));
75 Stem::chord_start_y (Grob*me)
77 return head_positions (me)[get_direction (me)]
78 * Staff_symbol_referencer::staff_space (me)/2.0;
82 Stem::stem_end_position (Grob*me)
84 SCM p =me->get_property ("stem-end-position");
88 pos = get_default_stem_end_position (me);
89 me->set_property ("stem-end-position", gh_double2scm (pos));
92 pos = gh_scm2double (p);
98 Stem::get_direction (Grob*me)
100 Direction d = get_grob_direction (me);
104 d = get_default_dir (me);
106 set_grob_direction (me, d);
113 Stem::set_stemend (Grob*me, Real se)
116 Direction d= get_direction (me);
118 if (d && d * head_positions (me)[get_direction (me)] >= se*d)
119 me->warning (_ ("Weird stem size; check for narrow beams"));
121 me->set_property ("stem-end-position", gh_double2scm (se));
126 Note head that determines hshift for upstems
128 WARNING: triggers direction
131 Stem::support_head (Grob*me)
133 if (head_count (me) == 1)
139 return unsmob_grob (ly_car (me->get_property ("note-heads")));
142 return first_head (me);
147 Stem::head_count (Grob*me)
149 return Pointer_group_interface::count (me, "note-heads");
153 The note head which forms one end of the stem.
155 WARNING: triggers direction
158 Stem::first_head (Grob*me)
160 Direction d = get_direction (me);
163 return extremal_heads (me)[-d];
167 The note head opposite to the first head.
170 Stem::last_head (Grob*me)
172 Direction d = get_direction (me);
175 return extremal_heads (me)[d];
179 START is part where stem reaches `last' head.
182 Stem::extremal_heads (Grob*me)
184 const int inf = 1000000;
185 Drul_array<int> extpos;
189 Drul_array<Grob *> exthead;
190 exthead[LEFT] = exthead[RIGHT] =0;
192 for (SCM s = me->get_property ("note-heads"); gh_pair_p (s); s = ly_cdr (s))
194 Grob * n = unsmob_grob (ly_car (s));
197 int p = int (Staff_symbol_referencer::get_position (n));
201 if (d* p > d* extpos[d])
206 } while (flip (&d) != DOWN);
213 icmp (int const &a, int const &b)
219 The positions, in ascending order.
222 Stem::note_head_positions (Grob *me)
225 for (SCM s = me->get_property ("note-heads"); gh_pair_p (s); s = ly_cdr (s))
227 Grob * n = unsmob_grob (ly_car (s));
228 int p = int (Staff_symbol_referencer::get_position (n));
239 Stem::add_head (Grob*me, Grob *n)
241 n->set_property ("stem", me->self_scm ());
242 n->add_dependency (me);
245 TODO: why not store Rest pointers?
247 if (Note_head::has_interface (n))
249 Pointer_group_interface::add_grob (me, ly_symbol2scm ("note-heads"), n);
254 Stem::is_invisible (Grob*me)
256 return ! (head_count (me)
257 && gh_scm2int (me->get_property ("duration-log")) >= 1);
261 Stem::get_default_dir (Grob*me)
263 int staff_center = 0;
264 Interval hp = head_positions (me);
270 int udistance = (int) (UP * hp[UP] - staff_center);
271 int ddistance = (int) (DOWN* hp[DOWN] - staff_center);
273 if (sign (ddistance - udistance))
274 return Direction (sign (ddistance -udistance));
276 return to_dir (me->get_property ("neutral-direction"));
280 Stem::get_default_stem_end_position (Grob*me)
282 Real ss = Staff_symbol_referencer::staff_space (me);
284 int durlog = duration_log (me);
290 Real length = 7; // WARNING: IN HALF SPACES
291 SCM scm_len = me->get_property ("length");
292 if (gh_number_p (scm_len))
294 length = gh_scm2double (scm_len);
298 s = me->get_property ("lengths");
301 length = 2* gh_scm2double (robust_list_ref (durlog -2, s));
308 'set-default-stemlen' sets direction too
310 Direction dir = get_direction (me);
313 dir = get_default_dir (me);
314 set_grob_direction (me, dir);
318 /* stems in unnatural (forced) direction should be shortened,
319 according to [Roush & Gourlay] */
320 if (!chord_start_y (me)
321 || (get_direction (me) != get_default_dir (me)))
325 SCM sshorten = me->get_property ("stem-shorten");
326 SCM scm_shorten = gh_pair_p (sshorten) ?
327 robust_list_ref ((duration_log (me) - 2) >? 0, sshorten): SCM_EOL;
328 Real shorten = 2* robust_scm2double (scm_shorten,0);
331 /* On boundary: shorten only half */
332 if (abs (head_positions (me)[get_direction (me)]) <= 1)
341 Grob * trem = unsmob_grob (me->get_property ("tremolo-flag"));
342 if (trem && !unsmob_grob (me->get_property ("beam")))
345 Crude hack: add extra space if tremolo flag is there.
347 We can't do this for the beam, since we get into a loop
348 (Stem_tremolo::raw_stencil() looks at the beam.)
354 1.0 + 2 * Stem_tremolo::raw_stencil (trem).extent (Y_AXIS).length () / ss;
358 Interval flag_ext = flag (me).extent (Y_AXIS) ;
359 if (!flag_ext.is_empty ())
360 minlen += 2 * flag_ext.length () / ss ;
363 The clash is smaller for down stems (since the tremolo is
370 length = length >? (minlen + 1.0);
373 Interval hp = head_positions (me);
374 Real st = hp[dir] + dir * length;
377 TODO: change name to extend-stems to staff/center/'()
379 bool no_extend_b = to_boolean (me->get_property ("no-stem-extend"));
380 if (!no_extend_b && dir * st < 0) // junkme?
384 Make a little room if we have a upflag and there is a dot.
385 previous approach was to lengthen the stem. This is not
386 good typesetting practice.
389 if (!get_beam (me) && dir == UP
392 Grob * closest_to_flag = extremal_heads (me)[dir];
393 Grob * dots = closest_to_flag
394 ? Rhythmic_head::get_dots (closest_to_flag ) : 0;
398 Real dp = Staff_symbol_referencer::get_position (dots);
399 Real flagy = flag (me).extent (Y_AXIS)[-dir] * 2
403 Very gory: add myself to the X-support of the parent,
404 which should be a dot-column.
406 if (dir * (st + flagy - dp) < 0.5)
408 Grob *par = dots->get_parent (X_AXIS);
410 if (Dot_column::has_interface (par))
412 Side_position_interface::add_support (par, me);
415 TODO: apply some better logic here. The flag is
416 curved inwards, so this will typically be too
432 the log of the duration (Number of hooks on the flag minus two)
435 Stem::duration_log (Grob*me)
437 SCM s = me->get_property ("duration-log");
438 return (gh_number_p (s)) ? gh_scm2int (s) : 2;
442 Stem::position_noteheads (Grob*me)
444 if (!head_count (me))
447 Link_array<Grob> heads =
448 Pointer_group_interface__extract_grobs (me, (Grob*)0, "note-heads");
450 heads.sort (compare_position);
451 Direction dir =get_direction (me);
457 Real thick = thickness (me);
459 Grob *hed = support_head (me);
460 Real w = Note_head::head_extent (hed,X_AXIS)[dir];
461 for (int i=0; i < heads.size (); i++)
463 heads[i]->translate_axis (w - Note_head::head_extent (heads[i],X_AXIS)[dir],
468 Real lastpos = Real (Staff_symbol_referencer::get_position (heads[0]));
469 for (int i=1; i < heads.size (); i ++)
471 Real p = Staff_symbol_referencer::get_position (heads[i]);
472 Real dy =fabs (lastpos- p);
475 dy should always be 0.5, 0.0, 1.0, but provide safety margin
482 Real l = Note_head::head_extent (heads[i], X_AXIS).length ();
484 Direction d = get_direction (me);
486 Reversed head should be shifted l-thickness, but this
487 looks too crowded, so we only shift l-0.5*thickness.
489 This leads to assymetry: Normal heads overlap the
490 stem 100% whereas reversed heads only overlaps the
495 Real reverse_overlap =0.5;
496 heads[i]->translate_axis ((l-thick*reverse_overlap) * d, X_AXIS);
498 if (is_invisible(me))
499 heads[i]->translate_axis (-thick*(2 - reverse_overlap) * d , X_AXIS);
504 For some cases we should kern some more: when the
505 distance between the next or prev note is too large, we'd
506 get large white gaps, eg.
525 MAKE_SCHEME_CALLBACK (Stem,before_line_breaking,1);
527 Stem::before_line_breaking (SCM smob)
529 Grob*me = unsmob_grob (smob);
533 Do the calculations for visible stems, but also for invisible stems
534 with note heads (i.e. half notes.)
538 stem_end_position (me); // ugh. Trigger direction calc.
539 position_noteheads (me);
543 me->set_property ("print-function", SCM_EOL);
546 return SCM_UNSPECIFIED;
551 When in a beam with tuplet brackets, brew_mol is called early,
552 caching a wrong value.
554 MAKE_SCHEME_CALLBACK (Stem, height, 2);
556 Stem::height (SCM smob, SCM ax)
558 Axis a = (Axis)gh_scm2int (ax);
559 Grob * me = unsmob_grob (smob);
560 assert (a == Y_AXIS);
562 SCM mol = me->get_uncached_stencil ();
565 iv = unsmob_stencil (mol)->extent (a);
566 if (Grob *b =get_beam (me))
568 Direction d = get_direction (me);
569 iv[d] += d * Beam::get_thickness (b) /2.0 ;
572 return ly_interval2scm (iv);
579 /* TODO: maybe property stroke-style should take different values,
580 e.g. "" (i.e. no stroke), "single" and "double" (currently, it's
584 SCM flag_style_scm = me->get_property ("flag-style");
585 if (gh_symbol_p (flag_style_scm))
587 flag_style = ly_symbol2string (flag_style_scm);
590 if (flag_style == "no-flag")
595 bool adjust = to_boolean (me->get_property ("adjust-if-on-staffline"));
597 String staffline_offs;
598 if (String::compare (flag_style, "mensural") == 0)
599 /* Mensural notation: For notes on staff lines, use different
600 flags than for notes between staff lines. The idea is that
601 flags are always vertically aligned with the staff lines,
602 regardless if the note head is on a staff line or between two
603 staff lines. In other words, the inner end of a flag always
604 touches a staff line.
609 /* Urrgh! We have to detect wether this stem ends on a staff
610 line or between two staff lines. But we can not call
611 stem_end_position(me) or get_default_stem_end_position(me),
612 since this encounters the flag and hence results in an
613 infinite recursion. However, in pure mensural notation,
614 there are no multiple note heads attached to a single stem,
615 neither is there usually need for using the stem_shorten
616 property (except for 32th and 64th notes, but that is not a
617 problem since the stem length in this case is augmented by
618 an integral multiple of staff_space). Hence, it should be
619 sufficient to just take the first note head, assume it's
620 the only one, look if it's on a staff line, and select the
621 flag's shape accordingly. In the worst case, the shape
622 looks slightly misplaced, but that will usually be the
623 programmer's fault (e.g. when trying to attach multiple
624 note heads to a single stem in mensural notation).
628 perhaps the detection whether this correction is needed should
629 happen in a different place to avoid the recursion.
633 int p = (int)rint (Staff_symbol_referencer::get_position (first_head (me)));
634 staffline_offs = Staff_symbol_referencer::on_staffline (me, p) ?
639 staffline_offs = "2";
647 char dir = (get_direction (me) == UP) ? 'u' : 'd';
649 flag_style + to_string (dir) + staffline_offs + to_string (duration_log (me));
650 Font_metric *fm = Font_interface::get_default_font (me);
651 Stencil flag = fm->find_by_name ("flags-" + font_char);
652 if (flag.is_empty ())
654 me->warning (_f ("flag `%s' not found", font_char));
657 SCM stroke_style_scm = me->get_property ("stroke-style");
658 if (gh_string_p (stroke_style_scm))
660 String stroke_style = ly_scm2string (stroke_style_scm);
661 if (!stroke_style.is_empty ())
663 String font_char = to_string (dir) + stroke_style;
664 Stencil stroke = fm->find_by_name ("flags-" + font_char);
665 if (stroke.is_empty ())
667 me->warning (_f ("flag stroke `%s' not found", font_char));
671 flag.add_stencil (stroke);
679 MAKE_SCHEME_CALLBACK (Stem,dim_callback,2);
681 Stem::dim_callback (SCM e, SCM ax)
683 Axis a = (Axis) gh_scm2int (ax);
684 assert (a == X_AXIS);
685 Grob *me = unsmob_grob (e);
687 if (unsmob_grob (me->get_property ("beam")) || abs (duration_log (me)) <= 2)
691 r = flag (me).extent (X_AXIS)
694 return ly_interval2scm (r);
698 Stem::thickness (Grob* me)
700 return gh_scm2double (me->get_property ("thickness"))
701 * Staff_symbol_referencer::line_thickness (me);
704 MAKE_SCHEME_CALLBACK (Stem,print,1);
707 Stem::print (SCM smob)
709 Grob*me = unsmob_grob (smob);
711 Direction d = get_direction (me);
714 TODO: make the stem start a direction ?
716 This is required to avoid stems passing in tablature chords...
718 Grob *lh = to_boolean (me->get_property ("avoid-note-head"))
719 ? last_head (me) : lh = first_head (me);
724 if (is_invisible (me))
727 Real y1 = Staff_symbol_referencer::get_position (lh);
728 Real y2 = stem_end_position (me);
730 Interval stem_y (y1 <? y2,y2 >? y1);
734 Real dy = Staff_symbol_referencer::staff_space (me) * 0.5;
736 if (Grob *hed = support_head (me))
739 must not take ledgers into account.
741 Interval head_height = Note_head::head_extent (hed,Y_AXIS);
742 Real y_attach = Note_head::stem_attachment_coordinate (hed, Y_AXIS);
744 y_attach = head_height.linear_combination (y_attach);
745 stem_y[Direction (-d)] += d * y_attach/dy;
750 Real stem_width = thickness (me);
752 me->get_paper ()->get_realvar (ly_symbol2scm ("blotdiameter"));
754 Box b = Box (Interval (-stem_width/2, stem_width/2),
755 Interval (stem_y[DOWN]*dy, stem_y[UP]*dy));
757 Stencil ss = Lookup::round_filled_box (b, blot);
758 mol.add_stencil (ss);
760 if (!get_beam (me) && abs (duration_log (me)) > 2)
762 Stencil fl = flag (me);
763 fl.translate_axis (stem_y[d]*dy - d * blot/2, Y_AXIS);
764 fl.translate_axis (stem_width/2, X_AXIS);
765 mol.add_stencil (fl);
768 return mol.smobbed_copy ();
772 move the stem to right of the notehead if it is up.
774 MAKE_SCHEME_CALLBACK (Stem,off_callback,2);
776 Stem::off_callback (SCM element_smob, SCM)
778 Grob *me = unsmob_grob (element_smob);
782 if (head_count (me) == 0)
784 return gh_double2scm (0.0);
787 if (Grob * f = first_head (me))
789 Interval head_wid = Note_head::head_extent(f, X_AXIS);
793 if (is_invisible (me))
798 attach = Note_head::stem_attachment_coordinate(f, X_AXIS);
800 Direction d = get_direction (me);
802 Real real_attach = head_wid.linear_combination (d * attach);
807 If not centered: correct for stem thickness.
814 r += - d * rule_thick * 0.5;
817 return gh_double2scm (r);
822 Stem::get_beam (Grob*me)
824 SCM b= me->get_property ("beam");
825 return unsmob_grob (b);
829 Stem::get_stem_info (Grob *me)
831 /* Return cached info if available */
832 SCM scm_info = me->get_property ("stem-info");
833 if (!gh_pair_p (scm_info))
836 scm_info = me->get_property ("stem-info");
840 si.dir_ = get_grob_direction (me);
841 si.ideal_y_ = gh_scm2double (gh_car (scm_info));
842 si.shortest_y_ = gh_scm2double (gh_cadr (scm_info));
848 TODO: add extra space for tremolos!
851 Stem::calc_stem_info (Grob *me)
853 Direction my_dir = get_grob_direction (me);
857 programming_error ("No stem dir set?");
861 Real staff_space = Staff_symbol_referencer::staff_space (me);
862 Grob *beam = get_beam (me);
863 Real beam_translation = Beam::get_beam_translation (beam);
864 Real beam_thickness = Beam::get_thickness (beam);
865 int beam_count = Beam::get_direction_beam_count (beam, my_dir);
868 /* Simple standard stem length */
869 SCM lengths = me->get_property ("beamed-lengths");
871 gh_scm2double (robust_list_ref (beam_count - 1,lengths))
874 /* stem only extends to center of beam */
875 - 0.5 * beam_thickness;
877 /* Condition: sane minimum free stem length (chord to beams) */
878 lengths = me->get_property ("beamed-minimum-free-lengths");
879 Real ideal_minimum_free =
880 gh_scm2double (robust_list_ref (beam_count - 1, lengths))
885 It seems that also for ideal minimum length, we must use
886 the maximum beam count (for this direction):
888 \score{ \notes\relative c''{ [a8 a32] }}
890 must be horizontal. */
891 Real height_of_my_beams = beam_thickness
892 + (beam_count - 1) * beam_translation;
894 Real ideal_minimum_length = ideal_minimum_free
896 /* stem only extends to center of beam */
897 - 0.5 * beam_thickness;
899 ideal_length = ideal_length >? ideal_minimum_length;
902 /* Convert to Y position, calculate for dir == UP */
904 /* staff positions */
905 head_positions (me)[my_dir] * 0.5
906 * my_dir * staff_space;
907 Real ideal_y = note_start + ideal_length;
910 /* Conditions for Y position */
912 /* Lowest beam of (UP) beam must never be lower than second staffline
916 Although this (additional) rule is probably correct,
917 I expect that highest beam (UP) should also never be lower
918 than middle staffline, just as normal stems.
922 Obviously not for grace beams.
924 Also, not for knees. Seems to be a good thing. */
925 bool no_extend_b = to_boolean (me->get_property ("no-stem-extend"));
926 bool is_knee = to_boolean (beam->get_property ("knee"));
927 if (!no_extend_b && !is_knee)
929 /* Highest beam of (UP) beam must never be lower than middle
931 ideal_y = ideal_y >? 0;
932 /* Lowest beam of (UP) beam must never be lower than second staffline */
933 ideal_y = ideal_y >? (-staff_space
934 - beam_thickness + height_of_my_beams);
938 ideal_y -= robust_scm2double (beam->get_property ("shorten"), 0);
941 gh_scm2double (robust_list_ref
944 ("beamed-extreme-minimum-free-lengths")))
947 Real minimum_length = minimum_free
949 /* stem only extends to center of beam */
950 - 0.5 * beam_thickness;
952 Real minimum_y = note_start + minimum_length;
956 Real shortest_y = minimum_y * my_dir;
958 me->set_property ("stem-info",
959 scm_list_n (gh_double2scm (ideal_y),
960 gh_double2scm (shortest_y),
965 Stem::beam_multiplicity (Grob *stem)
967 SCM beaming= stem->get_property ("beaming");
968 Slice l = int_list_to_slice (gh_car (beaming));
969 Slice r = int_list_to_slice (gh_cdr (beaming));
977 these are too many props.
979 ADD_INTERFACE (Stem,"stem-interface",
980 "The stem represent the graphical stem. "
981 " In addition, it internally connects note heads, beams, tremolos. Rests "
982 " and whole notes have invisible stems."
986 "tremolo-flag french-beaming "
987 "avoid-note-head adjust-if-on-staffline thickness "
988 "stem-info beamed-lengths beamed-minimum-free-lengths "
989 "beamed-extreme-minimum-free-lengths lengths beam stem-shorten "
990 "duration-log beaming neutral-direction stem-end-position "
991 "note-heads direction length flag-style "
992 "no-stem-extend stroke-style");
996 /****************************************************************/
998 Stem_info::Stem_info()
1000 ideal_y_ = shortest_y_ =0;
1005 Stem_info::scale (Real x)