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::position_f (e[DOWN]),
69 Staff_symbol_referencer::position_f (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::position_f (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::position_f (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::balltype_i (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");
297 for (SCM q = s; q != SCM_EOL; q = ly_cdr (q))
298 a.push (gh_scm2double (ly_car (q)));
300 // stem uses half-spaces
301 length_f = a[ ((duration_log (me) - 2) >? 0) <? (a.size () - 1)] * 2;
306 s = me->get_grob_property ("stem-shorten");
307 for (SCM q = s; gh_pair_p (q); q = ly_cdr (q))
308 a.push (gh_scm2double (ly_car (q)));
311 // stem uses half-spaces
313 // fixme: use scm_list_n_ref () iso. array[]
314 Real shorten_f = a[ ((duration_log (me) - 2) >? 0) <? (a.size () - 1)] * 2;
316 /* On boundary: shorten only half */
317 if (abs (chord_start_y (me)) == 0.5)
321 'set-default-stemlen' sets direction too
323 Direction dir = get_direction (me);
326 dir = get_default_dir (me);
327 Directional_element_interface::set (me, dir);
330 /* stems in unnatural (forced) direction should be shortened,
331 according to [Roush & Gourlay] */
332 if (chord_start_y (me)
333 && (get_direction (me) != get_default_dir (me)))
334 length_f -= shorten_f;
336 Interval hp = head_positions (me);
337 Real st = hp[dir] + dir * length_f;
339 bool no_extend_b = to_boolean (me->get_grob_property ("no-stem-extend"));
340 if (!grace_b && !no_extend_b && dir * st < 0) // junkme?
344 Make a little room if we have a upflag and there is a dot.
345 previous approach was to lengthen the stem. This is not
346 good typesetting practice.
349 if (!beam_l (me) && dir == UP
350 && duration_log (me) > 2)
352 Grob * closest_to_flag = extremal_heads (me)[dir];
353 Grob * dots = closest_to_flag
354 ? Rhythmic_head::dots_l (closest_to_flag ) : 0;
358 Real dp = Staff_symbol_referencer::position_f (dots);
359 Real flagy = flag (me).extent (Y_AXIS)[-dir] * 2
360 / Staff_symbol_referencer::staff_space (me);
363 Very gory: add myself to the X-support of the parent,
364 which should be a dot-column.
366 if (dir * (st + flagy - dp) < 0.5)
368 Grob *par = dots->get_parent (X_AXIS);
370 if (Dot_column::has_interface (par))
372 Side_position_interface::add_support (par, me);
375 TODO: apply some better logic here. The flag is
376 curved inwards, so this will typically be too
392 the log of the duration (Number of hooks on the flag minus two)
395 Stem::duration_log (Grob*me)
397 SCM s = me->get_grob_property ("duration-log");
398 return (gh_number_p (s)) ? gh_scm2int (s) : 2;
402 Stem::position_noteheads (Grob*me)
404 if (!head_count (me))
407 Link_array<Grob> heads =
408 Pointer_group_interface__extract_grobs (me, (Grob*)0, "note-heads");
410 heads.sort (compare_position);
411 Direction dir =get_direction (me);
417 bool invisible = invisible_b (me);
420 thick = gh_scm2double (me->get_grob_property ("thickness"))
421 * me->paper_l ()->get_var ("linethickness");
424 Grob *hed = support_head (me);
425 Real w = Note_head::head_extent (hed,X_AXIS)[dir];
426 for (int i=0; i < heads.size (); i++)
428 heads[i]->translate_axis (w - Note_head::head_extent (heads[i],X_AXIS)[dir],
432 bool parity= true; // todo: make me settable.
433 int lastpos = int (Staff_symbol_referencer::position_f (heads[0]));
434 for (int i=1; i < heads.size (); i ++)
436 Real p = Staff_symbol_referencer::position_f (heads[i]);
437 int dy =abs (lastpos- (int)p);
443 Real l = Note_head::head_extent (heads[i], X_AXIS).length ();
445 Direction d = get_direction (me);
446 heads[i]->translate_axis (l * d, X_AXIS);
449 heads[i]->translate_axis (-thick *2* d , X_AXIS);
454 For some cases we should kern some more: when the
455 distance between the next or prev note is too large, we'd
456 get large white gaps, eg.
475 MAKE_SCHEME_CALLBACK (Stem,before_line_breaking,1);
477 Stem::before_line_breaking (SCM smob)
479 Grob*me = unsmob_grob (smob);
483 Do the calculations for visible stems, but also for invisible stems
484 with note heads (i.e. half notes.)
488 stem_end_position (me); // ugh. Trigger direction calc.
489 position_noteheads (me);
493 me->remove_grob_property ("molecule-callback");
496 return SCM_UNSPECIFIED;
501 When in a beam with tuplet brackets, brew_mol is called early,
502 caching a wrong value.
504 MAKE_SCHEME_CALLBACK (Stem, height, 2);
506 Stem::height (SCM smob, SCM ax)
508 Axis a = (Axis)gh_scm2int (ax);
509 Grob * me = unsmob_grob (smob);
510 assert (a == Y_AXIS);
512 SCM mol = me->get_uncached_molecule ();
515 iv = unsmob_molecule (mol)->extent (a);
516 return ly_interval2scm (iv);
523 /* TODO: rename flag-style into something more appropriate,
524 e.g. "stroke-style", maybe with values "" (i.e. no stroke),
525 "single" and "double". Needs more discussion.
527 String style, fstyle, staffline_offs;
528 SCM fst = me->get_grob_property ("flag-style");
529 if (gh_string_p (fst))
531 fstyle = ly_scm2string (fst);
534 SCM st = me->get_grob_property ("style");
535 if (gh_symbol_p (st))
537 style = (ly_scm2string (scm_symbol_to_string (st)));
543 bool adjust = to_boolean (me->get_grob_property ("adjust-if-on-staffline"));
545 if (String::compare_i (style, "mensural") == 0)
546 /* Mensural notation: For notes on staff lines, use different
547 flags than for notes between staff lines. The idea is that
548 flags are always vertically aligned with the staff lines,
549 regardless if the note head is on a staff line or between two
550 staff lines. In other words, the inner end of a flag always
551 touches a staff line.
556 /* Urrgh! We have to detect wether this stem ends on a staff
557 line or between two staff lines. But we can not call
558 stem_end_position(me) or get_default_stem_end_position(me),
559 since this encounters the flag and hence results in an
560 infinite recursion. However, in pure mensural notation,
561 there are no multiple note heads attached to a single stem,
562 neither is there usually need for using the stem_shorten
563 property (except for 32th and 64th notes, but that is not a
564 problem since the stem length in this case is augmented by
565 an integral multiple of staff_space). Hence, it should be
566 sufficient to just take the first note head, assume it's
567 the only one, look if it's on a staff line, and select the
568 flag's shape accordingly. In the worst case, the shape
569 looks slightly misplaced, but that will usually be the
570 programmer's fault (e.g. when trying to attach multiple
571 note heads to a single stem in mensural notation). */
574 perhaps the detection whether this correction is needed should
575 happen in a different place to avoid the recursion.
579 Grob *first = first_head(me);
580 int sz = Staff_symbol_referencer::line_count (me)-1;
581 int p = (int)rint (Staff_symbol_referencer::position_f (first));
582 staffline_offs = (((p ^ sz) & 0x1) == 0) ? "1" : "0";
586 staffline_offs = "2";
593 char c = (get_direction (me) == UP) ? 'u' : 'd';
595 = String ("flags-") + style + to_str (c) + staffline_offs + to_str (duration_log (me));
597 = Font_interface::get_default_font (me)->find_by_name (index_str);
598 if (!fstyle.empty_b ())
599 m.add_molecule (Font_interface::get_default_font (me)->find_by_name (String ("flags-") + to_str (c) + fstyle));
603 MAKE_SCHEME_CALLBACK (Stem,dim_callback,2);
605 Stem::dim_callback (SCM e, SCM ax)
607 Axis a = (Axis) gh_scm2int (ax);
608 assert (a == X_AXIS);
609 Grob *se = unsmob_grob (e);
611 if (unsmob_grob (se->get_grob_property ("beam")) || abs (duration_log (se)) <= 2)
615 r = flag (se).extent (X_AXIS);
617 return ly_interval2scm (r);
622 MAKE_SCHEME_CALLBACK (Stem,brew_molecule,1);
625 Stem::brew_molecule (SCM smob)
627 Grob*me = unsmob_grob (smob);
629 Direction d = get_direction (me);
636 This is required to avoid stems passing in tablature chords...
641 TODO: make the stem start a direction ?
646 if (to_boolean (me->get_grob_property ("avoid-note-head")))
648 Grob * lh = last_head (me);
651 y1 = Staff_symbol_referencer::position_f (lh);
655 Grob * lh = first_head (me);
658 y1 = Staff_symbol_referencer::position_f (lh);
661 Real y2 = stem_end_position (me);
663 Interval stem_y (y1 <? y2,y2 >? y1);
667 Real dy = Staff_symbol_referencer::staff_space (me) * 0.5;
669 if (Grob *hed = support_head (me))
672 must not take ledgers into account.
674 Interval head_height = Note_head::head_extent (hed,Y_AXIS);
675 Real y_attach = Note_head::stem_attachment_coordinate ( hed, Y_AXIS);
677 y_attach = head_height.linear_combination (y_attach);
678 stem_y[Direction (-d)] += d * y_attach/dy;
681 if (!invisible_b (me))
683 Real stem_width = gh_scm2double (me->get_grob_property ("thickness"))
685 * me->paper_l ()->get_var ("linethickness");
687 Molecule ss =Lookup::filledbox (Box (Interval (-stem_width/2, stem_width/2),
688 Interval (stem_y[DOWN]*dy, stem_y[UP]*dy)));
689 mol.add_molecule (ss);
692 if (!beam_l (me) && abs (duration_log (me)) > 2)
694 Molecule fl = flag (me);
695 fl.translate_axis (stem_y[d]*dy, Y_AXIS);
696 mol.add_molecule (fl);
699 return mol.smobbed_copy ();
703 move the stem to right of the notehead if it is up.
705 MAKE_SCHEME_CALLBACK (Stem,off_callback,2);
707 Stem::off_callback (SCM element_smob, SCM)
709 Grob *me = unsmob_grob (element_smob);
713 if (head_count (me) == 0)
715 return gh_double2scm (0.0);
718 if (Grob * f = first_head (me))
720 Interval head_wid = Note_head::head_extent(f, X_AXIS);
725 if (invisible_b (me))
730 attach = Note_head::stem_attachment_coordinate(f, X_AXIS);
732 Direction d = get_direction (me);
734 Real real_attach = head_wid.linear_combination (d * attach);
739 If not centered: correct for stem thickness.
744 = gh_scm2double (me->get_grob_property ("thickness"))
745 * me->paper_l ()->get_var ("linethickness");
748 r += - d * rule_thick * 0.5;
751 return gh_double2scm (r);
757 Stem::beam_l (Grob*me)
759 SCM b= me->get_grob_property ("beam");
760 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 = beam_l (me);
807 int beam_count = Beam::get_beam_count (beam);
808 Real beam_space_f = Beam::get_beam_space (beam);
810 Real thick = gh_scm2double (beam->get_grob_property ("thickness"));
812 Real ideal_y = chord_start_y (me);
814 /* from here on, calculate as if dir == UP */
817 SCM grace_prop = me->get_grob_property ("grace");
819 bool grace_b = to_boolean (grace_prop);
824 s = me->get_grob_property ("beamed-minimum-lengths");
826 for (SCM q = s; q != SCM_EOL; q = ly_cdr (q))
827 a.push (gh_scm2double (ly_car (q)));
830 Real minimum_length = a[beam_count <? (a.size () - 1)] * staff_space;
831 s = me->get_grob_property ("beamed-lengths");
834 for (SCM q = s; q != SCM_EOL; q = ly_cdr (q))
835 a.push (gh_scm2double (ly_car (q)));
837 Real stem_length = a[beam_count <? (a.size () - 1)] * staff_space;
839 Grob *fvs = Beam::first_visible_stem (beam);
842 Let's hope people don't use kneed tremolo beams.
844 Direction first_dir = fvs ? Directional_element_interface::get(fvs) : mydir;
846 // FIXME, hairy. see beam::calc_stem_y, for knees it's not trival
847 // to calculate where secondary, ternary beams will go.
848 if (beam_count && first_dir == mydir)
849 ideal_y += thick + (beam_count - 1) * beam_space_f;
851 ideal_y += stem_length;
854 Real shortest_y = ideal_y -stem_length + minimum_length;
857 lowest beam of (UP) beam must never be lower than second staffline
859 Hmm, reference (Wanske?)
861 Although this (additional) rule is probably correct,
862 I expect that highest beam (UP) should also never be lower
863 than middle staffline, just as normal stems.
866 bool no_extend_b = to_boolean (me->get_grob_property ("no-stem-extend"));
867 if (!grace_b && !no_extend_b)
869 /* highest beam of (UP) beam must never be lower than middle
871 lowest beam of (UP) beam must never be lower than second staffline
875 >? (- 2 * half_space - thick
876 + (beam_count > 0) * thick
877 + beam_space_f * (beam_count - 1));
881 ideal_y = ideal_y >? shortest_y;
883 s = beam->get_grob_property ("shorten");
885 ideal_y -= gh_scm2double (s);
888 Grob *common = me->common_refpoint (beam, Y_AXIS);
891 UGH -> THIS CAUSES ASYMETRY: the same beam can start/end on
894 TODO: the beam calculation should probably also use
895 relative_coordinate() for the Y positions of all beams.
899 Real interstaff_f = mydir *
900 (me->relative_coordinate (common, Y_AXIS)
901 - beam->relative_coordinate (common, Y_AXIS));
903 ideal_y += interstaff_f;
904 shortest_y += interstaff_f;
910 me->set_grob_property ("stem-info",
911 scm_list_n (gh_double2scm (ideal_y),
912 gh_double2scm (shortest_y),
917 si.shortest_y_ = shortest_y;
918 si.ideal_y_ = ideal_y;
923 ADD_INTERFACE (Stem,"stem-interface",
925 "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");