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)))
322 SCM sshorten = me->get_grob_property ("stem-shorten");
323 SCM scm_shorten = gh_pair_p (sshorten) ?
324 robust_list_ref ((duration_log (me) - 2) >? 0, sshorten): SCM_EOL;
325 Real shorten = 2* robust_scm2double (scm_shorten,0);
328 /* On boundary: shorten only half */
329 if (abs (head_positions (me)[get_direction (me)]) <= 1)
338 Grob * trem = unsmob_grob (me->get_grob_property ("tremolo-flag"));
339 if (trem && !unsmob_grob (me->get_grob_property ("beam")))
342 Crude hack: add extra space if tremolo flag is there.
344 We can't do this for the beam, since we get into a loop
345 (Stem_tremolo::raw_molecule() looks at the beam.)
351 1.0 + 2 * Stem_tremolo::raw_molecule (trem).extent (Y_AXIS).length () / ss;
355 Interval flag_ext = flag (me).extent (Y_AXIS) ;
356 if (!flag_ext.is_empty ())
357 minlen += 2 * flag_ext.length () / ss ;
360 The clash is smaller for down stems (since the tremolo is
367 length = length >? (minlen + 1.0);
370 Interval hp = head_positions (me);
371 Real st = hp[dir] + dir * length;
374 TODO: change name to extend-stems to staff/center/'()
376 bool no_extend_b = to_boolean (me->get_grob_property ("no-stem-extend"));
377 if (!no_extend_b && dir * st < 0) // junkme?
381 Make a little room if we have a upflag and there is a dot.
382 previous approach was to lengthen the stem. This is not
383 good typesetting practice.
386 if (!get_beam (me) && dir == UP
389 Grob * closest_to_flag = extremal_heads (me)[dir];
390 Grob * dots = closest_to_flag
391 ? Rhythmic_head::get_dots (closest_to_flag ) : 0;
395 Real dp = Staff_symbol_referencer::get_position (dots);
396 Real flagy = flag (me).extent (Y_AXIS)[-dir] * 2
400 Very gory: add myself to the X-support of the parent,
401 which should be a dot-column.
403 if (dir * (st + flagy - dp) < 0.5)
405 Grob *par = dots->get_parent (X_AXIS);
407 if (Dot_column::has_interface (par))
409 Side_position_interface::add_support (par, me);
412 TODO: apply some better logic here. The flag is
413 curved inwards, so this will typically be too
429 the log of the duration (Number of hooks on the flag minus two)
432 Stem::duration_log (Grob*me)
434 SCM s = me->get_grob_property ("duration-log");
435 return (gh_number_p (s)) ? gh_scm2int (s) : 2;
439 Stem::position_noteheads (Grob*me)
441 if (!head_count (me))
444 Link_array<Grob> heads =
445 Pointer_group_interface__extract_grobs (me, (Grob*)0, "note-heads");
447 heads.sort (compare_position);
448 Direction dir =get_direction (me);
454 Real thick = robust_scm2double (me->get_grob_property ("thickness"),1)
455 * me->get_paper ()->get_realvar (ly_symbol2scm ("linethickness"));
457 Grob *hed = support_head (me);
458 Real w = Note_head::head_extent (hed,X_AXIS)[dir];
459 for (int i=0; i < heads.size (); i++)
461 heads[i]->translate_axis (w - Note_head::head_extent (heads[i],X_AXIS)[dir],
466 int lastpos = int (Staff_symbol_referencer::get_position (heads[0]));
467 for (int i=1; i < heads.size (); i ++)
469 Real p = Staff_symbol_referencer::get_position (heads[i]);
470 int dy =abs (lastpos- (int)p);
476 Real l = Note_head::head_extent (heads[i], X_AXIS).length ();
478 Direction d = get_direction (me);
480 Reversed head should be shifted l-thickness, but this
481 looks too crowded, so we only shift l-0.5*thickness.
483 This leads to assymetry: Normal heads overlap the
484 stem 100% whereas reversed heads only overlaps the
489 Real reverse_overlap =0.5;
490 heads[i]->translate_axis ((l-thick*reverse_overlap) * d, X_AXIS);
493 heads[i]->translate_axis (-thick*(2 - reverse_overlap) * d , X_AXIS);
498 For some cases we should kern some more: when the
499 distance between the next or prev note is too large, we'd
500 get large white gaps, eg.
519 MAKE_SCHEME_CALLBACK (Stem,before_line_breaking,1);
521 Stem::before_line_breaking (SCM smob)
523 Grob*me = unsmob_grob (smob);
527 Do the calculations for visible stems, but also for invisible stems
528 with note heads (i.e. half notes.)
532 stem_end_position (me); // ugh. Trigger direction calc.
533 position_noteheads (me);
537 me->set_grob_property ("molecule-callback", SCM_EOL);
540 return SCM_UNSPECIFIED;
545 When in a beam with tuplet brackets, brew_mol is called early,
546 caching a wrong value.
548 MAKE_SCHEME_CALLBACK (Stem, height, 2);
550 Stem::height (SCM smob, SCM ax)
552 Axis a = (Axis)gh_scm2int (ax);
553 Grob * me = unsmob_grob (smob);
554 assert (a == Y_AXIS);
556 SCM mol = me->get_uncached_molecule ();
559 iv = unsmob_molecule (mol)->extent (a);
560 if (Grob *b =get_beam (me))
562 Direction d = get_direction (me);
563 iv[d] += d * Beam::get_thickness (b) /2.0 ;
566 return ly_interval2scm (iv);
573 /* TODO: maybe property stroke-style should take different values,
574 e.g. "" (i.e. no stroke), "single" and "double" (currently, it's
578 SCM flag_style_scm = me->get_grob_property ("flag-style");
579 if (gh_symbol_p (flag_style_scm))
581 flag_style = ly_symbol2string (flag_style_scm);
584 if (flag_style == "no-flag")
589 bool adjust = to_boolean (me->get_grob_property ("adjust-if-on-staffline"));
591 String staffline_offs;
592 if (String::compare (flag_style, "mensural") == 0)
593 /* Mensural notation: For notes on staff lines, use different
594 flags than for notes between staff lines. The idea is that
595 flags are always vertically aligned with the staff lines,
596 regardless if the note head is on a staff line or between two
597 staff lines. In other words, the inner end of a flag always
598 touches a staff line.
603 /* Urrgh! We have to detect wether this stem ends on a staff
604 line or between two staff lines. But we can not call
605 stem_end_position(me) or get_default_stem_end_position(me),
606 since this encounters the flag and hence results in an
607 infinite recursion. However, in pure mensural notation,
608 there are no multiple note heads attached to a single stem,
609 neither is there usually need for using the stem_shorten
610 property (except for 32th and 64th notes, but that is not a
611 problem since the stem length in this case is augmented by
612 an integral multiple of staff_space). Hence, it should be
613 sufficient to just take the first note head, assume it's
614 the only one, look if it's on a staff line, and select the
615 flag's shape accordingly. In the worst case, the shape
616 looks slightly misplaced, but that will usually be the
617 programmer's fault (e.g. when trying to attach multiple
618 note heads to a single stem in mensural notation).
622 perhaps the detection whether this correction is needed should
623 happen in a different place to avoid the recursion.
627 int p = (int)rint (Staff_symbol_referencer::get_position (first_head (me)));
628 staffline_offs = Staff_symbol_referencer::on_staffline (me, p) ?
633 staffline_offs = "2";
641 char dir = (get_direction (me) == UP) ? 'u' : 'd';
643 flag_style + to_string (dir) + staffline_offs + to_string (duration_log (me));
644 Font_metric *fm = Font_interface::get_default_font (me);
645 Molecule flag = fm->find_by_name ("flags-" + font_char);
646 if (flag.is_empty ())
648 me->warning (_f ("flag `%s' not found", font_char));
651 SCM stroke_style_scm = me->get_grob_property ("stroke-style");
652 if (gh_string_p (stroke_style_scm))
654 String stroke_style = ly_scm2string (stroke_style_scm);
655 if (!stroke_style.is_empty ())
657 String font_char = to_string (dir) + stroke_style;
658 Molecule stroke = fm->find_by_name ("flags-" + font_char);
659 if (stroke.is_empty ())
661 me->warning (_f ("flag stroke `%s' not found", font_char));
665 flag.add_molecule (stroke);
673 MAKE_SCHEME_CALLBACK (Stem,dim_callback,2);
675 Stem::dim_callback (SCM e, SCM ax)
677 Axis a = (Axis) gh_scm2int (ax);
678 assert (a == X_AXIS);
679 Grob *me = unsmob_grob (e);
681 if (unsmob_grob (me->get_grob_property ("beam")) || abs (duration_log (me)) <= 2)
685 r = flag (me).extent (X_AXIS)
687 gh_scm2double (me->get_grob_property ("thickness"))
688 * me->get_paper ()->get_realvar (ly_symbol2scm ("linethickness"))/2;
690 return ly_interval2scm (r);
695 MAKE_SCHEME_CALLBACK (Stem,brew_molecule,1);
698 Stem::brew_molecule (SCM smob)
700 Grob*me = unsmob_grob (smob);
702 Direction d = get_direction (me);
707 TODO: make the stem start a direction ?
709 This is required to avoid stems passing in tablature chords...
711 Grob *lh = to_boolean (me->get_grob_property ("avoid-note-head"))
712 ? last_head (me) : lh = first_head (me);
717 if (invisible_b (me))
722 Real y1 = Staff_symbol_referencer::get_position (lh);
723 Real y2 = stem_end_position (me);
725 Interval stem_y (y1 <? y2,y2 >? y1);
729 Real dy = Staff_symbol_referencer::staff_space (me) * 0.5;
731 if (Grob *hed = support_head (me))
734 must not take ledgers into account.
736 Interval head_height = Note_head::head_extent (hed,Y_AXIS);
737 Real y_attach = Note_head::stem_attachment_coordinate (hed, Y_AXIS);
739 y_attach = head_height.linear_combination (y_attach);
740 stem_y[Direction (-d)] += d * y_attach/dy;
745 Real stem_width = robust_scm2double (me->get_grob_property ("thickness"), 1)
746 * me->get_paper ()->get_realvar (ly_symbol2scm ("linethickness"));
748 me->get_paper ()->get_realvar (ly_symbol2scm ("blotdiameter"));
750 Box b = Box (Interval (-stem_width/2, stem_width/2),
751 Interval (stem_y[DOWN]*dy, stem_y[UP]*dy));
753 Molecule ss = Lookup::round_filled_box (b, blot);
754 mol.add_molecule (ss);
756 if (!get_beam (me) && abs (duration_log (me)) > 2)
758 Molecule fl = flag (me);
759 fl.translate_axis (stem_y[d]*dy - d * blot/2, Y_AXIS);
760 fl.translate_axis (stem_width/2, X_AXIS);
761 mol.add_molecule (fl);
764 return mol.smobbed_copy ();
768 move the stem to right of the notehead if it is up.
770 MAKE_SCHEME_CALLBACK (Stem,off_callback,2);
772 Stem::off_callback (SCM element_smob, SCM)
774 Grob *me = unsmob_grob (element_smob);
778 if (head_count (me) == 0)
780 return gh_double2scm (0.0);
783 if (Grob * f = first_head (me))
785 Interval head_wid = Note_head::head_extent(f, X_AXIS);
789 if (invisible_b (me))
794 attach = Note_head::stem_attachment_coordinate(f, X_AXIS);
796 Direction d = get_direction (me);
798 Real real_attach = head_wid.linear_combination (d * attach);
803 If not centered: correct for stem thickness.
808 = robust_scm2double (me->get_grob_property ("thickness"), 1)
809 * me->get_paper ()->get_realvar (ly_symbol2scm ("linethickness"));
811 r += - d * rule_thick * 0.5;
814 return gh_double2scm (r);
819 Stem::get_beam (Grob*me)
821 SCM b= me->get_grob_property ("beam");
822 return unsmob_grob (b);
826 Stem::get_stem_info (Grob *me)
828 /* Return cached info if available */
829 SCM scm_info = me->get_grob_property ("stem-info");
830 if (!gh_pair_p (scm_info))
833 scm_info = me->get_grob_property ("stem-info");
837 si.dir_ = get_grob_direction (me);
838 si.ideal_y_ = gh_scm2double (gh_car (scm_info));
839 si.shortest_y_ = gh_scm2double (gh_cadr (scm_info));
845 TODO: add extra space for tremolos!
848 Stem::calc_stem_info (Grob *me)
850 Direction my_dir = get_grob_direction (me);
851 Real staff_space = Staff_symbol_referencer::staff_space (me);
852 Grob *beam = get_beam (me);
853 Real beam_translation = Beam::get_beam_translation (beam);
854 Real beam_thickness = robust_scm2double (beam->get_grob_property ("thickness"), 1);
855 int beam_count = Beam::get_direction_beam_count (beam, my_dir);
858 /* Simple standard stem length */
859 SCM lengths = me->get_grob_property ("beamed-lengths");
861 gh_scm2double (robust_list_ref (beam_count - 1,lengths))
864 /* stem only extends to center of beam */
865 - 0.5 * beam_thickness;
867 /* Condition: sane minimum free stem length (chord to beams) */
868 lengths = me->get_grob_property ("beamed-minimum-free-lengths");
869 Real ideal_minimum_free =
870 gh_scm2double (robust_list_ref (beam_count - 1, lengths))
875 It seems that also for ideal minimum length, we must use
876 the maximum beam count (for this direction):
878 \score{ \notes\relative c''{ [a8 a32] }}
880 must be horizontal. */
881 Real height_of_my_beams = beam_thickness
882 + (beam_count - 1) * beam_translation;
884 Real ideal_minimum_length = ideal_minimum_free
886 /* stem only extends to center of beam */
887 - 0.5 * beam_thickness;
889 ideal_length = ideal_length >? ideal_minimum_length;
892 /* Convert to Y position, calculate for dir == UP */
894 /* staff positions */
895 head_positions (me)[my_dir] * 0.5
897 Real ideal_y = note_start + ideal_length;
900 /* Conditions for Y position */
902 /* Lowest beam of (UP) beam must never be lower than second staffline
906 Although this (additional) rule is probably correct,
907 I expect that highest beam (UP) should also never be lower
908 than middle staffline, just as normal stems.
912 Obviously not for grace beams.
914 Also, not for knees. Seems to be a good thing. */
915 bool no_extend_b = to_boolean (me->get_grob_property ("no-stem-extend"));
916 bool knee_b = to_boolean (beam->get_grob_property ("knee"));
917 if (!no_extend_b && !knee_b)
919 /* Highest beam of (UP) beam must never be lower than middle
921 ideal_y = ideal_y >? 0;
922 /* Lowest beam of (UP) beam must never be lower than second staffline */
923 ideal_y = ideal_y >? (-staff_space
924 - beam_thickness + height_of_my_beams);
928 ideal_y -= robust_scm2double (beam->get_grob_property ("shorten"), 0);
931 gh_scm2double (robust_list_ref
933 me->get_grob_property
934 ("beamed-extreme-minimum-free-lengths")))
937 Real minimum_length = minimum_free
939 /* stem only extends to center of beam */
940 - 0.5 * beam_thickness;
942 Real minimum_y = note_start + minimum_length;
946 Real shortest_y = minimum_y * my_dir;
948 me->set_grob_property ("stem-info",
949 scm_list_n (gh_double2scm (ideal_y),
950 gh_double2scm (shortest_y),
955 Stem::beam_multiplicity (Grob *stem)
957 SCM beaming= stem->get_grob_property ("beaming");
958 Slice l = int_list_to_slice (gh_car (beaming));
959 Slice r = int_list_to_slice (gh_cdr (beaming));
967 these are too many props.
969 ADD_INTERFACE (Stem,"stem-interface",
971 "tremolo-flag french-beaming "
972 "avoid-note-head adjust-if-on-staffline thickness "
973 "stem-info beamed-lengths beamed-minimum-free-lengths "
974 "beamed-extreme-minimum-free-lengths lengths beam stem-shorten "
975 "duration-log beaming neutral-direction stem-end-position "
976 "note-heads direction length flag-style "
977 "no-stem-extend stroke-style");