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 + 3.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 ?
649 if (to_boolean (me->get_grob_property ("avoid-note-head")))
651 Grob * lh = last_head (me);
654 y1 = Staff_symbol_referencer::position_f (lh);
658 Grob * lh = first_head (me);
661 y1 = Staff_symbol_referencer::position_f (lh);
664 Real y2 = stem_end_position (me);
666 Interval stem_y (y1 <? y2,y2 >? y1);
670 Real dy = Staff_symbol_referencer::staff_space (me) * 0.5;
672 if (Grob *hed = support_head (me))
675 must not take ledgers into account.
677 Interval head_height = Note_head::head_extent (hed,Y_AXIS);
678 Real y_attach = Note_head::stem_attachment_coordinate ( hed, Y_AXIS);
680 y_attach = head_height.linear_combination (y_attach);
681 stem_y[Direction (-d)] += d * y_attach/dy;
684 if (!invisible_b (me))
686 Real stem_width = gh_scm2double (me->get_grob_property ("thickness"))
688 * me->paper_l ()->get_var ("linethickness");
690 Molecule ss =Lookup::filledbox (Box (Interval (-stem_width/2, stem_width/2),
691 Interval (stem_y[DOWN]*dy, stem_y[UP]*dy)));
692 mol.add_molecule (ss);
695 if (!beam_l (me) && abs (duration_log (me)) > 2)
697 Molecule fl = flag (me);
698 fl.translate_axis (stem_y[d]*dy, Y_AXIS);
699 mol.add_molecule (fl);
702 return mol.smobbed_copy ();
706 move the stem to right of the notehead if it is up.
708 MAKE_SCHEME_CALLBACK (Stem,off_callback,2);
710 Stem::off_callback (SCM element_smob, SCM)
712 Grob *me = unsmob_grob (element_smob);
716 if (head_count (me) == 0)
718 return gh_double2scm (0.0);
721 if (Grob * f = first_head (me))
723 Interval head_wid = Note_head::head_extent(f, X_AXIS);
728 if (invisible_b (me))
733 attach = Note_head::stem_attachment_coordinate(f, X_AXIS);
735 Direction d = get_direction (me);
737 Real real_attach = head_wid.linear_combination (d * attach);
742 If not centered: correct for stem thickness.
747 = gh_scm2double (me->get_grob_property ("thickness"))
748 * me->paper_l ()->get_var ("linethickness");
751 r += - d * rule_thick * 0.5;
754 return gh_double2scm (r);
760 Stem::beam_l (Grob*me)
762 SCM b= me->get_grob_property ("beam");
763 return unsmob_grob (b);
767 // ugh still very long.
769 Stem::calc_stem_info (Grob*me)
771 SCM up_to_staff = me->get_grob_property ("up-to-staff");
772 if (gh_scm2bool(up_to_staff)) {
774 // Up-to-staff : the stem end out of the staff.
777 FIXME: duplicate code.
779 int line_count = Staff_symbol_referencer::line_count (me);
783 Direction dir = get_direction (me);
785 si.ideal_y_ = dir* (line_count + 1.5);
787 si.shortest_y_ = si.ideal_y_;
792 SCM scm_info = me->get_grob_property ("stem-info");
794 if (gh_pair_p (scm_info ))
798 si.dir_ = Directional_element_interface::get(me);
799 si.ideal_y_ = gh_scm2double (gh_car (scm_info));
800 si.shortest_y_ = gh_scm2double (gh_cadr (scm_info));
805 Direction mydir = Directional_element_interface::get (me);
806 Real staff_space = Staff_symbol_referencer::staff_space (me);
807 Real half_space = staff_space / 2;
809 Grob * beam = beam_l (me);
810 int multiplicity = Beam::get_multiplicity (beam);
811 Real interbeam_f = Beam::get_interbeam (beam);
813 Real thick = gh_scm2double (beam->get_grob_property ("thickness"));
815 Real ideal_y = chord_start_y (me);
817 /* from here on, calculate as if dir == UP */
820 SCM grace_prop = me->get_grob_property ("grace");
822 bool grace_b = to_boolean (grace_prop);
827 s = me->get_grob_property ("beamed-minimum-lengths");
829 for (SCM q = s; q != SCM_EOL; q = ly_cdr (q))
830 a.push (gh_scm2double (ly_car (q)));
833 Real minimum_length = a[multiplicity <? (a.size () - 1)] * staff_space;
834 s = me->get_grob_property ("beamed-lengths");
837 for (SCM q = s; q != SCM_EOL; q = ly_cdr (q))
838 a.push (gh_scm2double (ly_car (q)));
840 Real stem_length = a[multiplicity <? (a.size () - 1)] * staff_space;
842 Grob *fvs = Beam::first_visible_stem (beam);
845 Let's hope people don't use kneed tremolo beams.
847 Direction first_dir = fvs ? Directional_element_interface::get(fvs) : mydir;
849 // FIXME, hairy. see beam::calc_stem_y, for knees it's not trival
850 // to calculate where secondary, ternary beams will go.
851 if (multiplicity && first_dir == mydir)
852 ideal_y += thick + (multiplicity - 1) * interbeam_f;
854 ideal_y += stem_length;
857 Real shortest_y = ideal_y -stem_length + minimum_length;
860 lowest beam of (UP) beam must never be lower than second staffline
862 Hmm, reference (Wanske?)
864 Although this (additional) rule is probably correct,
865 I expect that highest beam (UP) should also never be lower
866 than middle staffline, just as normal stems.
869 bool no_extend_b = to_boolean (me->get_grob_property ("no-stem-extend"));
870 if (!grace_b && !no_extend_b)
872 /* highest beam of (UP) beam must never be lower than middle
874 lowest beam of (UP) beam must never be lower than second staffline
878 >? (- 2 * half_space - thick
879 + (multiplicity > 0) * thick
880 + interbeam_f * (multiplicity - 1));
884 ideal_y = ideal_y >? shortest_y;
886 s = beam->get_grob_property ("shorten");
888 ideal_y -= gh_scm2double (s);
890 Grob *common = me->common_refpoint (beam, Y_AXIS);
893 UGH -> THIS CAUSES ASYMETRY: the same beam can start/end on
896 TODO: the beam calculation should probably also use
897 relative_coordinate() for the Y positions of all beams.
901 Real interstaff_f = mydir *
902 (me->relative_coordinate (common, Y_AXIS)
903 - beam->relative_coordinate (common, Y_AXIS));
905 ideal_y += interstaff_f;
906 shortest_y += interstaff_f;
911 me->set_grob_property ("stem-info",
912 scm_list_n (gh_double2scm (ideal_y),
913 gh_double2scm (shortest_y),
918 si.shortest_y_ = shortest_y;
919 si.ideal_y_ = ideal_y;
924 ADD_INTERFACE (Stem,"stem-interface",
926 "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");