2 stem.cc -- implement Stem
4 source file of the GNU LilyPond music typesetter
6 (c) 1996--2002 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"
38 Stem::set_beaming (Grob*me, int beam_count, Direction d)
40 SCM pair = me->get_grob_property ("beaming");
42 if (!gh_pair_p (pair))
44 pair = gh_cons (SCM_EOL, SCM_EOL);
45 me->set_grob_property ("beaming", pair);
48 SCM l = index_get_cell (pair, d);
49 for( int i = 0; i< beam_count; i++)
51 l = gh_cons (gh_int2scm (i), l);
53 index_set_cell (pair, d, l);
58 Stem::head_positions (Grob*me)
66 Drul_array<Grob*> e (extremal_heads (me));
68 return Interval (Staff_symbol_referencer::get_position (e[DOWN]),
69 Staff_symbol_referencer::get_position (e[UP]));
74 Stem::chord_start_y (Grob*me)
76 return head_positions (me)[get_direction (me)]
77 * Staff_symbol_referencer::staff_space (me)/2.0;
81 Stem::stem_end_position (Grob*me)
83 SCM p =me->get_grob_property ("stem-end-position");
87 pos = get_default_stem_end_position (me);
88 me->set_grob_property ("stem-end-position", gh_double2scm (pos));
91 pos = gh_scm2double (p);
97 Stem::get_direction (Grob*me)
99 Direction d = Directional_element_interface::get (me);
103 d = get_default_dir (me);
105 Directional_element_interface::set (me, d);
112 Stem::set_stemend (Grob*me, Real se)
115 Direction d= get_direction (me);
117 if (d && d * head_positions (me)[get_direction (me)] >= se*d)
118 me->warning (_ ("Weird stem size; check for narrow beams"));
120 me->set_grob_property ("stem-end-position", gh_double2scm (se));
125 Note head that determines hshift for upstems
127 WARNING: triggers direction
130 Stem::support_head (Grob*me)
132 SCM h = me->get_grob_property ("support-head");
133 Grob * nh = unsmob_grob (h);
136 else if (head_count (me) == 1)
142 return unsmob_grob (ly_car (me->get_grob_property ("note-heads")));
145 return first_head (me);
150 Stem::head_count (Grob*me)
152 return Pointer_group_interface::count (me, "note-heads");
156 The note head which forms one end of the stem.
158 WARNING: triggers direction
161 Stem::first_head (Grob*me)
163 Direction d = get_direction (me);
166 return extremal_heads (me)[-d];
170 The note head opposite to the first head.
173 Stem::last_head (Grob*me)
175 Direction d = get_direction (me);
178 return extremal_heads (me)[d];
182 START is part where stem reaches `last' head.
185 Stem::extremal_heads (Grob*me)
187 const int inf = 1000000;
188 Drul_array<int> extpos;
192 Drul_array<Grob *> exthead;
193 exthead[LEFT] = exthead[RIGHT] =0;
195 for (SCM s = me->get_grob_property ("note-heads"); gh_pair_p (s); s = ly_cdr (s))
197 Grob * n = unsmob_grob (ly_car (s));
200 int p = int (Staff_symbol_referencer::get_position (n));
204 if (d* p > d* extpos[d])
209 } while (flip (&d) != DOWN);
216 icmp (int const &a, int const &b)
222 Stem::note_head_positions (Grob *me)
225 for (SCM s = me->get_grob_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_grob_property ("stem", me->self_scm ());
242 n->add_dependency (me);
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 /* Tab notation feature: make stem end extend out of staff. */
280 SCM up_to_staff = me->get_grob_property ("up-to-staff");
281 if (to_boolean (up_to_staff))
283 int line_count = Staff_symbol_referencer::line_count (me);
284 Direction dir = get_direction (me);
285 return dir * (line_count + 3.5);
288 bool grace_b = to_boolean (me->get_grob_property ("grace"));
293 SCM scm_len = me->get_grob_property ("length");
294 if (gh_number_p (scm_len))
296 length_f = gh_scm2double (scm_len);
300 s = me->get_grob_property ("lengths");
303 length_f = 2* gh_scm2double (robust_list_ref (duration_log(me) -2, s));
307 Real shorten_f = 0.0;
309 SCM sshorten = me->get_grob_property ("stem-shorten");
310 if (gh_pair_p (sshorten))
312 shorten_f = 2* gh_scm2double (robust_list_ref ((duration_log (me) - 2) >? 0, sshorten));
315 /* On boundary: shorten only half */
316 if (abs (chord_start_y (me)) == 0.5)
320 'set-default-stemlen' sets direction too
322 Direction dir = get_direction (me);
325 dir = get_default_dir (me);
326 Directional_element_interface::set (me, dir);
329 /* stems in unnatural (forced) direction should be shortened,
330 according to [Roush & Gourlay] */
331 if (chord_start_y (me)
332 && (get_direction (me) != get_default_dir (me)))
333 length_f -= shorten_f;
335 Interval hp = head_positions (me);
336 Real st = hp[dir] + dir * length_f;
340 TODO: change name to extend-stems to staff/center/'()
342 bool no_extend_b = to_boolean (me->get_grob_property ("no-stem-extend"));
343 if (!grace_b && !no_extend_b && dir * st < 0) // junkme?
347 Make a little room if we have a upflag and there is a dot.
348 previous approach was to lengthen the stem. This is not
349 good typesetting practice.
352 if (!get_beam (me) && dir == UP
353 && duration_log (me) > 2)
355 Grob * closest_to_flag = extremal_heads (me)[dir];
356 Grob * dots = closest_to_flag
357 ? Rhythmic_head::get_dots (closest_to_flag ) : 0;
361 Real dp = Staff_symbol_referencer::get_position (dots);
362 Real flagy = flag (me).extent (Y_AXIS)[-dir] * 2
363 / Staff_symbol_referencer::staff_space (me);
366 Very gory: add myself to the X-support of the parent,
367 which should be a dot-column.
369 if (dir * (st + flagy - dp) < 0.5)
371 Grob *par = dots->get_parent (X_AXIS);
373 if (Dot_column::has_interface (par))
375 Side_position_interface::add_support (par, me);
378 TODO: apply some better logic here. The flag is
379 curved inwards, so this will typically be too
395 the log of the duration (Number of hooks on the flag minus two)
398 Stem::duration_log (Grob*me)
400 SCM s = me->get_grob_property ("duration-log");
401 return (gh_number_p (s)) ? gh_scm2int (s) : 2;
405 Stem::position_noteheads (Grob*me)
407 if (!head_count (me))
410 Link_array<Grob> heads =
411 Pointer_group_interface__extract_grobs (me, (Grob*)0, "note-heads");
413 heads.sort (compare_position);
414 Direction dir =get_direction (me);
420 bool invisible = invisible_b (me);
423 thick = gh_scm2double (me->get_grob_property ("thickness"))
424 * me->get_paper ()->get_var ("linethickness");
427 Grob *hed = support_head (me);
428 Real w = Note_head::head_extent (hed,X_AXIS)[dir];
429 for (int i=0; i < heads.size (); i++)
431 heads[i]->translate_axis (w - Note_head::head_extent (heads[i],X_AXIS)[dir],
435 bool parity= true; // todo: make me settable.
436 int lastpos = int (Staff_symbol_referencer::get_position (heads[0]));
437 for (int i=1; i < heads.size (); i ++)
439 Real p = Staff_symbol_referencer::get_position (heads[i]);
440 int dy =abs (lastpos- (int)p);
446 Real l = Note_head::head_extent (heads[i], X_AXIS).length ();
448 Direction d = get_direction (me);
449 heads[i]->translate_axis (l * d, X_AXIS);
452 heads[i]->translate_axis (-thick *2* d , X_AXIS);
457 For some cases we should kern some more: when the
458 distance between the next or prev note is too large, we'd
459 get large white gaps, eg.
478 MAKE_SCHEME_CALLBACK (Stem,before_line_breaking,1);
480 Stem::before_line_breaking (SCM smob)
482 Grob*me = unsmob_grob (smob);
486 Do the calculations for visible stems, but also for invisible stems
487 with note heads (i.e. half notes.)
491 stem_end_position (me); // ugh. Trigger direction calc.
492 position_noteheads (me);
496 me->set_grob_property ("molecule-callback", SCM_EOL);
499 return SCM_UNSPECIFIED;
504 When in a beam with tuplet brackets, brew_mol is called early,
505 caching a wrong value.
507 MAKE_SCHEME_CALLBACK (Stem, height, 2);
509 Stem::height (SCM smob, SCM ax)
511 Axis a = (Axis)gh_scm2int (ax);
512 Grob * me = unsmob_grob (smob);
513 assert (a == Y_AXIS);
515 SCM mol = me->get_uncached_molecule ();
518 iv = unsmob_molecule (mol)->extent (a);
519 if (Grob *b =get_beam (me))
521 Direction d = get_direction (me);
522 iv[d] += d * Beam::get_thickness (b) /2.0 ;
525 return ly_interval2scm (iv);
532 /* TODO: maybe property stroke-style should take different values,
533 e.g. "" (i.e. no stroke), "single" and "double" (currently, it's
538 SCM flag_style_scm = me->get_grob_property ("flag-style");
539 if (gh_symbol_p (flag_style_scm))
541 flag_style = ly_symbol2string (flag_style_scm);
544 if (flag_style == "no-flag")
549 bool adjust = to_boolean (me->get_grob_property ("adjust-if-on-staffline"));
551 String staffline_offs;
552 if (String::compare (flag_style, "mensural") == 0)
553 /* Mensural notation: For notes on staff lines, use different
554 flags than for notes between staff lines. The idea is that
555 flags are always vertically aligned with the staff lines,
556 regardless if the note head is on a staff line or between two
557 staff lines. In other words, the inner end of a flag always
558 touches a staff line.
563 /* Urrgh! We have to detect wether this stem ends on a staff
564 line or between two staff lines. But we can not call
565 stem_end_position(me) or get_default_stem_end_position(me),
566 since this encounters the flag and hence results in an
567 infinite recursion. However, in pure mensural notation,
568 there are no multiple note heads attached to a single stem,
569 neither is there usually need for using the stem_shorten
570 property (except for 32th and 64th notes, but that is not a
571 problem since the stem length in this case is augmented by
572 an integral multiple of staff_space). Hence, it should be
573 sufficient to just take the first note head, assume it's
574 the only one, look if it's on a staff line, and select the
575 flag's shape accordingly. In the worst case, the shape
576 looks slightly misplaced, but that will usually be the
577 programmer's fault (e.g. when trying to attach multiple
578 note heads to a single stem in mensural notation). */
581 perhaps the detection whether this correction is needed should
582 happen in a different place to avoid the recursion.
586 int p = (int)rint (Staff_symbol_referencer::get_position (first_head (me)));
587 staffline_offs = Staff_symbol_referencer::on_staffline (me, p) ?
592 staffline_offs = "2";
600 char dir = (get_direction (me) == UP) ? 'u' : 'd';
602 flag_style + to_string (dir) + staffline_offs + to_string (duration_log (me));
603 Font_metric *fm = Font_interface::get_default_font (me);
604 Molecule flag = fm->find_by_name ("flags-" + font_char);
607 me->warning (_f ("flag `%s' not found", font_char));
610 SCM stroke_style_scm = me->get_grob_property ("stroke-style");
611 if (gh_string_p (stroke_style_scm))
613 String stroke_style = ly_scm2string (stroke_style_scm);
614 if (!stroke_style.empty_b ())
616 String font_char = to_string (dir) + stroke_style;
617 Molecule stroke = fm->find_by_name ("flags-" + font_char);
618 if (stroke.empty_b ())
620 me->warning (_f ("flag stroke `%s' not found", font_char));
624 flag.add_molecule (stroke);
632 MAKE_SCHEME_CALLBACK (Stem,dim_callback,2);
634 Stem::dim_callback (SCM e, SCM ax)
636 Axis a = (Axis) gh_scm2int (ax);
637 assert (a == X_AXIS);
638 Grob *se = unsmob_grob (e);
640 if (unsmob_grob (se->get_grob_property ("beam")) || abs (duration_log (se)) <= 2)
644 r = flag (se).extent (X_AXIS);
646 return ly_interval2scm (r);
651 MAKE_SCHEME_CALLBACK (Stem,brew_molecule,1);
654 Stem::brew_molecule (SCM smob)
656 Grob*me = unsmob_grob (smob);
658 Direction d = get_direction (me);
665 This is required to avoid stems passing in tablature chords...
670 TODO: make the stem start a direction ?
672 if (to_boolean (me->get_grob_property ("avoid-note-head")))
674 Grob * lh = last_head (me);
677 y1 = Staff_symbol_referencer::get_position (lh);
681 Grob * lh = first_head (me);
684 y1 = Staff_symbol_referencer::get_position (lh);
687 Real y2 = stem_end_position (me);
689 Interval stem_y (y1 <? y2,y2 >? y1);
693 Real dy = Staff_symbol_referencer::staff_space (me) * 0.5;
695 if (Grob *hed = support_head (me))
698 must not take ledgers into account.
700 Interval head_height = Note_head::head_extent (hed,Y_AXIS);
701 Real y_attach = Note_head::stem_attachment_coordinate ( hed, Y_AXIS);
703 y_attach = head_height.linear_combination (y_attach);
704 stem_y[Direction (-d)] += d * y_attach/dy;
707 if (!invisible_b (me))
709 Real stem_width = gh_scm2double (me->get_grob_property ("thickness"))
711 * me->get_paper ()->get_var ("linethickness");
713 Molecule ss =Lookup::filledbox (Box (Interval (-stem_width/2, stem_width/2),
714 Interval (stem_y[DOWN]*dy, stem_y[UP]*dy)));
715 mol.add_molecule (ss);
718 if (!get_beam (me) && abs (duration_log (me)) > 2)
720 Molecule fl = flag (me);
721 fl.translate_axis (stem_y[d]*dy, Y_AXIS);
722 mol.add_molecule (fl);
725 return mol.smobbed_copy ();
729 move the stem to right of the notehead if it is up.
731 MAKE_SCHEME_CALLBACK (Stem,off_callback,2);
733 Stem::off_callback (SCM element_smob, SCM)
735 Grob *me = unsmob_grob (element_smob);
739 if (head_count (me) == 0)
741 return gh_double2scm (0.0);
744 if (Grob * f = first_head (me))
746 Interval head_wid = Note_head::head_extent(f, X_AXIS);
751 if (invisible_b (me))
756 attach = Note_head::stem_attachment_coordinate(f, X_AXIS);
758 Direction d = get_direction (me);
760 Real real_attach = head_wid.linear_combination (d * attach);
765 If not centered: correct for stem thickness.
770 = gh_scm2double (me->get_grob_property ("thickness"))
771 * me->get_paper ()->get_var ("linethickness");
774 r += - d * rule_thick * 0.5;
777 return gh_double2scm (r);
781 Stem::get_beam (Grob*me)
783 SCM b= me->get_grob_property ("beam");
784 return unsmob_grob (b);
788 Stem::get_stem_info (Grob *me)
790 /* Return cached info if available */
791 SCM scm_info = me->get_grob_property ("stem-info");
792 if (!gh_pair_p (scm_info))
795 scm_info = me->get_grob_property ("stem-info");
799 si.dir_ = Directional_element_interface::get (me);
800 si.ideal_y_ = gh_scm2double (gh_car (scm_info));
801 si.shortest_y_ = gh_scm2double (gh_cadr (scm_info));
806 Stem::calc_stem_info (Grob *me)
808 /* Tab notation feature: make stem end extend out of staff. */
809 SCM up_to_staff = me->get_grob_property ("up-to-staff");
810 if (to_boolean (up_to_staff))
812 int line_count = Staff_symbol_referencer::line_count (me);
813 Direction dir = get_direction (me);
814 Real ideal_y = dir * (line_count + 1.5);
815 Real shortest_y = ideal_y;
817 me->set_grob_property ("stem-info",
818 scm_list_n (gh_double2scm (ideal_y),
819 gh_double2scm (shortest_y),
824 Direction my_dir = Directional_element_interface::get (me);
825 Real staff_space = Staff_symbol_referencer::staff_space (me);
826 Grob *beam = get_beam (me);
827 Real beam_translation = Beam::get_beam_translation (beam);
828 Real beam_thickness = gh_scm2double (beam->get_grob_property ("thickness"));
829 int beam_count = Beam::get_direction_beam_count (beam, my_dir);
832 /* Simple standard stem length */
834 gh_scm2double (robust_list_ref
836 me->get_grob_property ("beamed-lengths")))
838 /* stem only extends to center of beam */
839 - 0.5 * beam_thickness;
842 /* Condition: sane minimum free stem length (chord to beams) */
843 Real ideal_minimum_free =
844 gh_scm2double (robust_list_ref
846 me->get_grob_property ("beamed-minimum-free-lengths")))
851 It seems that also for ideal minimum length, we must use
852 the maximum beam count (for this direction):
854 \score{ \notes\relative c''{ [a8 a32] }}
856 must be horizontal. */
857 Real height_of_my_beams = beam_thickness
858 + (beam_count - 1) * beam_translation;
860 Real ideal_minimum_length = ideal_minimum_free
862 /* stem only extends to center of beam */
863 - 0.5 * beam_thickness;
865 ideal_length = ideal_length >? ideal_minimum_length;
868 /* Convert to Y position, calculate for dir == UP */
870 /* staff positions */
871 head_positions (me)[my_dir] * 0.5
873 Real ideal_y = note_start + ideal_length;
876 /* Conditions for Y position */
878 /* Lowest beam of (UP) beam must never be lower than second staffline
882 Although this (additional) rule is probably correct,
883 I expect that highest beam (UP) should also never be lower
884 than middle staffline, just as normal stems.
888 Obviously not for grace beams.
890 Also, not for knees. Seems to be a good thing. */
891 SCM grace = me->get_grob_property ("grace");
892 bool grace_b = to_boolean (grace);
893 bool no_extend_b = to_boolean (me->get_grob_property ("no-stem-extend"));
894 bool knee_b = to_boolean (beam->get_grob_property ("knee"));
895 if (!grace_b && !no_extend_b && !knee_b)
897 /* Highest beam of (UP) beam must never be lower than middle
899 ideal_y = ideal_y >? 0;
900 /* Lowest beam of (UP) beam must never be lower than second staffline */
901 ideal_y = ideal_y >? (-staff_space
902 - beam_thickness + height_of_my_beams);
906 SCM shorten = beam->get_grob_property ("shorten");
907 if (gh_number_p (shorten))
908 ideal_y -= gh_scm2double (shorten);
912 gh_scm2double (robust_list_ref
914 me->get_grob_property
915 ("beamed-extreme-minimum-free-lengths")))
918 Real minimum_length = minimum_free
920 /* stem only extends to center of beam */
921 - 0.5 * beam_thickness;
923 Real minimum_y = note_start + minimum_length;
927 Real shortest_y = minimum_y * my_dir;
929 me->set_grob_property ("stem-info",
930 scm_list_n (gh_double2scm (ideal_y),
931 gh_double2scm (shortest_y),
936 Stem::beam_multiplicity (Grob *stem)
938 SCM beaming= stem->get_grob_property ("beaming");
939 Slice l = int_list_to_slice (gh_car (beaming));
940 Slice r = int_list_to_slice (gh_cdr (beaming));
947 ADD_INTERFACE (Stem,"stem-interface",
949 "up-to-staff avoid-note-head adjust-if-on-staffline thickness stem-info beamed-lengths beamed-minimum-free-lengths beamed-extreme-minimum-free-lengths lengths beam stem-shorten duration-log beaming neutral-direction stem-end-position support-head note-heads direction length flag-style no-stem-extend stroke-style");