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 SCM up_to_staff = me->get_grob_property ("up-to-staff");
280 if (to_boolean(up_to_staff))
282 int line_count = Staff_symbol_referencer::line_count (me);
284 Direction dir = get_direction (me);
286 return dir* (line_count + 3.5);
289 bool grace_b = to_boolean (me->get_grob_property ("grace"));
294 SCM scm_len = me->get_grob_property ("length");
295 if (gh_number_p (scm_len))
297 length_f = gh_scm2double (scm_len);
301 s = me->get_grob_property ("lengths");
304 length_f = 2* gh_scm2double (robust_list_ref (duration_log(me) -2, s));
309 Real shorten_f = 0.0;
311 SCM sshorten = me->get_grob_property ("stem-shorten");
312 if (gh_pair_p (sshorten))
314 shorten_f = 2* gh_scm2double (robust_list_ref ((duration_log (me) - 2) >? 0, sshorten));
317 /* On boundary: shorten only half */
318 if (abs (chord_start_y (me)) == 0.5)
322 'set-default-stemlen' sets direction too
324 Direction dir = get_direction (me);
327 dir = get_default_dir (me);
328 Directional_element_interface::set (me, dir);
331 /* stems in unnatural (forced) direction should be shortened,
332 according to [Roush & Gourlay] */
333 if (chord_start_y (me)
334 && (get_direction (me) != get_default_dir (me)))
335 length_f -= shorten_f;
337 Interval hp = head_positions (me);
338 Real st = hp[dir] + dir * length_f;
340 bool no_extend_b = to_boolean (me->get_grob_property ("no-stem-extend"));
341 if (!grace_b && !no_extend_b && dir * st < 0) // junkme?
345 Make a little room if we have a upflag and there is a dot.
346 previous approach was to lengthen the stem. This is not
347 good typesetting practice.
350 if (!get_beam (me) && dir == UP
351 && duration_log (me) > 2)
353 Grob * closest_to_flag = extremal_heads (me)[dir];
354 Grob * dots = closest_to_flag
355 ? Rhythmic_head::get_dots (closest_to_flag ) : 0;
359 Real dp = Staff_symbol_referencer::get_position (dots);
360 Real flagy = flag (me).extent (Y_AXIS)[-dir] * 2
361 / Staff_symbol_referencer::staff_space (me);
364 Very gory: add myself to the X-support of the parent,
365 which should be a dot-column.
367 if (dir * (st + flagy - dp) < 0.5)
369 Grob *par = dots->get_parent (X_AXIS);
371 if (Dot_column::has_interface (par))
373 Side_position_interface::add_support (par, me);
376 TODO: apply some better logic here. The flag is
377 curved inwards, so this will typically be too
393 the log of the duration (Number of hooks on the flag minus two)
396 Stem::duration_log (Grob*me)
398 SCM s = me->get_grob_property ("duration-log");
399 return (gh_number_p (s)) ? gh_scm2int (s) : 2;
403 Stem::position_noteheads (Grob*me)
405 if (!head_count (me))
408 Link_array<Grob> heads =
409 Pointer_group_interface__extract_grobs (me, (Grob*)0, "note-heads");
411 heads.sort (compare_position);
412 Direction dir =get_direction (me);
418 bool invisible = invisible_b (me);
421 thick = gh_scm2double (me->get_grob_property ("thickness"))
422 * me->get_paper ()->get_var ("linethickness");
425 Grob *hed = support_head (me);
426 Real w = Note_head::head_extent (hed,X_AXIS)[dir];
427 for (int i=0; i < heads.size (); i++)
429 heads[i]->translate_axis (w - Note_head::head_extent (heads[i],X_AXIS)[dir],
433 bool parity= true; // todo: make me settable.
434 int lastpos = int (Staff_symbol_referencer::get_position (heads[0]));
435 for (int i=1; i < heads.size (); i ++)
437 Real p = Staff_symbol_referencer::get_position (heads[i]);
438 int dy =abs (lastpos- (int)p);
444 Real l = Note_head::head_extent (heads[i], X_AXIS).length ();
446 Direction d = get_direction (me);
447 heads[i]->translate_axis (l * d, X_AXIS);
450 heads[i]->translate_axis (-thick *2* d , X_AXIS);
455 For some cases we should kern some more: when the
456 distance between the next or prev note is too large, we'd
457 get large white gaps, eg.
476 MAKE_SCHEME_CALLBACK (Stem,before_line_breaking,1);
478 Stem::before_line_breaking (SCM smob)
480 Grob*me = unsmob_grob (smob);
484 Do the calculations for visible stems, but also for invisible stems
485 with note heads (i.e. half notes.)
489 stem_end_position (me); // ugh. Trigger direction calc.
490 position_noteheads (me);
494 me->remove_grob_property ("molecule-callback");
497 return SCM_UNSPECIFIED;
502 When in a beam with tuplet brackets, brew_mol is called early,
503 caching a wrong value.
505 MAKE_SCHEME_CALLBACK (Stem, height, 2);
507 Stem::height (SCM smob, SCM ax)
509 Axis a = (Axis)gh_scm2int (ax);
510 Grob * me = unsmob_grob (smob);
511 assert (a == Y_AXIS);
513 SCM mol = me->get_uncached_molecule ();
516 iv = unsmob_molecule (mol)->extent (a);
517 return ly_interval2scm (iv);
524 /* TODO: rename flag-style into something more appropriate,
525 e.g. "stroke-style", maybe with values "" (i.e. no stroke),
526 "single" and "double". Needs more discussion.
528 String style, fstyle, staffline_offs;
529 SCM fst = me->get_grob_property ("flag-style");
530 if (gh_string_p (fst))
532 fstyle = ly_scm2string (fst);
535 SCM st = me->get_grob_property ("style");
536 if (gh_symbol_p (st))
538 style = (ly_scm2string (scm_symbol_to_string (st)));
544 bool adjust = to_boolean (me->get_grob_property ("adjust-if-on-staffline"));
546 if (String::compare (style, "mensural") == 0)
547 /* Mensural notation: For notes on staff lines, use different
548 flags than for notes between staff lines. The idea is that
549 flags are always vertically aligned with the staff lines,
550 regardless if the note head is on a staff line or between two
551 staff lines. In other words, the inner end of a flag always
552 touches a staff line.
557 /* Urrgh! We have to detect wether this stem ends on a staff
558 line or between two staff lines. But we can not call
559 stem_end_position(me) or get_default_stem_end_position(me),
560 since this encounters the flag and hence results in an
561 infinite recursion. However, in pure mensural notation,
562 there are no multiple note heads attached to a single stem,
563 neither is there usually need for using the stem_shorten
564 property (except for 32th and 64th notes, but that is not a
565 problem since the stem length in this case is augmented by
566 an integral multiple of staff_space). Hence, it should be
567 sufficient to just take the first note head, assume it's
568 the only one, look if it's on a staff line, and select the
569 flag's shape accordingly. In the worst case, the shape
570 looks slightly misplaced, but that will usually be the
571 programmer's fault (e.g. when trying to attach multiple
572 note heads to a single stem in mensural notation). */
575 perhaps the detection whether this correction is needed should
576 happen in a different place to avoid the recursion.
580 Grob *first = first_head(me);
581 int sz = Staff_symbol_referencer::line_count (me)-1;
582 int p = (int)rint (Staff_symbol_referencer::get_position (first));
583 staffline_offs = (((p ^ sz) & 0x1) == 0) ? "1" : "0";
587 staffline_offs = "2";
594 char c = (get_direction (me) == UP) ? 'u' : 'd';
596 = String ("flags-") + style + to_string (c) + staffline_offs + to_string (duration_log (me));
598 = Font_interface::get_default_font (me)->find_by_name (index_string);
599 if (!fstyle.empty_b ())
600 m.add_molecule (Font_interface::get_default_font (me)->find_by_name (String ("flags-") + to_string (c) + fstyle));
604 MAKE_SCHEME_CALLBACK (Stem,dim_callback,2);
606 Stem::dim_callback (SCM e, SCM ax)
608 Axis a = (Axis) gh_scm2int (ax);
609 assert (a == X_AXIS);
610 Grob *se = unsmob_grob (e);
612 if (unsmob_grob (se->get_grob_property ("beam")) || abs (duration_log (se)) <= 2)
616 r = flag (se).extent (X_AXIS);
618 return ly_interval2scm (r);
623 MAKE_SCHEME_CALLBACK (Stem,brew_molecule,1);
626 Stem::brew_molecule (SCM smob)
628 Grob*me = unsmob_grob (smob);
630 Direction d = get_direction (me);
637 This is required to avoid stems passing in tablature chords...
642 TODO: make the stem start a direction ?
647 if (to_boolean (me->get_grob_property ("avoid-note-head")))
649 Grob * lh = last_head (me);
652 y1 = Staff_symbol_referencer::get_position (lh);
656 Grob * lh = first_head (me);
659 y1 = Staff_symbol_referencer::get_position (lh);
662 Real y2 = stem_end_position (me);
664 Interval stem_y (y1 <? y2,y2 >? y1);
668 Real dy = Staff_symbol_referencer::staff_space (me) * 0.5;
670 if (Grob *hed = support_head (me))
673 must not take ledgers into account.
675 Interval head_height = Note_head::head_extent (hed,Y_AXIS);
676 Real y_attach = Note_head::stem_attachment_coordinate ( hed, Y_AXIS);
678 y_attach = head_height.linear_combination (y_attach);
679 stem_y[Direction (-d)] += d * y_attach/dy;
682 if (!invisible_b (me))
684 Real stem_width = gh_scm2double (me->get_grob_property ("thickness"))
686 * me->get_paper ()->get_var ("linethickness");
688 Molecule ss =Lookup::filledbox (Box (Interval (-stem_width/2, stem_width/2),
689 Interval (stem_y[DOWN]*dy, stem_y[UP]*dy)));
690 mol.add_molecule (ss);
693 if (!get_beam (me) && abs (duration_log (me)) > 2)
695 Molecule fl = flag (me);
696 fl.translate_axis (stem_y[d]*dy, Y_AXIS);
697 mol.add_molecule (fl);
700 return mol.smobbed_copy ();
704 move the stem to right of the notehead if it is up.
706 MAKE_SCHEME_CALLBACK (Stem,off_callback,2);
708 Stem::off_callback (SCM element_smob, SCM)
710 Grob *me = unsmob_grob (element_smob);
714 if (head_count (me) == 0)
716 return gh_double2scm (0.0);
719 if (Grob * f = first_head (me))
721 Interval head_wid = Note_head::head_extent(f, X_AXIS);
726 if (invisible_b (me))
731 attach = Note_head::stem_attachment_coordinate(f, X_AXIS);
733 Direction d = get_direction (me);
735 Real real_attach = head_wid.linear_combination (d * attach);
740 If not centered: correct for stem thickness.
745 = gh_scm2double (me->get_grob_property ("thickness"))
746 * me->get_paper ()->get_var ("linethickness");
749 r += - d * rule_thick * 0.5;
752 return gh_double2scm (r);
758 Stem::get_beam (Grob*me)
760 SCM b= me->get_grob_property ("beam");
761 return unsmob_grob (b);
764 // ugh still very long.
766 Stem::calc_stem_info (Grob*me)
768 SCM up_to_staff = me->get_grob_property ("up-to-staff");
769 if (gh_scm2bool(up_to_staff)) {
771 // Up-to-staff : the stem end out of the staff.
774 FIXME: duplicate code.
776 int line_count = Staff_symbol_referencer::line_count (me);
780 Direction dir = get_direction (me);
782 si.ideal_y_ = dir* (line_count + 1.5);
784 si.shortest_y_ = si.ideal_y_;
789 SCM scm_info = me->get_grob_property ("stem-info");
791 if (gh_pair_p (scm_info ))
795 si.dir_ = Directional_element_interface::get(me);
796 si.ideal_y_ = gh_scm2double (gh_car (scm_info));
797 si.shortest_y_ = gh_scm2double (gh_cadr (scm_info));
802 Direction mydir = Directional_element_interface::get (me);
803 Real staff_space = Staff_symbol_referencer::staff_space (me);
804 Real half_space = staff_space / 2;
806 Grob * beam = get_beam (me);
807 int beam_count = beam_multiplicity(me).length()+1;
808 Real beam_translation= Beam::get_beam_translation (beam);
809 Real thick = gh_scm2double (beam->get_grob_property ("thickness"));
810 Real note_start = chord_start_y (me);
812 /* from here on, calculate as if dir == UP */
815 SCM grace_prop = me->get_grob_property ("grace");
817 bool grace_b = to_boolean (grace_prop);
818 SCM bml = robust_list_ref ( beam_count ,
819 me->get_grob_property ("beamed-minimum-lengths"));
821 Real minimum_length = gh_scm2double(bml)*staff_space;
822 SCM bl = robust_list_ref ( beam_count ,
823 me->get_grob_property ("beamed-lengths"));
824 Real stem_length = gh_scm2double(bl) * staff_space;
828 stem goes to center of beam, hence 0.5
830 Real beam_lengthen = beam_translation* (beam_count - 1)
831 + ((beam_count > 0) ? thick : 0) - 0.5 * thick;
833 Real shortest_y = note_start + minimum_length + beam_lengthen;
834 Real ideal_y = stem_length + note_start + beam_lengthen;
837 lowest beam of (UP) beam must never be lower than second staffline
839 Hmm, reference (Wanske?)
841 Although this (additional) rule is probably correct,
842 I expect that highest beam (UP) should also never be lower
843 than middle staffline, just as normal stems.
845 Add: not for knees. Not sure if that's is a good thing.
847 bool no_extend_b = to_boolean (me->get_grob_property ("no-stem-extend"));
848 bool knee_b = to_boolean (beam->get_grob_property ("knee"));
849 if (!grace_b && !no_extend_b && !knee_b)
851 /* highest beam of (UP) beam must never be lower than middle
853 lowest beam of (UP) beam must never be lower than second staffline
857 >? (- 2 * half_space - thick + beam_lengthen);
861 // ideal_y = ideal_y >? shortest_y;
862 SCM s = beam->get_grob_property ("shorten");
864 ideal_y -= gh_scm2double (s);
870 me->set_grob_property ("stem-info",
871 scm_list_n (gh_double2scm (ideal_y),
872 gh_double2scm (shortest_y),
877 si.shortest_y_ = shortest_y;
878 si.ideal_y_ = ideal_y;
886 Stem::beam_multiplicity (Grob *stem)
888 SCM beaming= stem->get_grob_property ("beaming");
889 Slice l = int_list_to_slice (gh_car (beaming));
890 Slice r = int_list_to_slice (gh_cdr (beaming));
897 ADD_INTERFACE (Stem,"stem-interface",
899 "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");