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
535 String flag_style, staffline_offs;
537 SCM flag_style_scm = me->get_grob_property ("flag-style");
538 if (gh_symbol_p (flag_style_scm))
540 flag_style = (ly_scm2string (scm_symbol_to_string (flag_style_scm)));
546 bool adjust = to_boolean (me->get_grob_property ("adjust-if-on-staffline"));
548 if (String::compare (flag_style, "mensural") == 0)
549 /* Mensural notation: For notes on staff lines, use different
550 flags than for notes between staff lines. The idea is that
551 flags are always vertically aligned with the staff lines,
552 regardless if the note head is on a staff line or between two
553 staff lines. In other words, the inner end of a flag always
554 touches a staff line.
559 /* Urrgh! We have to detect wether this stem ends on a staff
560 line or between two staff lines. But we can not call
561 stem_end_position(me) or get_default_stem_end_position(me),
562 since this encounters the flag and hence results in an
563 infinite recursion. However, in pure mensural notation,
564 there are no multiple note heads attached to a single stem,
565 neither is there usually need for using the stem_shorten
566 property (except for 32th and 64th notes, but that is not a
567 problem since the stem length in this case is augmented by
568 an integral multiple of staff_space). Hence, it should be
569 sufficient to just take the first note head, assume it's
570 the only one, look if it's on a staff line, and select the
571 flag's shape accordingly. In the worst case, the shape
572 looks slightly misplaced, but that will usually be the
573 programmer's fault (e.g. when trying to attach multiple
574 note heads to a single stem in mensural notation). */
577 perhaps the detection whether this correction is needed should
578 happen in a different place to avoid the recursion.
582 int p = (int)rint (Staff_symbol_referencer::get_position (first_head (me)));
583 staffline_offs = Staff_symbol_referencer::on_staffline (me, p) ?
588 staffline_offs = "2";
595 char dir = (get_direction (me) == UP) ? 'u' : 'd';
597 flag_style + to_string (dir) + staffline_offs + to_string (duration_log (me));
598 Font_metric *fm = Font_interface::get_default_font (me);
599 Molecule flag = fm->find_by_name ("flags-" + font_char);
602 me->warning (_f ("flag `%s' not found", font_char));
605 SCM stroke_style_scm = me->get_grob_property ("stroke-style");
606 if (gh_string_p (stroke_style_scm))
608 String stroke_style = ly_scm2string (stroke_style_scm);
609 if (!stroke_style.empty_b ())
611 String font_char = to_string (dir) + stroke_style;
612 Molecule stroke = fm->find_by_name ("flags-" + font_char);
613 if (stroke.empty_b ())
615 me->warning (_f ("flag stroke `%s' not found", font_char));
619 flag.add_molecule (stroke);
627 MAKE_SCHEME_CALLBACK (Stem,dim_callback,2);
629 Stem::dim_callback (SCM e, SCM ax)
631 Axis a = (Axis) gh_scm2int (ax);
632 assert (a == X_AXIS);
633 Grob *se = unsmob_grob (e);
635 if (unsmob_grob (se->get_grob_property ("beam")) || abs (duration_log (se)) <= 2)
639 r = flag (se).extent (X_AXIS);
641 return ly_interval2scm (r);
646 MAKE_SCHEME_CALLBACK (Stem,brew_molecule,1);
649 Stem::brew_molecule (SCM smob)
651 Grob*me = unsmob_grob (smob);
653 Direction d = get_direction (me);
660 This is required to avoid stems passing in tablature chords...
665 TODO: make the stem start a direction ?
670 if (to_boolean (me->get_grob_property ("avoid-note-head")))
672 Grob * lh = last_head (me);
675 y1 = Staff_symbol_referencer::get_position (lh);
679 Grob * lh = first_head (me);
682 y1 = Staff_symbol_referencer::get_position (lh);
685 Real y2 = stem_end_position (me);
687 Interval stem_y (y1 <? y2,y2 >? y1);
691 Real dy = Staff_symbol_referencer::staff_space (me) * 0.5;
693 if (Grob *hed = support_head (me))
696 must not take ledgers into account.
698 Interval head_height = Note_head::head_extent (hed,Y_AXIS);
699 Real y_attach = Note_head::stem_attachment_coordinate ( hed, Y_AXIS);
701 y_attach = head_height.linear_combination (y_attach);
702 stem_y[Direction (-d)] += d * y_attach/dy;
705 if (!invisible_b (me))
707 Real stem_width = gh_scm2double (me->get_grob_property ("thickness"))
709 * me->get_paper ()->get_var ("linethickness");
711 Molecule ss =Lookup::filledbox (Box (Interval (-stem_width/2, stem_width/2),
712 Interval (stem_y[DOWN]*dy, stem_y[UP]*dy)));
713 mol.add_molecule (ss);
716 if (!get_beam (me) && abs (duration_log (me)) > 2)
718 Molecule fl = flag (me);
719 fl.translate_axis (stem_y[d]*dy, Y_AXIS);
720 mol.add_molecule (fl);
723 return mol.smobbed_copy ();
727 move the stem to right of the notehead if it is up.
729 MAKE_SCHEME_CALLBACK (Stem,off_callback,2);
731 Stem::off_callback (SCM element_smob, SCM)
733 Grob *me = unsmob_grob (element_smob);
737 if (head_count (me) == 0)
739 return gh_double2scm (0.0);
742 if (Grob * f = first_head (me))
744 Interval head_wid = Note_head::head_extent(f, X_AXIS);
749 if (invisible_b (me))
754 attach = Note_head::stem_attachment_coordinate(f, X_AXIS);
756 Direction d = get_direction (me);
758 Real real_attach = head_wid.linear_combination (d * attach);
763 If not centered: correct for stem thickness.
768 = gh_scm2double (me->get_grob_property ("thickness"))
769 * me->get_paper ()->get_var ("linethickness");
772 r += - d * rule_thick * 0.5;
775 return gh_double2scm (r);
779 Stem::get_beam (Grob*me)
781 SCM b= me->get_grob_property ("beam");
782 return unsmob_grob (b);
786 Stem::get_stem_info (Grob *me)
788 /* Return cached info if available */
789 SCM scm_info = me->get_grob_property ("stem-info");
790 if (!gh_pair_p (scm_info))
793 scm_info = me->get_grob_property ("stem-info");
797 si.dir_ = Directional_element_interface::get (me);
798 si.ideal_y_ = gh_scm2double (gh_car (scm_info));
799 si.shortest_y_ = gh_scm2double (gh_cadr (scm_info));
804 Stem::calc_stem_info (Grob *me)
806 /* Tab notation feature: make stem end extend out of staff. */
807 SCM up_to_staff = me->get_grob_property ("up-to-staff");
808 if (to_boolean (up_to_staff))
810 int line_count = Staff_symbol_referencer::line_count (me);
811 Direction dir = get_direction (me);
812 Real ideal_y = dir * (line_count + 1.5);
813 Real shortest_y = ideal_y;
815 me->set_grob_property ("stem-info",
816 scm_list_n (gh_double2scm (ideal_y),
817 gh_double2scm (shortest_y),
822 Direction my_dir = Directional_element_interface::get (me);
823 Real staff_space = Staff_symbol_referencer::staff_space (me);
824 Grob *beam = get_beam (me);
825 Real beam_translation = Beam::get_beam_translation (beam);
826 Real beam_thickness = gh_scm2double (beam->get_grob_property ("thickness"));
827 int beam_count = Beam::get_direction_beam_count (beam, my_dir);
830 /* Simple standard stem length */
832 gh_scm2double (robust_list_ref
834 me->get_grob_property ("beamed-lengths")))
836 /* stem only extends to center of beam */
837 - 0.5 * beam_thickness;
840 /* Condition: sane minimum free stem length (chord to beams) */
841 Real ideal_minimum_free =
842 gh_scm2double (robust_list_ref
844 me->get_grob_property ("beamed-minimum-free-lengths")))
849 It seems that also for ideal minimum length, we must use
850 the maximum beam count (for this direction):
852 \score{ \notes\relative c''{ [a8 a32] }}
854 must be horizontal. */
855 Real height_of_my_beams = beam_thickness
856 + (beam_count - 1) * beam_translation;
858 Real ideal_minimum_length = ideal_minimum_free
860 /* stem only extends to center of beam */
861 - 0.5 * beam_thickness;
863 ideal_length = ideal_length >? ideal_minimum_length;
866 /* Convert to Y position, calculate for dir == UP */
868 /* staff positions */
869 head_positions (me)[my_dir] * 0.5
871 Real ideal_y = note_start + ideal_length;
874 /* Conditions for Y position */
876 /* Lowest beam of (UP) beam must never be lower than second staffline
880 Although this (additional) rule is probably correct,
881 I expect that highest beam (UP) should also never be lower
882 than middle staffline, just as normal stems.
886 Obviously not for grace beams.
888 Also, not for knees. Seems to be a good thing. */
889 SCM grace = me->get_grob_property ("grace");
890 bool grace_b = to_boolean (grace);
891 bool no_extend_b = to_boolean (me->get_grob_property ("no-stem-extend"));
892 bool knee_b = to_boolean (beam->get_grob_property ("knee"));
893 if (!grace_b && !no_extend_b && !knee_b)
895 /* Highest beam of (UP) beam must never be lower than middle
897 ideal_y = ideal_y >? 0;
898 /* Lowest beam of (UP) beam must never be lower than second staffline */
899 ideal_y = ideal_y >? (-staff_space
900 - beam_thickness + height_of_my_beams);
904 SCM shorten = beam->get_grob_property ("shorten");
905 if (gh_number_p (shorten))
906 ideal_y -= gh_scm2double (shorten);
910 gh_scm2double (robust_list_ref
912 me->get_grob_property
913 ("beamed-extreme-minimum-free-lengths")))
916 Real minimum_length = minimum_free
918 /* stem only extends to center of beam */
919 - 0.5 * beam_thickness;
921 Real minimum_y = note_start + minimum_length;
925 Real shortest_y = minimum_y * my_dir;
927 me->set_grob_property ("stem-info",
928 scm_list_n (gh_double2scm (ideal_y),
929 gh_double2scm (shortest_y),
934 Stem::beam_multiplicity (Grob *stem)
936 SCM beaming= stem->get_grob_property ("beaming");
937 Slice l = int_list_to_slice (gh_car (beaming));
938 Slice r = int_list_to_slice (gh_cdr (beaming));
945 ADD_INTERFACE (Stem,"stem-interface",
947 "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");