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 lst = index_get_cell (pair, d);
50 for (int i = 0; i < beam_count; i++)
51 lst = scm_cons (scm_int2num (i), lst);
52 index_set_cell (pair, d, lst);
56 Stem::head_positions (Grob *me)
60 Drul_array<Grob*> e (extremal_heads (me));
61 return Interval (Staff_symbol_referencer::get_position (e[DOWN]),
62 Staff_symbol_referencer::get_position (e[UP]));
68 Stem::chord_start_y (Grob *me)
70 Interval hp = head_positions (me);
72 return hp[get_direction (me)] * Staff_symbol_referencer::staff_space (me)
78 Stem::stem_end_position (Grob *me)
80 SCM p = me->get_property ("stem-end-position");
82 if (!ly_c_number_p (p))
84 pos = get_default_stem_end_position (me);
85 me->set_property ("stem-end-position", scm_make_real (pos));
88 pos = ly_scm2double (p);
94 Stem::get_direction (Grob *me)
96 Direction d = get_grob_direction (me);
100 d = get_default_dir (me);
102 set_grob_direction (me, d);
108 Stem::set_stemend (Grob *me, Real se)
111 Direction d = get_direction (me);
113 if (d && d * head_positions (me)[get_direction (me)] >= se*d)
114 me->warning (_ ("Weird stem size; check for narrow beams"));
116 me->set_property ("stem-end-position", scm_make_real (se));
119 /* Note head that determines hshift for upstems
120 WARNING: triggers direction */
122 Stem::support_head (Grob *me)
124 if (head_count (me) == 1)
126 return unsmob_grob (ly_car (me->get_property ("note-heads")));
127 return first_head (me);
131 Stem::head_count (Grob *me)
133 return Pointer_group_interface::count (me, "note-heads");
136 /* The note head which forms one end of the stem.
137 WARNING: triggers direction */
139 Stem::first_head (Grob *me)
141 Direction d = get_direction (me);
143 return extremal_heads (me)[-d];
147 /* The note head opposite to the first head. */
149 Stem::last_head (Grob *me)
151 Direction d = get_direction (me);
153 return extremal_heads (me)[d];
157 /* START is part where stem reaches `last' head. */
159 Stem::extremal_heads (Grob *me)
161 const int inf = 1000000;
162 Drul_array<int> extpos;
166 Drul_array<Grob *> exthead;
167 exthead[LEFT] = exthead[RIGHT] =0;
169 for (SCM s = me->get_property ("note-heads"); ly_c_pair_p (s);
172 Grob *n = unsmob_grob (ly_car (s));
173 int p = Staff_symbol_referencer::get_rounded_position (n);
178 if (d * p > d * extpos[d])
183 } while (flip (&d) != DOWN);
189 icmp (int const &a, int const &b)
194 /* The positions, in ascending order. */
196 Stem::note_head_positions (Grob *me)
199 for (SCM s = me->get_property ("note-heads"); ly_c_pair_p (s);
202 Grob *n = unsmob_grob (ly_car (s));
203 int p = Staff_symbol_referencer::get_rounded_position (n);
213 Stem::add_head (Grob *me, Grob *n)
215 n->set_property ("stem", me->self_scm ());
216 n->add_dependency (me);
218 /* TODO: why not store Rest pointers? */
219 if (Note_head::has_interface (n))
220 Pointer_group_interface::add_grob (me, ly_symbol2scm ("note-heads"), n);
224 Stem::is_invisible (Grob *me)
226 return !(head_count (me)
227 && ly_scm2int (me->get_property ("duration-log")) >= 1);
231 Stem::get_default_dir (Grob *me)
233 int staff_center = 0;
234 Interval hp = head_positions (me);
238 int udistance = (int) (UP * hp[UP] - staff_center);
239 int ddistance = (int) (DOWN * hp[DOWN] - staff_center);
241 if (sign (ddistance - udistance))
242 return Direction (sign (ddistance - udistance));
244 return to_dir (me->get_property ("neutral-direction"));
248 Stem::get_default_stem_end_position (Grob *me)
250 Real ss = Staff_symbol_referencer::staff_space (me);
251 int durlog = duration_log (me);
255 /* WARNING: IN HALF SPACES */
257 SCM scm_len = me->get_property ("length");
258 if (ly_c_number_p (scm_len))
259 length = ly_scm2double (scm_len);
262 s = me->get_property ("lengths");
264 length = 2 * ly_scm2double (robust_list_ref (durlog - 2, s));
268 'set-default-stemlen' sets direction too. */
269 Direction dir = get_direction (me);
272 dir = get_default_dir (me);
273 set_grob_direction (me, dir);
276 /* Stems in unnatural (forced) direction should be shortened,
277 according to [Roush & Gourlay] */
278 Interval hp = head_positions (me);
279 if (dir && dir * hp[dir] >= 0)
281 SCM sshorten = me->get_property ("stem-shorten");
282 SCM scm_shorten = ly_c_pair_p (sshorten) ?
283 robust_list_ref ((duration_log (me) - 2) >? 0, sshorten): SCM_EOL;
284 Real shorten = 2* robust_scm2double (scm_shorten,0);
286 /* On boundary: shorten only half */
287 if (abs (head_positions (me)[dir]) <= 1)
294 Grob *t_flag = unsmob_grob (me->get_property ("tremolo-flag"));
295 if (t_flag && !unsmob_grob (me->get_property ("beam")))
297 /* Crude hack: add extra space if tremolo flag is there.
299 We can't do this for the beam, since we get into a loop
300 (Stem_tremolo::raw_stencil () looks at the beam.) --hwn */
303 + 2 * Stem_tremolo::raw_stencil (t_flag).extent (Y_AXIS).length ()
308 Interval flag_ext = flag (me).extent (Y_AXIS);
309 if (!flag_ext.is_empty ())
310 minlen += 2 * flag_ext.length () / ss;
312 /* The clash is smaller for down stems (since the tremolo is
317 length = length >? (minlen + 1.0);
320 Real st = dir ? hp[dir] + dir * length : 0;
322 /* TODO: change name to extend-stems to staff/center/'() */
323 bool no_extend_b = to_boolean (me->get_property ("no-stem-extend"));
324 if (!no_extend_b && dir * st < 0)
327 /* Make a little room if we have a upflag and there is a dot.
328 previous approach was to lengthen the stem. This is not
329 good typesetting practice. */
330 if (!get_beam (me) && dir == UP
333 Grob * closest_to_flag = extremal_heads (me)[dir];
334 Grob * dots = closest_to_flag
335 ? Rhythmic_head::get_dots (closest_to_flag ) : 0;
339 Real dp = Staff_symbol_referencer::get_position (dots);
340 Real flagy = flag (me).extent (Y_AXIS)[-dir] * 2 / ss;
342 /* Very gory: add myself to the X-support of the parent,
343 which should be a dot-column. */
344 if (dir * (st + flagy - dp) < 0.5)
346 Grob *par = dots->get_parent (X_AXIS);
348 if (Dot_column::has_interface (par))
350 Side_position_interface::add_support (par, me);
352 /* TODO: apply some better logic here. The flag is
353 curved inwards, so this will typically be too
362 /* The log of the duration (Number of hooks on the flag minus two) */
364 Stem::duration_log (Grob *me)
366 SCM s = me->get_property ("duration-log");
367 return (ly_c_number_p (s)) ? ly_scm2int (s) : 2;
371 Stem::position_noteheads (Grob *me)
373 if (!head_count (me))
376 Link_array<Grob> heads =
377 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "note-heads");
379 heads.sort (compare_position);
380 Direction dir =get_direction (me);
385 Real thick = thickness (me);
387 Grob *hed = support_head (me);
388 Real w = Note_head::head_extent (hed,X_AXIS)[dir];
389 for (int i = 0; i < heads.size (); i++)
390 heads[i]->translate_axis (w - Note_head::head_extent (heads[i],
395 Real lastpos = Real (Staff_symbol_referencer::get_position (heads[0]));
396 for (int i = 1; i < heads.size (); i ++)
398 Real p = Staff_symbol_referencer::get_position (heads[i]);
399 Real dy =fabs (lastpos- p);
402 dy should always be 0.5, 0.0, 1.0, but provide safety margin
409 Real ell = Note_head::head_extent (heads[i], X_AXIS).length ();
411 Direction d = get_direction (me);
413 Reversed head should be shifted ell-thickness, but this
414 looks too crowded, so we only shift ell-0.5*thickness.
416 This leads to assymetry: Normal heads overlap the
417 stem 100% whereas reversed heads only overlaps the
421 Real reverse_overlap = 0.5;
422 heads[i]->translate_axis ((ell - thick * reverse_overlap) * d,
425 if (is_invisible (me))
426 heads[i]->translate_axis (-thick * (2 - reverse_overlap) * d,
431 For some cases we should kern some more: when the
432 distance between the next or prev note is too large, we'd
433 get large white gaps, eg.
452 MAKE_SCHEME_CALLBACK (Stem,before_line_breaking,1);
454 Stem::before_line_breaking (SCM smob)
456 Grob *me = unsmob_grob (smob);
459 Do the calculations for visible stems, but also for invisible stems
460 with note heads (i.e. half notes.)
464 stem_end_position (me); // ugh. Trigger direction calc.
465 position_noteheads (me);
468 me->set_property ("print-function", SCM_EOL);
470 return SCM_UNSPECIFIED;
475 When in a beam with tuplet brackets, brew_mol is called early,
476 caching a wrong value.
478 MAKE_SCHEME_CALLBACK (Stem, height, 2);
480 Stem::height (SCM smob, SCM ax)
482 Axis a = (Axis)ly_scm2int (ax);
483 Grob *me = unsmob_grob (smob);
484 assert (a == Y_AXIS);
486 SCM mol = me->get_uncached_stencil ();
489 iv = unsmob_stencil (mol)->extent (a);
490 if (Grob *b =get_beam (me))
492 Direction d = get_direction (me);
493 iv[d] += d * Beam::get_thickness (b) * 0.5 ;
496 return ly_interval2scm (iv);
501 Stem::flag (Grob *me)
503 /* TODO: maybe property stroke-style should take different values,
504 e.g. "" (i.e. no stroke), "single" and "double" (currently, it's
508 SCM flag_style_scm = me->get_property ("flag-style");
509 if (ly_c_symbol_p (flag_style_scm))
510 flag_style = ly_symbol2string (flag_style_scm);
512 if (flag_style == "no-flag")
517 String staffline_offs;
518 if (String::compare (flag_style, "mensural") == 0)
519 /* Mensural notation: For notes on staff lines, use different
520 flags than for notes between staff lines. The idea is that
521 flags are always vertically aligned with the staff lines,
522 regardless if the note head is on a staff line or between two
523 staff lines. In other words, the inner end of a flag always
524 touches a staff line.
529 /* Urrgh! We have to detect wether this stem ends on a staff
530 line or between two staff lines. But we can not call
531 stem_end_position (me) or get_default_stem_end_position (me),
532 since this encounters the flag and hence results in an
533 infinite recursion. However, in pure mensural notation,
534 there are no multiple note heads attached to a single stem,
535 neither is there usually need for using the stem_shorten
536 property (except for 32th and 64th notes, but that is not a
537 problem since the stem length in this case is augmented by
538 an integral multiple of staff_space). Hence, it should be
539 sufficient to just take the first note head, assume it's
540 the only one, look if it's on a staff line, and select the
541 flag's shape accordingly. In the worst case, the shape
542 looks slightly misplaced, but that will usually be the
543 programmer's fault (e.g. when trying to attach multiple
544 note heads to a single stem in mensural notation).
548 perhaps the detection whether this correction is needed should
549 happen in a different place to avoid the recursion.
553 int p = Staff_symbol_referencer::get_rounded_position (me);
554 staffline_offs = Staff_symbol_referencer::on_staffline (me, p)
559 staffline_offs = "2";
567 char dir = (get_direction (me) == UP) ? 'u' : 'd';
568 String font_char = flag_style
569 + to_string (dir) + staffline_offs + to_string (duration_log (me));
570 Font_metric *fm = Font_interface::get_default_font (me);
571 Stencil flag = fm->find_by_name ("flags-" + font_char);
572 if (flag.is_empty ())
573 me->warning (_f ("flag `%s' not found", font_char));
575 SCM stroke_style_scm = me->get_property ("stroke-style");
576 if (ly_c_string_p (stroke_style_scm))
578 String stroke_style = ly_scm2string (stroke_style_scm);
579 if (!stroke_style.is_empty ())
581 String font_char = to_string (dir) + stroke_style;
582 Stencil stroke = fm->find_by_name ("flags-" + font_char);
583 if (stroke.is_empty ())
584 me->warning (_f ("flag stroke `%s' not found", font_char));
586 flag.add_stencil (stroke);
593 MAKE_SCHEME_CALLBACK (Stem,dim_callback,2);
595 Stem::dim_callback (SCM e, SCM ax)
597 Axis a = (Axis) ly_scm2int (ax);
598 assert (a == X_AXIS);
599 Grob *me = unsmob_grob (e);
601 if (unsmob_grob (me->get_property ("beam")) || abs (duration_log (me)) <= 2)
604 r = flag (me).extent (X_AXIS)
606 return ly_interval2scm (r);
610 Stem::thickness (Grob *me)
612 return ly_scm2double (me->get_property ("thickness"))
613 * Staff_symbol_referencer::line_thickness (me);
616 MAKE_SCHEME_CALLBACK (Stem, print, 1);
618 Stem::print (SCM smob)
620 Grob *me = unsmob_grob (smob);
622 Direction d = get_direction (me);
624 /* TODO: make the stem start a direction ?
625 This is required to avoid stems passing in tablature chords. */
626 Grob *lh = to_boolean (me->get_property ("avoid-note-head"))
627 ? last_head (me) : lh = first_head (me);
632 if (is_invisible (me))
635 Real y1 = Staff_symbol_referencer::get_position (lh);
636 Real y2 = stem_end_position (me);
638 Interval stem_y (y1 <? y2,y2 >? y1);
641 Real dy = Staff_symbol_referencer::staff_space (me) * 0.5;
643 if (Grob *hed = support_head (me))
646 must not take ledgers into account.
648 Interval head_height = Note_head::head_extent (hed,Y_AXIS);
649 Real y_attach = Note_head::stem_attachment_coordinate (hed, Y_AXIS);
651 y_attach = head_height.linear_combination (y_attach);
652 stem_y[Direction (-d)] += d * y_attach/dy;
657 Real stem_width = thickness (me);
659 me->get_paper ()->get_dimension (ly_symbol2scm ("blotdiameter"));
661 Box b = Box (Interval (-stem_width/2, stem_width/2),
662 Interval (stem_y[DOWN]*dy, stem_y[UP]*dy));
664 Stencil ss = Lookup::round_filled_box (b, blot);
665 mol.add_stencil (ss);
667 if (!get_beam (me) && abs (duration_log (me)) > 2)
669 Stencil fl = flag (me);
670 fl.translate_axis (stem_y[d]*dy - d * blot/2, Y_AXIS);
671 fl.translate_axis (stem_width/2, X_AXIS);
672 mol.add_stencil (fl);
675 return mol.smobbed_copy ();
679 move the stem to right of the notehead if it is up.
681 MAKE_SCHEME_CALLBACK (Stem, off_callback, 2);
683 Stem::off_callback (SCM element_smob, SCM)
685 Grob *me = unsmob_grob (element_smob);
689 if (Grob *f = first_head (me))
691 Interval head_wid = Note_head::head_extent (f, X_AXIS);
694 if (is_invisible (me))
697 attach = Note_head::stem_attachment_coordinate (f, X_AXIS);
699 Direction d = get_direction (me);
700 Real real_attach = head_wid.linear_combination (d * attach);
703 /* If not centered: correct for stem thickness. */
706 Real rule_thick = thickness (me);
707 r += - d * rule_thick * 0.5;
710 return scm_make_real (r);
714 Stem::get_beam (Grob *me)
716 SCM b = me->get_property ("beam");
717 return unsmob_grob (b);
721 Stem::get_stem_info (Grob *me)
723 /* Return cached info if available */
724 SCM scm_info = me->get_property ("stem-info");
725 if (!ly_c_pair_p (scm_info))
728 scm_info = me->get_property ("stem-info");
732 si.dir_ = get_grob_direction (me);
733 si.ideal_y_ = ly_scm2double (ly_car (scm_info));
734 si.shortest_y_ = ly_scm2double (ly_cadr (scm_info));
739 /* TODO: add extra space for tremolos! */
741 Stem::calc_stem_info (Grob *me)
743 Direction my_dir = get_grob_direction (me);
747 programming_error ("No stem dir set?");
751 Real staff_space = Staff_symbol_referencer::staff_space (me);
752 Grob *beam = get_beam (me);
753 Real beam_translation = Beam::get_beam_translation (beam);
754 Real beam_thickness = Beam::get_thickness (beam);
755 int beam_count = Beam::get_direction_beam_count (beam, my_dir);
758 /* Simple standard stem length */
759 SCM lengths = me->get_property ("beamed-lengths");
761 ly_scm2double (robust_list_ref (beam_count - 1,lengths))
764 /* stem only extends to center of beam */
765 - 0.5 * beam_thickness;
767 /* Condition: sane minimum free stem length (chord to beams) */
768 lengths = me->get_property ("beamed-minimum-free-lengths");
769 Real ideal_minimum_free =
770 ly_scm2double (robust_list_ref (beam_count - 1, lengths))
775 It seems that also for ideal minimum length, we must use
776 the maximum beam count (for this direction):
778 \score{ \notes\relative c''{ [a8 a32] }}
780 must be horizontal. */
781 Real height_of_my_beams = beam_thickness
782 + (beam_count - 1) * beam_translation;
784 Real ideal_minimum_length = ideal_minimum_free
786 /* stem only extends to center of beam */
787 - 0.5 * beam_thickness;
789 ideal_length = ideal_length >? ideal_minimum_length;
791 /* Convert to Y position, calculate for dir == UP */
793 /* staff positions */
794 head_positions (me)[my_dir] * 0.5
795 * my_dir * staff_space;
796 Real ideal_y = note_start + ideal_length;
799 /* Conditions for Y position */
801 /* Lowest beam of (UP) beam must never be lower than second staffline
805 Although this (additional) rule is probably correct,
806 I expect that highest beam (UP) should also never be lower
807 than middle staffline, just as normal stems.
811 Obviously not for grace beams.
813 Also, not for knees. Seems to be a good thing. */
814 bool no_extend_b = to_boolean (me->get_property ("no-stem-extend"));
815 bool is_knee = to_boolean (beam->get_property ("knee"));
816 if (!no_extend_b && !is_knee)
818 /* Highest beam of (UP) beam must never be lower than middle
820 ideal_y = ideal_y >? 0;
821 /* Lowest beam of (UP) beam must never be lower than second staffline */
822 ideal_y = ideal_y >? (-staff_space
823 - beam_thickness + height_of_my_beams);
827 ideal_y -= robust_scm2double (beam->get_property ("shorten"), 0);
830 ly_scm2double (robust_list_ref
833 ("beamed-extreme-minimum-free-lengths")))
836 Real minimum_length = minimum_free
838 /* stem only extends to center of beam */
839 - 0.5 * beam_thickness;
842 Real minimum_y = note_start + minimum_length;
843 Real shortest_y = minimum_y * my_dir;
845 me->set_property ("stem-info",
846 scm_list_2 (scm_make_real (ideal_y),
847 scm_make_real (shortest_y)));
851 Stem::beam_multiplicity (Grob *stem)
853 SCM beaming= stem->get_property ("beaming");
854 Slice le = int_list_to_slice (ly_car (beaming));
855 Slice ri = int_list_to_slice (ly_cdr (beaming));
861 /* FIXME: Too many properties */
862 ADD_INTERFACE (Stem, "stem-interface",
863 "The stem represent the graphical stem. "
864 "In addition, it internally connects note heads, beams and"
866 "Rests and whole notes have invisible stems.",
867 "tremolo-flag french-beaming "
868 "avoid-note-head thickness "
869 "stem-info beamed-lengths beamed-minimum-free-lengths "
870 "beamed-extreme-minimum-free-lengths lengths beam stem-shorten "
871 "duration-log beaming neutral-direction stem-end-position "
872 "note-heads direction length flag-style "
873 "no-stem-extend stroke-style");
875 /****************************************************************/
877 Stem_info::Stem_info ()
879 ideal_y_ = shortest_y_ = 0;
884 Stem_info::scale (Real x)