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");
301 length_f = 2* gh_scm2double (robust_list_ref (duration_log(me) -2, s));
306 Real shorten_f = 0.0;
308 SCM sshorten = me->get_grob_property ("stem-shorten");
309 if (gh_pair_p (sshorten))
311 shorten_f = 2* gh_scm2double (robust_list_ref ((duration_log (me) - 2) >? 0, sshorten));
314 /* On boundary: shorten only half */
315 if (abs (chord_start_y (me)) == 0.5)
319 'set-default-stemlen' sets direction too
321 Direction dir = get_direction (me);
324 dir = get_default_dir (me);
325 Directional_element_interface::set (me, dir);
328 /* stems in unnatural (forced) direction should be shortened,
329 according to [Roush & Gourlay] */
330 if (chord_start_y (me)
331 && (get_direction (me) != get_default_dir (me)))
332 length_f -= shorten_f;
334 Interval hp = head_positions (me);
335 Real st = hp[dir] + dir * length_f;
337 bool no_extend_b = to_boolean (me->get_grob_property ("no-stem-extend"));
338 if (!grace_b && !no_extend_b && dir * st < 0) // junkme?
342 Make a little room if we have a upflag and there is a dot.
343 previous approach was to lengthen the stem. This is not
344 good typesetting practice.
347 if (!beam_l (me) && dir == UP
348 && duration_log (me) > 2)
350 Grob * closest_to_flag = extremal_heads (me)[dir];
351 Grob * dots = closest_to_flag
352 ? Rhythmic_head::dots_l (closest_to_flag ) : 0;
356 Real dp = Staff_symbol_referencer::position_f (dots);
357 Real flagy = flag (me).extent (Y_AXIS)[-dir] * 2
358 / Staff_symbol_referencer::staff_space (me);
361 Very gory: add myself to the X-support of the parent,
362 which should be a dot-column.
364 if (dir * (st + flagy - dp) < 0.5)
366 Grob *par = dots->get_parent (X_AXIS);
368 if (Dot_column::has_interface (par))
370 Side_position_interface::add_support (par, me);
373 TODO: apply some better logic here. The flag is
374 curved inwards, so this will typically be too
390 the log of the duration (Number of hooks on the flag minus two)
393 Stem::duration_log (Grob*me)
395 SCM s = me->get_grob_property ("duration-log");
396 return (gh_number_p (s)) ? gh_scm2int (s) : 2;
400 Stem::position_noteheads (Grob*me)
402 if (!head_count (me))
405 Link_array<Grob> heads =
406 Pointer_group_interface__extract_grobs (me, (Grob*)0, "note-heads");
408 heads.sort (compare_position);
409 Direction dir =get_direction (me);
415 bool invisible = invisible_b (me);
418 thick = gh_scm2double (me->get_grob_property ("thickness"))
419 * me->paper_l ()->get_var ("linethickness");
422 Grob *hed = support_head (me);
423 Real w = Note_head::head_extent (hed,X_AXIS)[dir];
424 for (int i=0; i < heads.size (); i++)
426 heads[i]->translate_axis (w - Note_head::head_extent (heads[i],X_AXIS)[dir],
430 bool parity= true; // todo: make me settable.
431 int lastpos = int (Staff_symbol_referencer::position_f (heads[0]));
432 for (int i=1; i < heads.size (); i ++)
434 Real p = Staff_symbol_referencer::position_f (heads[i]);
435 int dy =abs (lastpos- (int)p);
441 Real l = Note_head::head_extent (heads[i], X_AXIS).length ();
443 Direction d = get_direction (me);
444 heads[i]->translate_axis (l * d, X_AXIS);
447 heads[i]->translate_axis (-thick *2* d , X_AXIS);
452 For some cases we should kern some more: when the
453 distance between the next or prev note is too large, we'd
454 get large white gaps, eg.
473 MAKE_SCHEME_CALLBACK (Stem,before_line_breaking,1);
475 Stem::before_line_breaking (SCM smob)
477 Grob*me = unsmob_grob (smob);
481 Do the calculations for visible stems, but also for invisible stems
482 with note heads (i.e. half notes.)
486 stem_end_position (me); // ugh. Trigger direction calc.
487 position_noteheads (me);
491 me->remove_grob_property ("molecule-callback");
494 return SCM_UNSPECIFIED;
499 When in a beam with tuplet brackets, brew_mol is called early,
500 caching a wrong value.
502 MAKE_SCHEME_CALLBACK (Stem, height, 2);
504 Stem::height (SCM smob, SCM ax)
506 Axis a = (Axis)gh_scm2int (ax);
507 Grob * me = unsmob_grob (smob);
508 assert (a == Y_AXIS);
510 SCM mol = me->get_uncached_molecule ();
513 iv = unsmob_molecule (mol)->extent (a);
514 return ly_interval2scm (iv);
521 /* TODO: rename flag-style into something more appropriate,
522 e.g. "stroke-style", maybe with values "" (i.e. no stroke),
523 "single" and "double". Needs more discussion.
525 String style, fstyle, staffline_offs;
526 SCM fst = me->get_grob_property ("flag-style");
527 if (gh_string_p (fst))
529 fstyle = ly_scm2string (fst);
532 SCM st = me->get_grob_property ("style");
533 if (gh_symbol_p (st))
535 style = (ly_scm2string (scm_symbol_to_string (st)));
541 bool adjust = to_boolean (me->get_grob_property ("adjust-if-on-staffline"));
543 if (String::compare_i (style, "mensural") == 0)
544 /* Mensural notation: For notes on staff lines, use different
545 flags than for notes between staff lines. The idea is that
546 flags are always vertically aligned with the staff lines,
547 regardless if the note head is on a staff line or between two
548 staff lines. In other words, the inner end of a flag always
549 touches a staff line.
554 /* Urrgh! We have to detect wether this stem ends on a staff
555 line or between two staff lines. But we can not call
556 stem_end_position(me) or get_default_stem_end_position(me),
557 since this encounters the flag and hence results in an
558 infinite recursion. However, in pure mensural notation,
559 there are no multiple note heads attached to a single stem,
560 neither is there usually need for using the stem_shorten
561 property (except for 32th and 64th notes, but that is not a
562 problem since the stem length in this case is augmented by
563 an integral multiple of staff_space). Hence, it should be
564 sufficient to just take the first note head, assume it's
565 the only one, look if it's on a staff line, and select the
566 flag's shape accordingly. In the worst case, the shape
567 looks slightly misplaced, but that will usually be the
568 programmer's fault (e.g. when trying to attach multiple
569 note heads to a single stem in mensural notation). */
572 perhaps the detection whether this correction is needed should
573 happen in a different place to avoid the recursion.
577 Grob *first = first_head(me);
578 int sz = Staff_symbol_referencer::line_count (me)-1;
579 int p = (int)rint (Staff_symbol_referencer::position_f (first));
580 staffline_offs = (((p ^ sz) & 0x1) == 0) ? "1" : "0";
584 staffline_offs = "2";
591 char c = (get_direction (me) == UP) ? 'u' : 'd';
593 = String ("flags-") + style + to_str (c) + staffline_offs + to_str (duration_log (me));
595 = Font_interface::get_default_font (me)->find_by_name (index_str);
596 if (!fstyle.empty_b ())
597 m.add_molecule (Font_interface::get_default_font (me)->find_by_name (String ("flags-") + to_str (c) + fstyle));
601 MAKE_SCHEME_CALLBACK (Stem,dim_callback,2);
603 Stem::dim_callback (SCM e, SCM ax)
605 Axis a = (Axis) gh_scm2int (ax);
606 assert (a == X_AXIS);
607 Grob *se = unsmob_grob (e);
609 if (unsmob_grob (se->get_grob_property ("beam")) || abs (duration_log (se)) <= 2)
613 r = flag (se).extent (X_AXIS);
615 return ly_interval2scm (r);
620 MAKE_SCHEME_CALLBACK (Stem,brew_molecule,1);
623 Stem::brew_molecule (SCM smob)
625 Grob*me = unsmob_grob (smob);
627 Direction d = get_direction (me);
634 This is required to avoid stems passing in tablature chords...
639 TODO: make the stem start a direction ?
644 if (to_boolean (me->get_grob_property ("avoid-note-head")))
646 Grob * lh = last_head (me);
649 y1 = Staff_symbol_referencer::position_f (lh);
653 Grob * lh = first_head (me);
656 y1 = Staff_symbol_referencer::position_f (lh);
659 Real y2 = stem_end_position (me);
661 Interval stem_y (y1 <? y2,y2 >? y1);
665 Real dy = Staff_symbol_referencer::staff_space (me) * 0.5;
667 if (Grob *hed = support_head (me))
670 must not take ledgers into account.
672 Interval head_height = Note_head::head_extent (hed,Y_AXIS);
673 Real y_attach = Note_head::stem_attachment_coordinate ( hed, Y_AXIS);
675 y_attach = head_height.linear_combination (y_attach);
676 stem_y[Direction (-d)] += d * y_attach/dy;
679 if (!invisible_b (me))
681 Real stem_width = gh_scm2double (me->get_grob_property ("thickness"))
683 * me->paper_l ()->get_var ("linethickness");
685 Molecule ss =Lookup::filledbox (Box (Interval (-stem_width/2, stem_width/2),
686 Interval (stem_y[DOWN]*dy, stem_y[UP]*dy)));
687 mol.add_molecule (ss);
690 if (!beam_l (me) && abs (duration_log (me)) > 2)
692 Molecule fl = flag (me);
693 fl.translate_axis (stem_y[d]*dy, Y_AXIS);
694 mol.add_molecule (fl);
697 return mol.smobbed_copy ();
701 move the stem to right of the notehead if it is up.
703 MAKE_SCHEME_CALLBACK (Stem,off_callback,2);
705 Stem::off_callback (SCM element_smob, SCM)
707 Grob *me = unsmob_grob (element_smob);
711 if (head_count (me) == 0)
713 return gh_double2scm (0.0);
716 if (Grob * f = first_head (me))
718 Interval head_wid = Note_head::head_extent(f, X_AXIS);
723 if (invisible_b (me))
728 attach = Note_head::stem_attachment_coordinate(f, X_AXIS);
730 Direction d = get_direction (me);
732 Real real_attach = head_wid.linear_combination (d * attach);
737 If not centered: correct for stem thickness.
742 = gh_scm2double (me->get_grob_property ("thickness"))
743 * me->paper_l ()->get_var ("linethickness");
746 r += - d * rule_thick * 0.5;
749 return gh_double2scm (r);
755 Stem::beam_l (Grob*me)
757 SCM b= me->get_grob_property ("beam");
758 return unsmob_grob (b);
761 // ugh still very long.
763 Stem::calc_stem_info (Grob*me)
765 SCM up_to_staff = me->get_grob_property ("up-to-staff");
766 if (gh_scm2bool(up_to_staff)) {
768 // Up-to-staff : the stem end out of the staff.
771 FIXME: duplicate code.
773 int line_count = Staff_symbol_referencer::line_count (me);
777 Direction dir = get_direction (me);
779 si.ideal_y_ = dir* (line_count + 1.5);
781 si.shortest_y_ = si.ideal_y_;
786 SCM scm_info = me->get_grob_property ("stem-info");
788 if (gh_pair_p (scm_info ))
792 si.dir_ = Directional_element_interface::get(me);
793 si.ideal_y_ = gh_scm2double (gh_car (scm_info));
794 si.shortest_y_ = gh_scm2double (gh_cadr (scm_info));
799 Direction mydir = Directional_element_interface::get (me);
800 Real staff_space = Staff_symbol_referencer::staff_space (me);
801 Real half_space = staff_space / 2;
803 Grob * beam = beam_l (me);
804 int beam_count = beam_multiplicity(me).length()+1;
805 Real beam_translation= Beam::get_beam_translation (beam);
806 Real thick = gh_scm2double (beam->get_grob_property ("thickness"));
807 Real note_start = 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);
815 SCM bml = robust_list_ref ( beam_count ,
816 me->get_grob_property ("beamed-minimum-lengths"));
818 Real minimum_length = gh_scm2double(bml)*staff_space;
819 SCM bl = robust_list_ref ( beam_count ,
820 me->get_grob_property ("beamed-lengths"));
821 Real stem_length = gh_scm2double(bl) * staff_space;
825 stem goes to center of beam, hence 0.5
827 Real beam_lengthen = beam_translation* (beam_count - 1)
828 + ((beam_count > 0) ? thick : 0) - 0.5 * thick;
830 Real shortest_y = note_start + minimum_length + beam_lengthen;
831 Real ideal_y = stem_length + note_start + beam_lengthen;
834 lowest beam of (UP) beam must never be lower than second staffline
836 Hmm, reference (Wanske?)
838 Although this (additional) rule is probably correct,
839 I expect that highest beam (UP) should also never be lower
840 than middle staffline, just as normal stems.
843 bool no_extend_b = to_boolean (me->get_grob_property ("no-stem-extend"));
844 if (!grace_b && !no_extend_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");