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 "output-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 (!ly_c_pair_p (pair))
45 pair = scm_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 = scm_cons (scm_int2num (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 Interval hp = head_positions (me);
80 return hp[get_direction (me)] * Staff_symbol_referencer::staff_space (me)
85 Stem::stem_end_position (Grob *me)
87 SCM p = me->get_property ("stem-end-position");
89 if (!ly_c_number_p (p))
91 pos = get_default_stem_end_position (me);
92 me->set_property ("stem-end-position", scm_make_real (pos));
95 pos = ly_scm2double (p);
101 Stem::get_direction (Grob *me)
103 Direction d = get_grob_direction (me);
107 d = get_default_dir (me);
109 set_grob_direction (me, d);
115 Stem::set_stemend (Grob *me, Real se)
118 Direction d= get_direction (me);
120 if (d && d * head_positions (me)[get_direction (me)] >= se*d)
121 me->warning (_ ("Weird stem size; check for narrow beams"));
123 me->set_property ("stem-end-position", scm_make_real (se));
126 /* Note head that determines hshift for upstems
127 WARNING: triggers direction */
129 Stem::support_head (Grob *me)
131 if (head_count (me) == 1)
133 return unsmob_grob (ly_car (me->get_property ("note-heads")));
134 return first_head (me);
138 Stem::head_count (Grob*me)
140 return Pointer_group_interface::count (me, "note-heads");
143 /* The note head which forms one end of the stem.
144 WARNING: triggers direction */
146 Stem::first_head (Grob *me)
148 Direction d = get_direction (me);
151 return extremal_heads (me)[-d];
154 /* The note head opposite to the first head. */
156 Stem::last_head (Grob *me)
158 Direction d = get_direction (me);
161 return extremal_heads (me)[d];
164 /* START is part where stem reaches `last' head. */
166 Stem::extremal_heads (Grob *me)
168 const int inf = 1000000;
169 Drul_array<int> extpos;
173 Drul_array<Grob *> exthead;
174 exthead[LEFT] = exthead[RIGHT] =0;
176 for (SCM s = me->get_property ("note-heads"); ly_c_pair_p (s); s = ly_cdr (s))
178 Grob *n = unsmob_grob (ly_car (s));
179 int p = Staff_symbol_referencer::get_rounded_position (n);
184 if (d* p > d* extpos[d])
189 } while (flip (&d) != DOWN);
195 icmp (int const &a, int const &b)
200 /* The positions, in ascending order. */
202 Stem::note_head_positions (Grob *me)
205 for (SCM s = me->get_property ("note-heads"); ly_c_pair_p (s); s = ly_cdr (s))
207 Grob * n = unsmob_grob (ly_car (s));
208 int p = Staff_symbol_referencer::get_rounded_position (n);
218 Stem::add_head (Grob *me, Grob *n)
220 n->set_property ("stem", me->self_scm ());
221 n->add_dependency (me);
223 /* TODO: why not store Rest pointers? */
224 if (Note_head::has_interface (n))
225 Pointer_group_interface::add_grob (me, ly_symbol2scm ("note-heads"), n);
229 Stem::is_invisible (Grob *me)
231 return !(head_count (me)
232 && ly_scm2int (me->get_property ("duration-log")) >= 1);
236 Stem::get_default_dir (Grob *me)
238 int staff_center = 0;
239 Interval hp = head_positions (me);
243 int udistance = (int) (UP * hp[UP] - staff_center);
244 int ddistance = (int) (DOWN * hp[DOWN] - staff_center);
246 if (sign (ddistance - udistance))
247 return Direction (sign (ddistance - udistance));
249 return to_dir (me->get_property ("neutral-direction"));
253 Stem::get_default_stem_end_position (Grob*me)
255 Real ss = Staff_symbol_referencer::staff_space (me);
256 int durlog = duration_log (me);
260 /* WARNING: IN HALF SPACES */
262 SCM scm_len = me->get_property ("length");
263 if (ly_c_number_p (scm_len))
264 length = ly_scm2double (scm_len);
267 s = me->get_property ("lengths");
269 length = 2* ly_scm2double (robust_list_ref (durlog -2, s));
273 'set-default-stemlen' sets direction too. */
274 Direction dir = get_direction (me);
277 dir = get_default_dir (me);
278 set_grob_direction (me, dir);
281 /* Stems in unnatural (forced) direction should be shortened,
282 according to [Roush & Gourlay] */
283 Interval hp = head_positions (me);
284 if (dir && dir * hp[dir] >= 0)
286 SCM sshorten = me->get_property ("stem-shorten");
287 SCM scm_shorten = ly_c_pair_p (sshorten) ?
288 robust_list_ref ((duration_log (me) - 2) >? 0, sshorten): SCM_EOL;
289 Real shorten = 2* robust_scm2double (scm_shorten,0);
291 /* On boundary: shorten only half */
292 if (abs (head_positions (me)[dir]) <= 1)
299 Grob *t_flag = unsmob_grob (me->get_property ("tremolo-flag"));
300 if (t_flag && !unsmob_grob (me->get_property ("beam")))
302 /* Crude hack: add extra space if tremolo flag is there.
304 We can't do this for the beam, since we get into a loop
305 (Stem_tremolo::raw_stencil () looks at the beam.) --hwn */
308 + 2 * Stem_tremolo::raw_stencil (t_flag).extent (Y_AXIS).length ()
313 Interval flag_ext = flag (me).extent (Y_AXIS) ;
314 if (!flag_ext.is_empty ())
315 minlen += 2 * flag_ext.length () / ss ;
317 /* The clash is smaller for down stems (since the tremolo is
322 length = length >? (minlen + 1.0);
325 Real st = dir ? hp[dir] + dir * length : 0;
327 /* TODO: change name to extend-stems to staff/center/'() */
328 bool no_extend_b = to_boolean (me->get_property ("no-stem-extend"));
329 if (!no_extend_b && dir * st < 0)
332 /* Make a little room if we have a upflag and there is a dot.
333 previous approach was to lengthen the stem. This is not
334 good typesetting practice. */
335 if (!get_beam (me) && dir == UP
338 Grob * closest_to_flag = extremal_heads (me)[dir];
339 Grob * dots = closest_to_flag
340 ? Rhythmic_head::get_dots (closest_to_flag ) : 0;
344 Real dp = Staff_symbol_referencer::get_position (dots);
345 Real flagy = flag (me).extent (Y_AXIS)[-dir] * 2
348 /* Very gory: add myself to the X-support of the parent,
349 which should be a dot-column. */
350 if (dir * (st + flagy - dp) < 0.5)
352 Grob *par = dots->get_parent (X_AXIS);
354 if (Dot_column::has_interface (par))
356 Side_position_interface::add_support (par, me);
358 /* TODO: apply some better logic here. The flag is
359 curved inwards, so this will typically be too
372 the log of the duration (Number of hooks on the flag minus two)
375 Stem::duration_log (Grob*me)
377 SCM s = me->get_property ("duration-log");
378 return (ly_c_number_p (s)) ? ly_scm2int (s) : 2;
382 Stem::position_noteheads (Grob*me)
384 if (!head_count (me))
387 Link_array<Grob> heads =
388 Pointer_group_interface__extract_grobs (me, (Grob*)0, "note-heads");
390 heads.sort (compare_position);
391 Direction dir =get_direction (me);
397 Real thick = thickness (me);
399 Grob *hed = support_head (me);
400 Real w = Note_head::head_extent (hed,X_AXIS)[dir];
401 for (int i=0; i < heads.size (); i++)
403 heads[i]->translate_axis (w - Note_head::head_extent (heads[i], X_AXIS)[dir],
408 Real lastpos = Real (Staff_symbol_referencer::get_position (heads[0]));
409 for (int i=1; i < heads.size (); i ++)
411 Real p = Staff_symbol_referencer::get_position (heads[i]);
412 Real dy =fabs (lastpos- p);
415 dy should always be 0.5, 0.0, 1.0, but provide safety margin
422 Real l = Note_head::head_extent (heads[i], X_AXIS).length ();
424 Direction d = get_direction (me);
426 Reversed head should be shifted l-thickness, but this
427 looks too crowded, so we only shift l-0.5*thickness.
429 This leads to assymetry: Normal heads overlap the
430 stem 100% whereas reversed heads only overlaps the
435 Real reverse_overlap =0.5;
436 heads[i]->translate_axis ((l-thick*reverse_overlap) * d, X_AXIS);
438 if (is_invisible (me))
439 heads[i]->translate_axis (-thick*(2 - reverse_overlap) * d , X_AXIS);
444 For some cases we should kern some more: when the
445 distance between the next or prev note is too large, we'd
446 get large white gaps, eg.
465 MAKE_SCHEME_CALLBACK (Stem,before_line_breaking,1);
467 Stem::before_line_breaking (SCM smob)
469 Grob*me = unsmob_grob (smob);
473 Do the calculations for visible stems, but also for invisible stems
474 with note heads (i.e. half notes.)
478 stem_end_position (me); // ugh. Trigger direction calc.
479 position_noteheads (me);
483 me->set_property ("print-function", SCM_EOL);
486 return SCM_UNSPECIFIED;
491 When in a beam with tuplet brackets, brew_mol is called early,
492 caching a wrong value.
494 MAKE_SCHEME_CALLBACK (Stem, height, 2);
496 Stem::height (SCM smob, SCM ax)
498 Axis a = (Axis)ly_scm2int (ax);
499 Grob * me = unsmob_grob (smob);
500 assert (a == Y_AXIS);
502 SCM mol = me->get_uncached_stencil ();
505 iv = unsmob_stencil (mol)->extent (a);
506 if (Grob *b =get_beam (me))
508 Direction d = get_direction (me);
509 iv[d] += d * Beam::get_thickness (b) * 0.5 ;
512 return ly_interval2scm (iv);
519 /* TODO: maybe property stroke-style should take different values,
520 e.g. "" (i.e. no stroke), "single" and "double" (currently, it's
524 SCM flag_style_scm = me->get_property ("flag-style");
525 if (ly_c_symbol_p (flag_style_scm))
527 flag_style = ly_symbol2string (flag_style_scm);
530 if (flag_style == "no-flag")
537 String staffline_offs;
538 if (String::compare (flag_style, "mensural") == 0)
539 /* Mensural notation: For notes on staff lines, use different
540 flags than for notes between staff lines. The idea is that
541 flags are always vertically aligned with the staff lines,
542 regardless if the note head is on a staff line or between two
543 staff lines. In other words, the inner end of a flag always
544 touches a staff line.
549 /* Urrgh! We have to detect wether this stem ends on a staff
550 line or between two staff lines. But we can not call
551 stem_end_position (me) or get_default_stem_end_position (me),
552 since this encounters the flag and hence results in an
553 infinite recursion. However, in pure mensural notation,
554 there are no multiple note heads attached to a single stem,
555 neither is there usually need for using the stem_shorten
556 property (except for 32th and 64th notes, but that is not a
557 problem since the stem length in this case is augmented by
558 an integral multiple of staff_space). Hence, it should be
559 sufficient to just take the first note head, assume it's
560 the only one, look if it's on a staff line, and select the
561 flag's shape accordingly. In the worst case, the shape
562 looks slightly misplaced, but that will usually be the
563 programmer's fault (e.g. when trying to attach multiple
564 note heads to a single stem in mensural notation).
568 perhaps the detection whether this correction is needed should
569 happen in a different place to avoid the recursion.
573 int p = Staff_symbol_referencer::get_rounded_position (me);
574 staffline_offs = Staff_symbol_referencer::on_staffline (me, p) ?
579 staffline_offs = "2";
587 char dir = (get_direction (me) == UP) ? 'u' : 'd';
589 flag_style + to_string (dir) + staffline_offs + to_string (duration_log (me));
590 Font_metric *fm = Font_interface::get_default_font (me);
591 Stencil flag = fm->find_by_name ("flags-" + font_char);
592 if (flag.is_empty ())
594 me->warning (_f ("flag `%s' not found", font_char));
597 SCM stroke_style_scm = me->get_property ("stroke-style");
598 if (ly_c_string_p (stroke_style_scm))
600 String stroke_style = ly_scm2string (stroke_style_scm);
601 if (!stroke_style.is_empty ())
603 String font_char = to_string (dir) + stroke_style;
604 Stencil stroke = fm->find_by_name ("flags-" + font_char);
605 if (stroke.is_empty ())
607 me->warning (_f ("flag stroke `%s' not found", font_char));
611 flag.add_stencil (stroke);
619 MAKE_SCHEME_CALLBACK (Stem,dim_callback,2);
621 Stem::dim_callback (SCM e, SCM ax)
623 Axis a = (Axis) ly_scm2int (ax);
624 assert (a == X_AXIS);
625 Grob *me = unsmob_grob (e);
627 if (unsmob_grob (me->get_property ("beam")) || abs (duration_log (me)) <= 2)
631 r = flag (me).extent (X_AXIS)
634 return ly_interval2scm (r);
638 Stem::thickness (Grob* me)
640 return ly_scm2double (me->get_property ("thickness"))
641 * Staff_symbol_referencer::line_thickness (me);
644 MAKE_SCHEME_CALLBACK (Stem,print,1);
647 Stem::print (SCM smob)
649 Grob*me = unsmob_grob (smob);
651 Direction d = get_direction (me);
654 TODO: make the stem start a direction ?
656 This is required to avoid stems passing in tablature chords...
658 Grob *lh = to_boolean (me->get_property ("avoid-note-head"))
659 ? last_head (me) : lh = first_head (me);
664 if (is_invisible (me))
667 Real y1 = Staff_symbol_referencer::get_position (lh);
668 Real y2 = stem_end_position (me);
670 Interval stem_y (y1 <? y2,y2 >? y1);
674 Real dy = Staff_symbol_referencer::staff_space (me) * 0.5;
676 if (Grob *hed = support_head (me))
679 must not take ledgers into account.
681 Interval head_height = Note_head::head_extent (hed,Y_AXIS);
682 Real y_attach = Note_head::stem_attachment_coordinate (hed, Y_AXIS);
684 y_attach = head_height.linear_combination (y_attach);
685 stem_y[Direction (-d)] += d * y_attach/dy;
690 Real stem_width = thickness (me);
692 me->get_paper ()->get_dimension (ly_symbol2scm ("blotdiameter"));
694 Box b = Box (Interval (-stem_width/2, stem_width/2),
695 Interval (stem_y[DOWN]*dy, stem_y[UP]*dy));
697 Stencil ss = Lookup::round_filled_box (b, blot);
698 mol.add_stencil (ss);
700 if (!get_beam (me) && abs (duration_log (me)) > 2)
702 Stencil fl = flag (me);
703 fl.translate_axis (stem_y[d]*dy - d * blot/2, Y_AXIS);
704 fl.translate_axis (stem_width/2, X_AXIS);
705 mol.add_stencil (fl);
708 return mol.smobbed_copy ();
712 move the stem to right of the notehead if it is up.
714 MAKE_SCHEME_CALLBACK (Stem,off_callback,2);
716 Stem::off_callback (SCM element_smob, SCM)
718 Grob *me = unsmob_grob (element_smob);
722 if (head_count (me) == 0)
724 return scm_make_real (0.0);
727 if (Grob * f = first_head (me))
729 Interval head_wid = Note_head::head_extent (f, X_AXIS);
733 if (is_invisible (me))
738 attach = Note_head::stem_attachment_coordinate (f, X_AXIS);
740 Direction d = get_direction (me);
742 Real real_attach = head_wid.linear_combination (d * attach);
747 If not centered: correct for stem thickness.
754 r += - d * rule_thick * 0.5;
757 return scm_make_real (r);
762 Stem::get_beam (Grob*me)
764 SCM b= me->get_property ("beam");
765 return unsmob_grob (b);
769 Stem::get_stem_info (Grob *me)
771 /* Return cached info if available */
772 SCM scm_info = me->get_property ("stem-info");
773 if (!ly_c_pair_p (scm_info))
776 scm_info = me->get_property ("stem-info");
780 si.dir_ = get_grob_direction (me);
781 si.ideal_y_ = ly_scm2double (ly_car (scm_info));
782 si.shortest_y_ = ly_scm2double (ly_cadr (scm_info));
787 /* TODO: add extra space for tremolos! */
789 Stem::calc_stem_info (Grob *me)
791 Direction my_dir = get_grob_direction (me);
795 programming_error ("No stem dir set?");
799 Real staff_space = Staff_symbol_referencer::staff_space (me);
800 Grob *beam = get_beam (me);
801 Real beam_translation = Beam::get_beam_translation (beam);
802 Real beam_thickness = Beam::get_thickness (beam);
803 int beam_count = Beam::get_direction_beam_count (beam, my_dir);
806 /* Simple standard stem length */
807 SCM lengths = me->get_property ("beamed-lengths");
809 ly_scm2double (robust_list_ref (beam_count - 1,lengths))
812 /* stem only extends to center of beam */
813 - 0.5 * beam_thickness;
815 /* Condition: sane minimum free stem length (chord to beams) */
816 lengths = me->get_property ("beamed-minimum-free-lengths");
817 Real ideal_minimum_free =
818 ly_scm2double (robust_list_ref (beam_count - 1, lengths))
823 It seems that also for ideal minimum length, we must use
824 the maximum beam count (for this direction):
826 \score{ \notes\relative c''{ [a8 a32] }}
828 must be horizontal. */
829 Real height_of_my_beams = beam_thickness
830 + (beam_count - 1) * beam_translation;
832 Real ideal_minimum_length = ideal_minimum_free
834 /* stem only extends to center of beam */
835 - 0.5 * beam_thickness;
837 ideal_length = ideal_length >? ideal_minimum_length;
839 /* Convert to Y position, calculate for dir == UP */
841 /* staff positions */
842 head_positions (me)[my_dir] * 0.5
843 * my_dir * staff_space;
844 Real ideal_y = note_start + ideal_length;
847 /* Conditions for Y position */
849 /* Lowest beam of (UP) beam must never be lower than second staffline
853 Although this (additional) rule is probably correct,
854 I expect that highest beam (UP) should also never be lower
855 than middle staffline, just as normal stems.
859 Obviously not for grace beams.
861 Also, not for knees. Seems to be a good thing. */
862 bool no_extend_b = to_boolean (me->get_property ("no-stem-extend"));
863 bool is_knee = to_boolean (beam->get_property ("knee"));
864 if (!no_extend_b && !is_knee)
866 /* Highest beam of (UP) beam must never be lower than middle
868 ideal_y = ideal_y >? 0;
869 /* Lowest beam of (UP) beam must never be lower than second staffline */
870 ideal_y = ideal_y >? (-staff_space
871 - beam_thickness + height_of_my_beams);
875 ideal_y -= robust_scm2double (beam->get_property ("shorten"), 0);
878 ly_scm2double (robust_list_ref
881 ("beamed-extreme-minimum-free-lengths")))
884 Real minimum_length = minimum_free
886 /* stem only extends to center of beam */
887 - 0.5 * beam_thickness;
890 Real minimum_y = note_start + minimum_length;
891 Real shortest_y = minimum_y * my_dir;
893 me->set_property ("stem-info",
894 scm_list_2 (scm_make_real (ideal_y),
895 scm_make_real (shortest_y)));
899 Stem::beam_multiplicity (Grob *stem)
901 SCM beaming= stem->get_property ("beaming");
902 Slice le = int_list_to_slice (ly_car (beaming));
903 Slice ri = int_list_to_slice (ly_cdr (beaming));
909 /* FIXME: Too many properties */
910 ADD_INTERFACE (Stem,"stem-interface",
911 "The stem represent the graphical stem. "
912 " In addition, it internally connects note heads, beams, tremolos. Rests "
913 " and whole notes have invisible stems.",
914 "tremolo-flag french-beaming "
915 "avoid-note-head thickness "
916 "stem-info beamed-lengths beamed-minimum-free-lengths "
917 "beamed-extreme-minimum-free-lengths lengths beam stem-shorten "
918 "duration-log beaming neutral-direction stem-end-position "
919 "note-heads direction length flag-style "
920 "no-stem-extend stroke-style");
924 /****************************************************************/
926 Stem_info::Stem_info ()
928 ideal_y_ = shortest_y_ =0;
933 Stem_info::scale (Real x)