2 stem.cc -- implement Stem
4 source file of the GNU LilyPond music typesetter
6 (c) 1996--2003 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"
26 #include "molecule.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_grob_property ("beaming");
43 if (!gh_pair_p (pair))
45 pair = gh_cons (SCM_EOL, SCM_EOL);
46 me->set_grob_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_grob_property ("stem-end-position");
88 pos = get_default_stem_end_position (me);
89 me->set_grob_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_grob_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_grob_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_grob_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 Stem::note_head_positions (Grob *me)
222 for (SCM s = me->get_grob_property ("note-heads"); gh_pair_p (s); s = ly_cdr (s))
224 Grob * n = unsmob_grob (ly_car (s));
225 int p = int (Staff_symbol_referencer::get_position (n));
236 Stem::add_head (Grob*me, Grob *n)
238 n->set_grob_property ("stem", me->self_scm ());
239 n->add_dependency (me);
242 TODO: why not store Rest pointers?
244 if (Note_head::has_interface (n))
246 Pointer_group_interface::add_grob (me, ly_symbol2scm ("note-heads"), n);
251 Stem::invisible_b (Grob*me)
253 return ! (head_count (me)
254 && gh_scm2int (me->get_grob_property ("duration-log")) >= 1);
258 Stem::get_default_dir (Grob*me)
260 int staff_center = 0;
261 Interval hp = head_positions (me);
267 int udistance = (int) (UP * hp[UP] - staff_center);
268 int ddistance = (int) (DOWN* hp[DOWN] - staff_center);
270 if (sign (ddistance - udistance))
271 return Direction (sign (ddistance -udistance));
273 return to_dir (me->get_grob_property ("neutral-direction"));
277 Stem::get_default_stem_end_position (Grob*me)
279 Real ss = Staff_symbol_referencer::staff_space (me);
281 int durlog = duration_log (me);
287 Real length = 7; // WARNING: IN HALF SPACES
288 SCM scm_len = me->get_grob_property ("length");
289 if (gh_number_p (scm_len))
291 length = gh_scm2double (scm_len);
295 s = me->get_grob_property ("lengths");
298 length = 2* gh_scm2double (robust_list_ref (durlog -2, s));
305 'set-default-stemlen' sets direction too
307 Direction dir = get_direction (me);
310 dir = get_default_dir (me);
311 set_grob_direction (me, dir);
315 /* stems in unnatural (forced) direction should be shortened,
316 according to [Roush & Gourlay] */
317 if (!chord_start_y (me)
318 || (get_direction (me) != get_default_dir (me)))
323 SCM sshorten = me->get_grob_property ("stem-shorten");
324 SCM scm_shorten = gh_pair_p (sshorten) ?
325 robust_list_ref ((duration_log (me) - 2) >? 0, sshorten): SCM_EOL;
326 if (gh_number_p (scm_shorten))
328 shorten = 2* gh_scm2double (scm_shorten);
331 /* On boundary: shorten only half */
332 if (abs (head_positions (me)[get_direction (me)]) <= 1)
341 Grob * trem = unsmob_grob (me->get_grob_property ("tremolo-flag"));
342 if (trem && !unsmob_grob (me->get_grob_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_molecule() looks at the beam.)
354 1.0 + 2 * Stem_tremolo::raw_molecule (trem).extent (Y_AXIS).length () / ss;
358 Interval flag_ext = flag (me).extent (Y_AXIS) ;
359 if (!flag_ext.empty_b())
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_grob_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_grob_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 = gh_scm2double (me->get_grob_property ("thickness"))
458 * me->get_paper ()->get_realvar (ly_symbol2scm ("linethickness"));
460 Grob *hed = support_head (me);
461 Real w = Note_head::head_extent (hed,X_AXIS)[dir];
462 for (int i=0; i < heads.size (); i++)
464 heads[i]->translate_axis (w - Note_head::head_extent (heads[i],X_AXIS)[dir],
469 int lastpos = int (Staff_symbol_referencer::get_position (heads[0]));
470 for (int i=1; i < heads.size (); i ++)
472 Real p = Staff_symbol_referencer::get_position (heads[i]);
473 int dy =abs (lastpos- (int)p);
479 Real l = Note_head::head_extent (heads[i], X_AXIS).length ();
481 Direction d = get_direction (me);
482 /* reversed head should be shifted l-thickness, but this looks
483 too crowded, so we only shift l-0.5*thickness.
484 Notice that this leads to assymetry: Normal heads overlap
485 the stem 100% whereas reversed heads only overlaps the stem
488 heads[i]->translate_axis ((l-thick*magic) * d, X_AXIS);
491 heads[i]->translate_axis (-thick*(2-magic) * d , X_AXIS);
496 For some cases we should kern some more: when the
497 distance between the next or prev note is too large, we'd
498 get large white gaps, eg.
517 MAKE_SCHEME_CALLBACK (Stem,before_line_breaking,1);
519 Stem::before_line_breaking (SCM smob)
521 Grob*me = unsmob_grob (smob);
525 Do the calculations for visible stems, but also for invisible stems
526 with note heads (i.e. half notes.)
530 stem_end_position (me); // ugh. Trigger direction calc.
531 position_noteheads (me);
535 me->set_grob_property ("molecule-callback", SCM_EOL);
538 return SCM_UNSPECIFIED;
543 When in a beam with tuplet brackets, brew_mol is called early,
544 caching a wrong value.
546 MAKE_SCHEME_CALLBACK (Stem, height, 2);
548 Stem::height (SCM smob, SCM ax)
550 Axis a = (Axis)gh_scm2int (ax);
551 Grob * me = unsmob_grob (smob);
552 assert (a == Y_AXIS);
554 SCM mol = me->get_uncached_molecule ();
557 iv = unsmob_molecule (mol)->extent (a);
558 if (Grob *b =get_beam (me))
560 Direction d = get_direction (me);
561 iv[d] += d * Beam::get_thickness (b) /2.0 ;
564 return ly_interval2scm (iv);
571 /* TODO: maybe property stroke-style should take different values,
572 e.g. "" (i.e. no stroke), "single" and "double" (currently, it's
576 SCM flag_style_scm = me->get_grob_property ("flag-style");
577 if (gh_symbol_p (flag_style_scm))
579 flag_style = ly_symbol2string (flag_style_scm);
582 if (flag_style == "no-flag")
587 bool adjust = to_boolean (me->get_grob_property ("adjust-if-on-staffline"));
589 String staffline_offs;
590 if (String::compare (flag_style, "mensural") == 0)
591 /* Mensural notation: For notes on staff lines, use different
592 flags than for notes between staff lines. The idea is that
593 flags are always vertically aligned with the staff lines,
594 regardless if the note head is on a staff line or between two
595 staff lines. In other words, the inner end of a flag always
596 touches a staff line.
601 /* Urrgh! We have to detect wether this stem ends on a staff
602 line or between two staff lines. But we can not call
603 stem_end_position(me) or get_default_stem_end_position(me),
604 since this encounters the flag and hence results in an
605 infinite recursion. However, in pure mensural notation,
606 there are no multiple note heads attached to a single stem,
607 neither is there usually need for using the stem_shorten
608 property (except for 32th and 64th notes, but that is not a
609 problem since the stem length in this case is augmented by
610 an integral multiple of staff_space). Hence, it should be
611 sufficient to just take the first note head, assume it's
612 the only one, look if it's on a staff line, and select the
613 flag's shape accordingly. In the worst case, the shape
614 looks slightly misplaced, but that will usually be the
615 programmer's fault (e.g. when trying to attach multiple
616 note heads to a single stem in mensural notation).
620 perhaps the detection whether this correction is needed should
621 happen in a different place to avoid the recursion.
625 int p = (int)rint (Staff_symbol_referencer::get_position (first_head (me)));
626 staffline_offs = Staff_symbol_referencer::on_staffline (me, p) ?
631 staffline_offs = "2";
639 char dir = (get_direction (me) == UP) ? 'u' : 'd';
641 flag_style + to_string (dir) + staffline_offs + to_string (duration_log (me));
642 Font_metric *fm = Font_interface::get_default_font (me);
643 Molecule flag = fm->find_by_name ("flags-" + font_char);
646 me->warning (_f ("flag `%s' not found", font_char));
649 SCM stroke_style_scm = me->get_grob_property ("stroke-style");
650 if (gh_string_p (stroke_style_scm))
652 String stroke_style = ly_scm2string (stroke_style_scm);
653 if (!stroke_style.empty_b ())
655 String font_char = to_string (dir) + stroke_style;
656 Molecule stroke = fm->find_by_name ("flags-" + font_char);
657 if (stroke.empty_b ())
659 me->warning (_f ("flag stroke `%s' not found", font_char));
663 flag.add_molecule (stroke);
671 MAKE_SCHEME_CALLBACK (Stem,dim_callback,2);
673 Stem::dim_callback (SCM e, SCM ax)
675 Axis a = (Axis) gh_scm2int (ax);
676 assert (a == X_AXIS);
677 Grob *me = unsmob_grob (e);
679 if (unsmob_grob (me->get_grob_property ("beam")) || abs (duration_log (me)) <= 2)
683 r = flag (me).extent (X_AXIS)
685 gh_scm2double (me->get_grob_property ("thickness"))
686 * me->get_paper ()->get_realvar (ly_symbol2scm ("linethickness"))/2;
688 return ly_interval2scm (r);
693 MAKE_SCHEME_CALLBACK (Stem,brew_molecule,1);
696 Stem::brew_molecule (SCM smob)
698 Grob*me = unsmob_grob (smob);
700 Direction d = get_direction (me);
705 TODO: make the stem start a direction ?
707 This is required to avoid stems passing in tablature chords...
709 Grob *lh = to_boolean (me->get_grob_property ("avoid-note-head"))
710 ? last_head (me) : lh = first_head (me);
715 if (invisible_b (me))
720 Real y1 = Staff_symbol_referencer::get_position (lh);
721 Real y2 = stem_end_position (me);
723 Interval stem_y (y1 <? y2,y2 >? y1);
727 Real dy = Staff_symbol_referencer::staff_space (me) * 0.5;
729 if (Grob *hed = support_head (me))
732 must not take ledgers into account.
734 Interval head_height = Note_head::head_extent (hed,Y_AXIS);
735 Real y_attach = Note_head::stem_attachment_coordinate ( hed, Y_AXIS);
737 y_attach = head_height.linear_combination (y_attach);
738 stem_y[Direction (-d)] += d * y_attach/dy;
743 Real stem_width = gh_scm2double (me->get_grob_property ("thickness"))
744 * me->get_paper ()->get_realvar (ly_symbol2scm ("linethickness"));
746 me->get_paper ()->get_realvar (ly_symbol2scm ("blotdiameter"));
748 Box b = Box (Interval (-stem_width/2, stem_width/2),
749 Interval (stem_y[DOWN]*dy, stem_y[UP]*dy));
751 Molecule ss = Lookup::round_filled_box (b, blot);
752 mol.add_molecule (ss);
754 if (!get_beam (me) && abs (duration_log (me)) > 2)
756 Molecule fl = flag (me);
757 fl.translate_axis (stem_y[d]*dy - d * blot/2, Y_AXIS);
758 fl.translate_axis (stem_width/2, X_AXIS);
759 mol.add_molecule (fl);
762 return mol.smobbed_copy ();
766 move the stem to right of the notehead if it is up.
768 MAKE_SCHEME_CALLBACK (Stem,off_callback,2);
770 Stem::off_callback (SCM element_smob, SCM)
772 Grob *me = unsmob_grob (element_smob);
776 if (head_count (me) == 0)
778 return gh_double2scm (0.0);
781 if (Grob * f = first_head (me))
783 Interval head_wid = Note_head::head_extent(f, X_AXIS);
788 if (invisible_b (me))
793 attach = Note_head::stem_attachment_coordinate(f, X_AXIS);
795 Direction d = get_direction (me);
797 Real real_attach = head_wid.linear_combination (d * attach);
802 If not centered: correct for stem thickness.
807 = gh_scm2double (me->get_grob_property ("thickness"))
808 * me->get_paper ()->get_realvar (ly_symbol2scm ("linethickness"));
811 r += - d * rule_thick * 0.5;
814 return gh_double2scm (r);
818 Stem::get_beam (Grob*me)
820 SCM b= me->get_grob_property ("beam");
821 return unsmob_grob (b);
825 Stem::get_stem_info (Grob *me)
827 /* Return cached info if available */
828 SCM scm_info = me->get_grob_property ("stem-info");
829 if (!gh_pair_p (scm_info))
832 scm_info = me->get_grob_property ("stem-info");
836 si.dir_ = get_grob_direction (me);
837 si.ideal_y_ = gh_scm2double (gh_car (scm_info));
838 si.shortest_y_ = gh_scm2double (gh_cadr (scm_info));
844 TODO: add extra space for tremolos!
847 Stem::calc_stem_info (Grob *me)
849 Direction my_dir = get_grob_direction (me);
850 Real staff_space = Staff_symbol_referencer::staff_space (me);
851 Grob *beam = get_beam (me);
852 Real beam_translation = Beam::get_beam_translation (beam);
853 Real beam_thickness = gh_scm2double (beam->get_grob_property ("thickness"));
854 int beam_count = Beam::get_direction_beam_count (beam, my_dir);
857 /* Simple standard stem length */
858 SCM lengths = me->get_grob_property ("beamed-lengths");
860 gh_scm2double (robust_list_ref (beam_count - 1,lengths))
863 /* stem only extends to center of beam */
864 - 0.5 * beam_thickness;
866 /* Condition: sane minimum free stem length (chord to beams) */
867 lengths = me->get_grob_property ("beamed-minimum-free-lengths");
868 Real ideal_minimum_free =
869 gh_scm2double (robust_list_ref (beam_count - 1, lengths))
874 It seems that also for ideal minimum length, we must use
875 the maximum beam count (for this direction):
877 \score{ \notes\relative c''{ [a8 a32] }}
879 must be horizontal. */
880 Real height_of_my_beams = beam_thickness
881 + (beam_count - 1) * beam_translation;
883 Real ideal_minimum_length = ideal_minimum_free
885 /* stem only extends to center of beam */
886 - 0.5 * beam_thickness;
888 ideal_length = ideal_length >? ideal_minimum_length;
891 /* Convert to Y position, calculate for dir == UP */
893 /* staff positions */
894 head_positions (me)[my_dir] * 0.5
896 Real ideal_y = note_start + ideal_length;
899 /* Conditions for Y position */
901 /* Lowest beam of (UP) beam must never be lower than second staffline
905 Although this (additional) rule is probably correct,
906 I expect that highest beam (UP) should also never be lower
907 than middle staffline, just as normal stems.
911 Obviously not for grace beams.
913 Also, not for knees. Seems to be a good thing. */
914 bool no_extend_b = to_boolean (me->get_grob_property ("no-stem-extend"));
915 bool knee_b = to_boolean (beam->get_grob_property ("knee"));
916 if (!no_extend_b && !knee_b)
918 /* Highest beam of (UP) beam must never be lower than middle
920 ideal_y = ideal_y >? 0;
921 /* Lowest beam of (UP) beam must never be lower than second staffline */
922 ideal_y = ideal_y >? (-staff_space
923 - beam_thickness + height_of_my_beams);
927 SCM shorten = beam->get_grob_property ("shorten");
928 if (gh_number_p (shorten))
929 ideal_y -= gh_scm2double (shorten);
932 gh_scm2double (robust_list_ref
934 me->get_grob_property
935 ("beamed-extreme-minimum-free-lengths")))
938 Real minimum_length = minimum_free
940 /* stem only extends to center of beam */
941 - 0.5 * beam_thickness;
943 Real minimum_y = note_start + minimum_length;
947 Real shortest_y = minimum_y * my_dir;
949 me->set_grob_property ("stem-info",
950 scm_list_n (gh_double2scm (ideal_y),
951 gh_double2scm (shortest_y),
956 Stem::beam_multiplicity (Grob *stem)
958 SCM beaming= stem->get_grob_property ("beaming");
959 Slice l = int_list_to_slice (gh_car (beaming));
960 Slice r = int_list_to_slice (gh_cdr (beaming));
968 these are too many props.
970 ADD_INTERFACE (Stem,"stem-interface",
972 "tremolo-flag french-beaming "
973 "avoid-note-head adjust-if-on-staffline thickness "
974 "stem-info beamed-lengths beamed-minimum-free-lengths "
975 "beamed-extreme-minimum-free-lengths lengths beam stem-shorten "
976 "duration-log beaming neutral-direction stem-end-position "
977 "note-heads direction length flag-style "
978 "no-stem-extend stroke-style");