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 i, Direction d)
40 SCM pair = me->get_grob_property ("beaming");
42 if (!gh_pair_p (pair))
44 pair = gh_cons (gh_int2scm (-1),gh_int2scm (-1));
45 me-> set_grob_property ("beaming", pair);
47 index_set_cell (pair, d, gh_int2scm (i));
51 Stem::beam_count (Grob*me,Direction d)
53 SCM p=me->get_grob_property ("beaming");
55 return gh_scm2int (index_cell (p,d));
61 Stem::head_positions (Grob*me)
69 Drul_array<Grob*> e (extremal_heads (me));
71 return Interval (Staff_symbol_referencer::position_f (e[DOWN]),
72 Staff_symbol_referencer::position_f (e[UP]));
77 Stem::chord_start_y (Grob*me)
79 return head_positions (me)[get_direction (me)]
80 * Staff_symbol_referencer::staff_space (me)/2.0;
84 Stem::stem_end_position (Grob*me)
86 SCM p =me->get_grob_property ("stem-end-position");
90 pos = get_default_stem_end_position (me);
91 me->set_grob_property ("stem-end-position", gh_double2scm (pos));
94 pos = gh_scm2double (p);
100 Stem::get_direction (Grob*me)
102 Direction d = Directional_element_interface::get (me);
106 d = get_default_dir (me);
108 Directional_element_interface::set (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_grob_property ("stem-end-position", gh_double2scm (se));
128 Note head that determines hshift for upstems
131 Stem::support_head (Grob*me)
133 SCM h = me->get_grob_property ("support-head");
134 Grob * nh = unsmob_grob (h);
137 else if (head_count (me) == 1)
143 return unsmob_grob (ly_car (me->get_grob_property ("note-heads")));
146 return first_head (me);
151 Stem::head_count (Grob*me)
153 return Pointer_group_interface::count (me, "note-heads");
157 The note head which forms one end of the stem.
160 Stem::first_head (Grob*me)
162 Direction d = get_direction (me);
165 return extremal_heads (me)[-d];
169 The note head opposite to the first head.
172 Stem::last_head (Grob*me)
174 Direction d = get_direction (me);
177 return extremal_heads (me)[d];
181 START is part where stem reaches `last' head.
184 Stem::extremal_heads (Grob*me)
186 const int inf = 1000000;
187 Drul_array<int> extpos;
191 Drul_array<Grob *> exthead;
192 exthead[LEFT] = exthead[RIGHT] =0;
194 for (SCM s = me->get_grob_property ("note-heads"); gh_pair_p (s); s = ly_cdr (s))
196 Grob * n = unsmob_grob (ly_car (s));
199 int p = int (Staff_symbol_referencer::position_f (n));
203 if (d* p > d* extpos[d])
208 } while (flip (&d) != DOWN);
215 icmp (int const &a, int const &b)
221 Stem::note_head_positions (Grob *me)
224 for (SCM s = me->get_grob_property ("note-heads"); gh_pair_p (s); s = ly_cdr (s))
226 Grob * n = unsmob_grob (ly_car (s));
227 int p = int (Staff_symbol_referencer::position_f (n));
238 Stem::add_head (Grob*me, Grob *n)
240 n->set_grob_property ("stem", me->self_scm ());
241 n->add_dependency (me);
243 if (Note_head::has_interface (n))
245 Pointer_group_interface::add_grob (me, ly_symbol2scm ("note-heads"), n);
250 Stem::invisible_b (Grob*me)
252 return ! (head_count (me) && Note_head::balltype_i (support_head (me)) >= 1);
256 Stem::get_default_dir (Grob*me)
258 int staff_center = 0;
259 Interval hp = head_positions (me);
265 int udistance = (int) (UP * hp[UP] - staff_center);
266 int ddistance = (int) (DOWN* hp[DOWN] - staff_center);
268 if (sign (ddistance - udistance))
269 return Direction (sign (ddistance -udistance));
271 return to_dir (me->get_grob_property ("neutral-direction"));
275 Stem::get_default_stem_end_position (Grob*me)
277 SCM up_to_staff = me->get_grob_property ("up-to-staff");
278 if (to_boolean(up_to_staff))
280 int line_count = Staff_symbol_referencer::line_count (me);
282 Direction dir = get_direction (me);
284 return dir* (line_count + 1.5);
287 bool grace_b = to_boolean (me->get_grob_property ("grace"));
292 SCM scm_len = me->get_grob_property ("length");
293 if (gh_number_p (scm_len))
295 length_f = gh_scm2double (scm_len);
299 s = me->get_grob_property ("lengths");
300 for (SCM q = s; q != SCM_EOL; q = ly_cdr (q))
301 a.push (gh_scm2double (ly_car (q)));
303 // stem uses half-spaces
304 length_f = a[ ((duration_log (me) - 2) >? 0) <? (a.size () - 1)] * 2;
309 s = me->get_grob_property ("stem-shorten");
310 for (SCM q = s; gh_pair_p (q); q = ly_cdr (q))
311 a.push (gh_scm2double (ly_car (q)));
314 // stem uses half-spaces
316 // fixme: use scm_list_n_ref () iso. array[]
317 Real shorten_f = a[ ((duration_log (me) - 2) >? 0) <? (a.size () - 1)] * 2;
319 /* On boundary: shorten only half */
320 if (abs (chord_start_y (me)) == 0.5)
324 'set-default-stemlen' sets direction too
326 Direction dir = get_direction (me);
329 dir = get_default_dir (me);
330 Directional_element_interface::set (me, dir);
333 /* stems in unnatural (forced) direction should be shortened,
334 according to [Roush & Gourlay] */
335 if (chord_start_y (me)
336 && (get_direction (me) != get_default_dir (me)))
337 length_f -= shorten_f;
339 Interval hp = head_positions (me);
340 Real st = hp[dir] + dir * length_f;
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 (!beam_l (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::dots_l (closest_to_flag ) : 0;
361 Real dp = Staff_symbol_referencer::position_f (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->paper_l ()->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::position_f (heads[0]));
437 for (int i=1; i < heads.size (); i ++)
439 Real p = Staff_symbol_referencer::position_f (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->remove_grob_property ("molecule-callback");
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 return ly_interval2scm (iv);
526 /* TODO: rename flag-style into something more appropriate,
527 e.g. "stroke-style", maybe with values "" (i.e. no stroke),
528 "single" and "double". Needs more discussion.
530 String style, fstyle, staffline_offs;
531 SCM fst = me->get_grob_property ("flag-style");
532 if (gh_string_p (fst))
534 fstyle = ly_scm2string (fst);
537 SCM st = me->get_grob_property ("style");
538 if (gh_symbol_p (st))
540 style = (ly_scm2string (scm_symbol_to_string (st)));
546 bool adjust = to_boolean (me->get_grob_property ("adjust-if-on-staffline"));
548 if (String::compare_i (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 Grob *first = first_head(me);
583 int sz = Staff_symbol_referencer::line_count (me)-1;
584 int p = (int)rint (Staff_symbol_referencer::position_f (first));
585 staffline_offs = (((p ^ sz) & 0x1) == 0) ? "1" : "0";
589 staffline_offs = "2";
596 char c = (get_direction (me) == UP) ? 'u' : 'd';
598 = String ("flags-") + style + to_str (c) + staffline_offs + to_str (duration_log (me));
600 = Font_interface::get_default_font (me)->find_by_name (index_str);
601 if (!fstyle.empty_b ())
602 m.add_molecule (Font_interface::get_default_font (me)->find_by_name (String ("flags-") + to_str (c) + fstyle));
606 MAKE_SCHEME_CALLBACK (Stem,dim_callback,2);
608 Stem::dim_callback (SCM e, SCM ax)
610 Axis a = (Axis) gh_scm2int (ax);
611 assert (a == X_AXIS);
612 Grob *se = unsmob_grob (e);
614 if (unsmob_grob (se->get_grob_property ("beam")) || abs (duration_log (se)) <= 2)
618 r = flag (se).extent (X_AXIS);
620 return ly_interval2scm (r);
625 MAKE_SCHEME_CALLBACK (Stem,brew_molecule,1);
628 Stem::brew_molecule (SCM smob)
630 Grob*me = unsmob_grob (smob);
632 Direction d = get_direction (me);
639 This is required to avoid stems passing in tablature chords...
644 TODO: make the stem start a direction ?
647 if (to_boolean (me->get_grob_property ("avoid-note-head")))
649 y1 = Staff_symbol_referencer::position_f (last_head (me));
653 y1 = Staff_symbol_referencer::position_f (first_head (me));
656 Real y2 = stem_end_position (me);
658 Interval stem_y (y1 <? y2,y2 >? y1);
662 Real dy = Staff_symbol_referencer::staff_space (me) * 0.5;
664 if (Grob *hed = support_head (me))
667 must not take ledgers into account.
669 Interval head_height = Note_head::head_extent (hed,Y_AXIS);
670 Real y_attach = Note_head::stem_attachment_coordinate ( hed, Y_AXIS);
672 y_attach = head_height.linear_combination (y_attach);
673 stem_y[Direction (-d)] += d * y_attach/dy;
676 if (!invisible_b (me))
678 Real stem_width = gh_scm2double (me->get_grob_property ("thickness"))
680 * me->paper_l ()->get_var ("linethickness");
682 Molecule ss =Lookup::filledbox (Box (Interval (-stem_width/2, stem_width/2),
683 Interval (stem_y[DOWN]*dy, stem_y[UP]*dy)));
684 mol.add_molecule (ss);
687 if (!beam_l (me) && abs (duration_log (me)) > 2)
689 Molecule fl = flag (me);
690 fl.translate_axis (stem_y[d]*dy, Y_AXIS);
691 mol.add_molecule (fl);
694 return mol.smobbed_copy ();
698 move the stem to right of the notehead if it is up.
700 MAKE_SCHEME_CALLBACK (Stem,off_callback,2);
702 Stem::off_callback (SCM element_smob, SCM)
704 Grob *me = unsmob_grob (element_smob);
708 if (head_count (me) == 0)
710 return gh_double2scm (0.0);
713 if (Grob * f = first_head (me))
715 Interval head_wid = Note_head::head_extent(f, X_AXIS);
720 if (invisible_b (me))
725 attach = Note_head::stem_attachment_coordinate(f, X_AXIS);
727 Direction d = get_direction (me);
729 Real real_attach = head_wid.linear_combination (d * attach);
734 If not centered: correct for stem thickness.
739 = gh_scm2double (me->get_grob_property ("thickness"))
740 * me->paper_l ()->get_var ("linethickness");
743 r += - d * rule_thick * 0.5;
746 return gh_double2scm (r);
752 Stem::beam_l (Grob*me)
754 SCM b= me->get_grob_property ("beam");
755 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 = beam_l (me);
802 int multiplicity = Beam::get_multiplicity (beam);
803 Real interbeam_f = Beam::get_interbeam (beam);
805 Real thick = gh_scm2double (beam->get_grob_property ("thickness"));
807 Real ideal_y = chord_start_y (me);
809 /* from here on, calculate as if dir == UP */
812 SCM grace_prop = me->get_grob_property ("grace");
814 bool grace_b = to_boolean (grace_prop);
819 s = me->get_grob_property ("beamed-minimum-lengths");
821 for (SCM q = s; q != SCM_EOL; q = ly_cdr (q))
822 a.push (gh_scm2double (ly_car (q)));
825 Real minimum_length = a[multiplicity <? (a.size () - 1)] * staff_space;
826 s = me->get_grob_property ("beamed-lengths");
829 for (SCM q = s; q != SCM_EOL; q = ly_cdr (q))
830 a.push (gh_scm2double (ly_car (q)));
832 Real stem_length = a[multiplicity <? (a.size () - 1)] * staff_space;
834 Grob *fvs = Beam::first_visible_stem (beam);
837 Let's hope people don't use kneed tremolo beams.
839 Direction first_dir = fvs ? Directional_element_interface::get(fvs) : mydir;
841 // FIXME, hairy. see beam::calc_stem_y, for knees it's not trival
842 // to calculate where secondary, ternary beams will go.
843 if (multiplicity && first_dir == mydir)
844 ideal_y += thick + (multiplicity - 1) * interbeam_f;
846 ideal_y += stem_length;
849 Real shortest_y = ideal_y -stem_length + minimum_length;
852 lowest beam of (UP) beam must never be lower than second staffline
854 Hmm, reference (Wanske?)
856 Although this (additional) rule is probably correct,
857 I expect that highest beam (UP) should also never be lower
858 than middle staffline, just as normal stems.
861 bool no_extend_b = to_boolean (me->get_grob_property ("no-stem-extend"));
862 if (!grace_b && !no_extend_b)
864 /* highest beam of (UP) beam must never be lower than middle
866 lowest beam of (UP) beam must never be lower than second staffline
870 >? (- 2 * half_space - thick
871 + (multiplicity > 0) * thick
872 + interbeam_f * (multiplicity - 1));
876 ideal_y = ideal_y >? shortest_y;
878 s = beam->get_grob_property ("shorten");
880 ideal_y -= gh_scm2double (s);
882 Grob *common = me->common_refpoint (beam, Y_AXIS);
885 UGH -> THIS CAUSES ASYMETRY: the same beam can start/end on
888 TODO: the beam calculation should probably also use
889 relative_coordinate() for the Y positions of all beams.
893 Real interstaff_f = mydir *
894 (me->relative_coordinate (common, Y_AXIS)
895 - beam->relative_coordinate (common, Y_AXIS));
897 ideal_y += interstaff_f;
898 shortest_y += interstaff_f;
903 me->set_grob_property ("stem-info",
904 scm_list_n (gh_double2scm (ideal_y),
905 gh_double2scm (shortest_y),
910 si.shortest_y_ = shortest_y;
911 si.ideal_y_ = ideal_y;
916 ADD_INTERFACE (Stem,"stem-interface",
918 "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");