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
128 Stem::support_head (Grob*me)
130 SCM h = me->get_grob_property ("support-head");
131 Grob * nh = unsmob_grob (h);
134 else if (head_count (me) == 1)
140 return unsmob_grob (ly_car (me->get_grob_property ("note-heads")));
143 return first_head (me);
148 Stem::head_count (Grob*me)
150 return Pointer_group_interface::count (me, "note-heads");
154 The note head which forms one end of the stem.
157 Stem::first_head (Grob*me)
159 Direction d = get_direction (me);
162 return extremal_heads (me)[-d];
166 The note head opposite to the first head.
169 Stem::last_head (Grob*me)
171 Direction d = get_direction (me);
174 return extremal_heads (me)[d];
178 START is part where stem reaches `last' head.
181 Stem::extremal_heads (Grob*me)
183 const int inf = 1000000;
184 Drul_array<int> extpos;
188 Drul_array<Grob *> exthead;
189 exthead[LEFT] = exthead[RIGHT] =0;
191 for (SCM s = me->get_grob_property ("note-heads"); gh_pair_p (s); s = ly_cdr (s))
193 Grob * n = unsmob_grob (ly_car (s));
196 int p = int (Staff_symbol_referencer::get_position (n));
200 if (d* p > d* extpos[d])
205 } while (flip (&d) != DOWN);
212 icmp (int const &a, int const &b)
218 Stem::note_head_positions (Grob *me)
221 for (SCM s = me->get_grob_property ("note-heads"); gh_pair_p (s); s = ly_cdr (s))
223 Grob * n = unsmob_grob (ly_car (s));
224 int p = int (Staff_symbol_referencer::get_position (n));
235 Stem::add_head (Grob*me, Grob *n)
237 n->set_grob_property ("stem", me->self_scm ());
238 n->add_dependency (me);
240 if (Note_head::has_interface (n))
242 Pointer_group_interface::add_grob (me, ly_symbol2scm ("note-heads"), n);
247 Stem::invisible_b (Grob*me)
249 return ! (head_count (me) && Note_head::get_balltype (support_head (me)) >= 1);
253 Stem::get_default_dir (Grob*me)
255 int staff_center = 0;
256 Interval hp = head_positions (me);
262 int udistance = (int) (UP * hp[UP] - staff_center);
263 int ddistance = (int) (DOWN* hp[DOWN] - staff_center);
265 if (sign (ddistance - udistance))
266 return Direction (sign (ddistance -udistance));
268 return to_dir (me->get_grob_property ("neutral-direction"));
272 Stem::get_default_stem_end_position (Grob*me)
274 SCM up_to_staff = me->get_grob_property ("up-to-staff");
275 if (to_boolean(up_to_staff))
277 int line_count = Staff_symbol_referencer::line_count (me);
279 Direction dir = get_direction (me);
281 return dir* (line_count + 3.5);
284 bool grace_b = to_boolean (me->get_grob_property ("grace"));
289 SCM scm_len = me->get_grob_property ("length");
290 if (gh_number_p (scm_len))
292 length_f = gh_scm2double (scm_len);
296 s = me->get_grob_property ("lengths");
299 length_f = 2* gh_scm2double (robust_list_ref (duration_log(me) -2, s));
304 Real shorten_f = 0.0;
306 SCM sshorten = me->get_grob_property ("stem-shorten");
307 if (gh_pair_p (sshorten))
309 shorten_f = 2* gh_scm2double (robust_list_ref ((duration_log (me) - 2) >? 0, sshorten));
312 /* On boundary: shorten only half */
313 if (abs (chord_start_y (me)) == 0.5)
317 'set-default-stemlen' sets direction too
319 Direction dir = get_direction (me);
322 dir = get_default_dir (me);
323 Directional_element_interface::set (me, dir);
326 /* stems in unnatural (forced) direction should be shortened,
327 according to [Roush & Gourlay] */
328 if (chord_start_y (me)
329 && (get_direction (me) != get_default_dir (me)))
330 length_f -= shorten_f;
332 Interval hp = head_positions (me);
333 Real st = hp[dir] + dir * length_f;
335 bool no_extend_b = to_boolean (me->get_grob_property ("no-stem-extend"));
336 if (!grace_b && !no_extend_b && dir * st < 0) // junkme?
340 Make a little room if we have a upflag and there is a dot.
341 previous approach was to lengthen the stem. This is not
342 good typesetting practice.
345 if (!get_beam (me) && dir == UP
346 && duration_log (me) > 2)
348 Grob * closest_to_flag = extremal_heads (me)[dir];
349 Grob * dots = closest_to_flag
350 ? Rhythmic_head::get_dots (closest_to_flag ) : 0;
354 Real dp = Staff_symbol_referencer::get_position (dots);
355 Real flagy = flag (me).extent (Y_AXIS)[-dir] * 2
356 / Staff_symbol_referencer::staff_space (me);
359 Very gory: add myself to the X-support of the parent,
360 which should be a dot-column.
362 if (dir * (st + flagy - dp) < 0.5)
364 Grob *par = dots->get_parent (X_AXIS);
366 if (Dot_column::has_interface (par))
368 Side_position_interface::add_support (par, me);
371 TODO: apply some better logic here. The flag is
372 curved inwards, so this will typically be too
388 the log of the duration (Number of hooks on the flag minus two)
391 Stem::duration_log (Grob*me)
393 SCM s = me->get_grob_property ("duration-log");
394 return (gh_number_p (s)) ? gh_scm2int (s) : 2;
398 Stem::position_noteheads (Grob*me)
400 if (!head_count (me))
403 Link_array<Grob> heads =
404 Pointer_group_interface__extract_grobs (me, (Grob*)0, "note-heads");
406 heads.sort (compare_position);
407 Direction dir =get_direction (me);
413 bool invisible = invisible_b (me);
416 thick = gh_scm2double (me->get_grob_property ("thickness"))
417 * me->get_paper ()->get_var ("linethickness");
420 Grob *hed = support_head (me);
421 Real w = Note_head::head_extent (hed,X_AXIS)[dir];
422 for (int i=0; i < heads.size (); i++)
424 heads[i]->translate_axis (w - Note_head::head_extent (heads[i],X_AXIS)[dir],
428 bool parity= true; // todo: make me settable.
429 int lastpos = int (Staff_symbol_referencer::get_position (heads[0]));
430 for (int i=1; i < heads.size (); i ++)
432 Real p = Staff_symbol_referencer::get_position (heads[i]);
433 int dy =abs (lastpos- (int)p);
439 Real l = Note_head::head_extent (heads[i], X_AXIS).length ();
441 Direction d = get_direction (me);
442 heads[i]->translate_axis (l * d, X_AXIS);
445 heads[i]->translate_axis (-thick *2* d , X_AXIS);
450 For some cases we should kern some more: when the
451 distance between the next or prev note is too large, we'd
452 get large white gaps, eg.
471 MAKE_SCHEME_CALLBACK (Stem,before_line_breaking,1);
473 Stem::before_line_breaking (SCM smob)
475 Grob*me = unsmob_grob (smob);
479 Do the calculations for visible stems, but also for invisible stems
480 with note heads (i.e. half notes.)
484 stem_end_position (me); // ugh. Trigger direction calc.
485 position_noteheads (me);
489 me->remove_grob_property ("molecule-callback");
492 return SCM_UNSPECIFIED;
497 When in a beam with tuplet brackets, brew_mol is called early,
498 caching a wrong value.
500 MAKE_SCHEME_CALLBACK (Stem, height, 2);
502 Stem::height (SCM smob, SCM ax)
504 Axis a = (Axis)gh_scm2int (ax);
505 Grob * me = unsmob_grob (smob);
506 assert (a == Y_AXIS);
508 SCM mol = me->get_uncached_molecule ();
511 iv = unsmob_molecule (mol)->extent (a);
512 return ly_interval2scm (iv);
519 /* TODO: rename flag-style into something more appropriate,
520 e.g. "stroke-style", maybe with values "" (i.e. no stroke),
521 "single" and "double". Needs more discussion.
523 String style, fstyle, staffline_offs;
524 SCM fst = me->get_grob_property ("flag-style");
525 if (gh_string_p (fst))
527 fstyle = ly_scm2string (fst);
530 SCM st = me->get_grob_property ("style");
531 if (gh_symbol_p (st))
533 style = (ly_scm2string (scm_symbol_to_string (st)));
539 bool adjust = to_boolean (me->get_grob_property ("adjust-if-on-staffline"));
541 if (String::compare (style, "mensural") == 0)
542 /* Mensural notation: For notes on staff lines, use different
543 flags than for notes between staff lines. The idea is that
544 flags are always vertically aligned with the staff lines,
545 regardless if the note head is on a staff line or between two
546 staff lines. In other words, the inner end of a flag always
547 touches a staff line.
552 /* Urrgh! We have to detect wether this stem ends on a staff
553 line or between two staff lines. But we can not call
554 stem_end_position(me) or get_default_stem_end_position(me),
555 since this encounters the flag and hence results in an
556 infinite recursion. However, in pure mensural notation,
557 there are no multiple note heads attached to a single stem,
558 neither is there usually need for using the stem_shorten
559 property (except for 32th and 64th notes, but that is not a
560 problem since the stem length in this case is augmented by
561 an integral multiple of staff_space). Hence, it should be
562 sufficient to just take the first note head, assume it's
563 the only one, look if it's on a staff line, and select the
564 flag's shape accordingly. In the worst case, the shape
565 looks slightly misplaced, but that will usually be the
566 programmer's fault (e.g. when trying to attach multiple
567 note heads to a single stem in mensural notation). */
570 perhaps the detection whether this correction is needed should
571 happen in a different place to avoid the recursion.
575 Grob *first = first_head(me);
576 int sz = Staff_symbol_referencer::line_count (me)-1;
577 int p = (int)rint (Staff_symbol_referencer::get_position (first));
578 staffline_offs = (((p ^ sz) & 0x1) == 0) ? "1" : "0";
582 staffline_offs = "2";
589 char c = (get_direction (me) == UP) ? 'u' : 'd';
591 = String ("flags-") + style + to_string (c) + staffline_offs + to_string (duration_log (me));
593 = Font_interface::get_default_font (me)->find_by_name (index_string);
594 if (!fstyle.empty_b ())
595 m.add_molecule (Font_interface::get_default_font (me)->find_by_name (String ("flags-") + to_string (c) + fstyle));
599 MAKE_SCHEME_CALLBACK (Stem,dim_callback,2);
601 Stem::dim_callback (SCM e, SCM ax)
603 Axis a = (Axis) gh_scm2int (ax);
604 assert (a == X_AXIS);
605 Grob *se = unsmob_grob (e);
607 if (unsmob_grob (se->get_grob_property ("beam")) || abs (duration_log (se)) <= 2)
611 r = flag (se).extent (X_AXIS);
613 return ly_interval2scm (r);
618 MAKE_SCHEME_CALLBACK (Stem,brew_molecule,1);
621 Stem::brew_molecule (SCM smob)
623 Grob*me = unsmob_grob (smob);
625 Direction d = get_direction (me);
632 This is required to avoid stems passing in tablature chords...
637 TODO: make the stem start a direction ?
642 if (to_boolean (me->get_grob_property ("avoid-note-head")))
644 Grob * lh = last_head (me);
647 y1 = Staff_symbol_referencer::get_position (lh);
651 Grob * lh = first_head (me);
654 y1 = Staff_symbol_referencer::get_position (lh);
657 Real y2 = stem_end_position (me);
659 Interval stem_y (y1 <? y2,y2 >? y1);
663 Real dy = Staff_symbol_referencer::staff_space (me) * 0.5;
665 if (Grob *hed = support_head (me))
668 must not take ledgers into account.
670 Interval head_height = Note_head::head_extent (hed,Y_AXIS);
671 Real y_attach = Note_head::stem_attachment_coordinate ( hed, Y_AXIS);
673 y_attach = head_height.linear_combination (y_attach);
674 stem_y[Direction (-d)] += d * y_attach/dy;
677 if (!invisible_b (me))
679 Real stem_width = gh_scm2double (me->get_grob_property ("thickness"))
681 * me->get_paper ()->get_var ("linethickness");
683 Molecule ss =Lookup::filledbox (Box (Interval (-stem_width/2, stem_width/2),
684 Interval (stem_y[DOWN]*dy, stem_y[UP]*dy)));
685 mol.add_molecule (ss);
688 if (!get_beam (me) && abs (duration_log (me)) > 2)
690 Molecule fl = flag (me);
691 fl.translate_axis (stem_y[d]*dy, Y_AXIS);
692 mol.add_molecule (fl);
695 return mol.smobbed_copy ();
699 move the stem to right of the notehead if it is up.
701 MAKE_SCHEME_CALLBACK (Stem,off_callback,2);
703 Stem::off_callback (SCM element_smob, SCM)
705 Grob *me = unsmob_grob (element_smob);
709 if (head_count (me) == 0)
711 return gh_double2scm (0.0);
714 if (Grob * f = first_head (me))
716 Interval head_wid = Note_head::head_extent(f, X_AXIS);
721 if (invisible_b (me))
726 attach = Note_head::stem_attachment_coordinate(f, X_AXIS);
728 Direction d = get_direction (me);
730 Real real_attach = head_wid.linear_combination (d * attach);
735 If not centered: correct for stem thickness.
740 = gh_scm2double (me->get_grob_property ("thickness"))
741 * me->get_paper ()->get_var ("linethickness");
744 r += - d * rule_thick * 0.5;
747 return gh_double2scm (r);
753 Stem::get_beam (Grob*me)
755 SCM b= me->get_grob_property ("beam");
756 return unsmob_grob (b);
759 // ugh still very long.
761 Stem::calc_stem_info (Grob*me)
763 SCM up_to_staff = me->get_grob_property ("up-to-staff");
764 if (gh_scm2bool(up_to_staff)) {
766 // Up-to-staff : the stem end out of the staff.
769 FIXME: duplicate code.
771 int line_count = Staff_symbol_referencer::line_count (me);
775 Direction dir = get_direction (me);
777 si.ideal_y_ = dir* (line_count + 1.5);
779 si.shortest_y_ = si.ideal_y_;
784 SCM scm_info = me->get_grob_property ("stem-info");
786 if (gh_pair_p (scm_info ))
790 si.dir_ = Directional_element_interface::get(me);
791 si.ideal_y_ = gh_scm2double (gh_car (scm_info));
792 si.shortest_y_ = gh_scm2double (gh_cadr (scm_info));
797 Direction mydir = Directional_element_interface::get (me);
798 Real staff_space = Staff_symbol_referencer::staff_space (me);
799 Real half_space = staff_space / 2;
801 Grob * beam = get_beam (me);
802 int beam_count = beam_multiplicity(me).length()+1;
803 Real beam_translation= Beam::get_beam_translation (beam);
804 Real thick = gh_scm2double (beam->get_grob_property ("thickness"));
805 Real note_start = chord_start_y (me);
807 /* from here on, calculate as if dir == UP */
810 SCM grace_prop = me->get_grob_property ("grace");
812 bool grace_b = to_boolean (grace_prop);
813 SCM bml = robust_list_ref ( beam_count ,
814 me->get_grob_property ("beamed-minimum-lengths"));
816 Real minimum_length = gh_scm2double(bml)*staff_space;
817 SCM bl = robust_list_ref ( beam_count ,
818 me->get_grob_property ("beamed-lengths"));
819 Real stem_length = gh_scm2double(bl) * staff_space;
823 stem goes to center of beam, hence 0.5
825 Real beam_lengthen = beam_translation* (beam_count - 1)
826 + ((beam_count > 0) ? thick : 0) - 0.5 * thick;
828 Real shortest_y = note_start + minimum_length + beam_lengthen;
829 Real ideal_y = stem_length + note_start + beam_lengthen;
832 lowest beam of (UP) beam must never be lower than second staffline
834 Hmm, reference (Wanske?)
836 Although this (additional) rule is probably correct,
837 I expect that highest beam (UP) should also never be lower
838 than middle staffline, just as normal stems.
840 Add: not for knees. Not sure if that's is a good thing.
842 bool no_extend_b = to_boolean (me->get_grob_property ("no-stem-extend"));
843 bool knee_b = to_boolean (beam->get_grob_property ("knee"));
844 if (!grace_b && !no_extend_b && !knee_b)
846 /* highest beam of (UP) beam must never be lower than middle
848 lowest beam of (UP) beam must never be lower than second staffline
852 >? (- 2 * half_space - thick + beam_lengthen);
856 // ideal_y = ideal_y >? shortest_y;
857 SCM s = beam->get_grob_property ("shorten");
859 ideal_y -= gh_scm2double (s);
865 me->set_grob_property ("stem-info",
866 scm_list_n (gh_double2scm (ideal_y),
867 gh_double2scm (shortest_y),
872 si.shortest_y_ = shortest_y;
873 si.ideal_y_ = ideal_y;
881 Stem::beam_multiplicity (Grob *stem)
883 SCM beaming= stem->get_grob_property ("beaming");
884 Slice l = int_list_to_slice (gh_car (beaming));
885 Slice r = int_list_to_slice (gh_cdr (beaming));
892 ADD_INTERFACE (Stem,"stem-interface",
894 "up-to-staff avoid-note-head adjust-if-on-staffline thickness stem-info beamed-lengths beamed-minimum-lengths lengths beam stem-shorten duration-log beaming neutral-direction stem-end-position support-head note-heads direction length style no-stem-extend flag-style dir-forced");