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 /* Tab notation feature: make stem end extend out of staff. */
280 SCM up_to_staff = me->get_grob_property ("up-to-staff");
281 if (to_boolean (up_to_staff))
283 int line_count = Staff_symbol_referencer::line_count (me);
284 Direction dir = get_direction (me);
285 return dir * (line_count + 3.5);
288 bool grace_b = to_boolean (me->get_grob_property ("grace"));
293 SCM scm_len = me->get_grob_property ("length");
294 if (gh_number_p (scm_len))
296 length_f = gh_scm2double (scm_len);
300 s = me->get_grob_property ("lengths");
303 length_f = 2* gh_scm2double (robust_list_ref (duration_log(me) -2, s));
307 Real shorten_f = 0.0;
309 SCM sshorten = me->get_grob_property ("stem-shorten");
310 if (gh_pair_p (sshorten))
312 shorten_f = 2* gh_scm2double (robust_list_ref ((duration_log (me) - 2) >? 0, sshorten));
315 /* On boundary: shorten only half */
316 if (abs (chord_start_y (me)) == 0.5)
320 'set-default-stemlen' sets direction too
322 Direction dir = get_direction (me);
325 dir = get_default_dir (me);
326 Directional_element_interface::set (me, dir);
329 /* stems in unnatural (forced) direction should be shortened,
330 according to [Roush & Gourlay] */
331 if (chord_start_y (me)
332 && (get_direction (me) != get_default_dir (me)))
333 length_f -= shorten_f;
335 Interval hp = head_positions (me);
336 Real st = hp[dir] + dir * length_f;
338 bool no_extend_b = to_boolean (me->get_grob_property ("no-stem-extend"));
339 if (!grace_b && !no_extend_b && dir * st < 0) // junkme?
343 Make a little room if we have a upflag and there is a dot.
344 previous approach was to lengthen the stem. This is not
345 good typesetting practice.
348 if (!get_beam (me) && dir == UP
349 && duration_log (me) > 2)
351 Grob * closest_to_flag = extremal_heads (me)[dir];
352 Grob * dots = closest_to_flag
353 ? Rhythmic_head::get_dots (closest_to_flag ) : 0;
357 Real dp = Staff_symbol_referencer::get_position (dots);
358 Real flagy = flag (me).extent (Y_AXIS)[-dir] * 2
359 / Staff_symbol_referencer::staff_space (me);
362 Very gory: add myself to the X-support of the parent,
363 which should be a dot-column.
365 if (dir * (st + flagy - dp) < 0.5)
367 Grob *par = dots->get_parent (X_AXIS);
369 if (Dot_column::has_interface (par))
371 Side_position_interface::add_support (par, me);
374 TODO: apply some better logic here. The flag is
375 curved inwards, so this will typically be too
391 the log of the duration (Number of hooks on the flag minus two)
394 Stem::duration_log (Grob*me)
396 SCM s = me->get_grob_property ("duration-log");
397 return (gh_number_p (s)) ? gh_scm2int (s) : 2;
401 Stem::position_noteheads (Grob*me)
403 if (!head_count (me))
406 Link_array<Grob> heads =
407 Pointer_group_interface__extract_grobs (me, (Grob*)0, "note-heads");
409 heads.sort (compare_position);
410 Direction dir =get_direction (me);
416 bool invisible = invisible_b (me);
419 thick = gh_scm2double (me->get_grob_property ("thickness"))
420 * me->get_paper ()->get_var ("linethickness");
423 Grob *hed = support_head (me);
424 Real w = Note_head::head_extent (hed,X_AXIS)[dir];
425 for (int i=0; i < heads.size (); i++)
427 heads[i]->translate_axis (w - Note_head::head_extent (heads[i],X_AXIS)[dir],
431 bool parity= true; // todo: make me settable.
432 int lastpos = int (Staff_symbol_referencer::get_position (heads[0]));
433 for (int i=1; i < heads.size (); i ++)
435 Real p = Staff_symbol_referencer::get_position (heads[i]);
436 int dy =abs (lastpos- (int)p);
442 Real l = Note_head::head_extent (heads[i], X_AXIS).length ();
444 Direction d = get_direction (me);
445 heads[i]->translate_axis (l * d, X_AXIS);
448 heads[i]->translate_axis (-thick *2* d , X_AXIS);
453 For some cases we should kern some more: when the
454 distance between the next or prev note is too large, we'd
455 get large white gaps, eg.
474 MAKE_SCHEME_CALLBACK (Stem,before_line_breaking,1);
476 Stem::before_line_breaking (SCM smob)
478 Grob*me = unsmob_grob (smob);
482 Do the calculations for visible stems, but also for invisible stems
483 with note heads (i.e. half notes.)
487 stem_end_position (me); // ugh. Trigger direction calc.
488 position_noteheads (me);
492 me->set_grob_property ("molecule-callback", SCM_EOL);
495 return SCM_UNSPECIFIED;
500 When in a beam with tuplet brackets, brew_mol is called early,
501 caching a wrong value.
503 MAKE_SCHEME_CALLBACK (Stem, height, 2);
505 Stem::height (SCM smob, SCM ax)
507 Axis a = (Axis)gh_scm2int (ax);
508 Grob * me = unsmob_grob (smob);
509 assert (a == Y_AXIS);
511 SCM mol = me->get_uncached_molecule ();
514 iv = unsmob_molecule (mol)->extent (a);
515 return ly_interval2scm (iv);
522 /* TODO: rename flag-style into something more appropriate,
523 e.g. "stroke-style", maybe with values "" (i.e. no stroke),
524 "single" and "double". Needs more discussion.
526 String style, fstyle, staffline_offs;
527 SCM fst = me->get_grob_property ("flag-style");
528 if (gh_string_p (fst))
530 fstyle = ly_scm2string (fst);
533 SCM st = me->get_grob_property ("style");
534 if (gh_symbol_p (st))
536 style = (ly_scm2string (scm_symbol_to_string (st)));
542 bool adjust = to_boolean (me->get_grob_property ("adjust-if-on-staffline"));
544 if (String::compare (style, "mensural") == 0)
545 /* Mensural notation: For notes on staff lines, use different
546 flags than for notes between staff lines. The idea is that
547 flags are always vertically aligned with the staff lines,
548 regardless if the note head is on a staff line or between two
549 staff lines. In other words, the inner end of a flag always
550 touches a staff line.
555 /* Urrgh! We have to detect wether this stem ends on a staff
556 line or between two staff lines. But we can not call
557 stem_end_position(me) or get_default_stem_end_position(me),
558 since this encounters the flag and hence results in an
559 infinite recursion. However, in pure mensural notation,
560 there are no multiple note heads attached to a single stem,
561 neither is there usually need for using the stem_shorten
562 property (except for 32th and 64th notes, but that is not a
563 problem since the stem length in this case is augmented by
564 an integral multiple of staff_space). Hence, it should be
565 sufficient to just take the first note head, assume it's
566 the only one, look if it's on a staff line, and select the
567 flag's shape accordingly. In the worst case, the shape
568 looks slightly misplaced, but that will usually be the
569 programmer's fault (e.g. when trying to attach multiple
570 note heads to a single stem in mensural notation). */
573 perhaps the detection whether this correction is needed should
574 happen in a different place to avoid the recursion.
578 Grob *first = first_head(me);
579 int sz = Staff_symbol_referencer::line_count (me)-1;
580 int p = (int)rint (Staff_symbol_referencer::get_position (first));
581 staffline_offs = (((p ^ sz) & 0x1) == 0) ? "1" : "0";
585 staffline_offs = "2";
592 char c = (get_direction (me) == UP) ? 'u' : 'd';
594 = String ("flags-") + style + to_string (c) + staffline_offs + to_string (duration_log (me));
596 = Font_interface::get_default_font (me)->find_by_name (index_string);
597 if (!fstyle.empty_b ())
598 m.add_molecule (Font_interface::get_default_font (me)->find_by_name (String ("flags-") + to_string (c) + fstyle));
602 MAKE_SCHEME_CALLBACK (Stem,dim_callback,2);
604 Stem::dim_callback (SCM e, SCM ax)
606 Axis a = (Axis) gh_scm2int (ax);
607 assert (a == X_AXIS);
608 Grob *se = unsmob_grob (e);
610 if (unsmob_grob (se->get_grob_property ("beam")) || abs (duration_log (se)) <= 2)
614 r = flag (se).extent (X_AXIS);
616 return ly_interval2scm (r);
621 MAKE_SCHEME_CALLBACK (Stem,brew_molecule,1);
624 Stem::brew_molecule (SCM smob)
626 Grob*me = unsmob_grob (smob);
628 Direction d = get_direction (me);
635 This is required to avoid stems passing in tablature chords...
640 TODO: make the stem start a direction ?
645 if (to_boolean (me->get_grob_property ("avoid-note-head")))
647 Grob * lh = last_head (me);
650 y1 = Staff_symbol_referencer::get_position (lh);
654 Grob * lh = first_head (me);
657 y1 = Staff_symbol_referencer::get_position (lh);
660 Real y2 = stem_end_position (me);
662 Interval stem_y (y1 <? y2,y2 >? y1);
666 Real dy = Staff_symbol_referencer::staff_space (me) * 0.5;
668 if (Grob *hed = support_head (me))
671 must not take ledgers into account.
673 Interval head_height = Note_head::head_extent (hed,Y_AXIS);
674 Real y_attach = Note_head::stem_attachment_coordinate ( hed, Y_AXIS);
676 y_attach = head_height.linear_combination (y_attach);
677 stem_y[Direction (-d)] += d * y_attach/dy;
680 if (!invisible_b (me))
682 Real stem_width = gh_scm2double (me->get_grob_property ("thickness"))
684 * me->get_paper ()->get_var ("linethickness");
686 Molecule ss =Lookup::filledbox (Box (Interval (-stem_width/2, stem_width/2),
687 Interval (stem_y[DOWN]*dy, stem_y[UP]*dy)));
688 mol.add_molecule (ss);
691 if (!get_beam (me) && abs (duration_log (me)) > 2)
693 Molecule fl = flag (me);
694 fl.translate_axis (stem_y[d]*dy, Y_AXIS);
695 mol.add_molecule (fl);
698 return mol.smobbed_copy ();
702 move the stem to right of the notehead if it is up.
704 MAKE_SCHEME_CALLBACK (Stem,off_callback,2);
706 Stem::off_callback (SCM element_smob, SCM)
708 Grob *me = unsmob_grob (element_smob);
712 if (head_count (me) == 0)
714 return gh_double2scm (0.0);
717 if (Grob * f = first_head (me))
719 Interval head_wid = Note_head::head_extent(f, X_AXIS);
724 if (invisible_b (me))
729 attach = Note_head::stem_attachment_coordinate(f, X_AXIS);
731 Direction d = get_direction (me);
733 Real real_attach = head_wid.linear_combination (d * attach);
738 If not centered: correct for stem thickness.
743 = gh_scm2double (me->get_grob_property ("thickness"))
744 * me->get_paper ()->get_var ("linethickness");
747 r += - d * rule_thick * 0.5;
750 return gh_double2scm (r);
754 Stem::get_beam (Grob*me)
756 SCM b= me->get_grob_property ("beam");
757 return unsmob_grob (b);
761 Stem::get_stem_info (Grob *me)
763 /* Return cached info if available */
764 SCM scm_info = me->get_grob_property ("stem-info");
765 if (!gh_pair_p (scm_info))
768 scm_info = me->get_grob_property ("stem-info");
772 si.dir_ = Directional_element_interface::get (me);
773 si.ideal_y_ = gh_scm2double (gh_car (scm_info));
774 si.shortest_y_ = gh_scm2double (gh_cadr (scm_info));
779 Stem::calc_stem_info (Grob *me)
781 /* Tab notation feature: make stem end extend out of staff. */
782 SCM up_to_staff = me->get_grob_property ("up-to-staff");
783 if (to_boolean (up_to_staff))
785 int line_count = Staff_symbol_referencer::line_count (me);
786 Direction dir = get_direction (me);
787 Real ideal_y = dir * (line_count + 1.5);
788 Real shortest_y = ideal_y;
790 me->set_grob_property ("stem-info",
791 scm_list_n (gh_double2scm (ideal_y),
792 gh_double2scm (shortest_y),
797 Direction my_dir = Directional_element_interface::get (me);
798 Real staff_space = Staff_symbol_referencer::staff_space (me);
799 Grob *beam = get_beam (me);
800 Real beam_translation = Beam::get_beam_translation (beam);
801 Real beam_thickness = gh_scm2double (beam->get_grob_property ("thickness"));
802 int beam_count = Beam::get_direction_beam_count (beam, my_dir);
805 /* Simple standard stem length */
807 gh_scm2double (robust_list_ref
809 me->get_grob_property ("beamed-lengths")))
811 /* stem only extends to center of beam */
812 - 0.5 * beam_thickness;
815 /* Condition: sane minimum free stem length (chord to beams) */
816 Real ideal_minimum_free =
817 gh_scm2double (robust_list_ref
819 me->get_grob_property ("beamed-minimum-free-lengths")))
822 int my_beam_count = Stem::beam_multiplicity (me).length () + 1;
823 Real height_of_my_beams = beam_thickness
824 + (my_beam_count - 1) * beam_translation;
826 Real ideal_minimum_length = ideal_minimum_free
828 /* stem only extends to center of beam */
829 - 0.5 * beam_thickness;
831 ideal_length = ideal_length >? ideal_minimum_length;
834 /* Convert to Y position, calculate for dir == UP */
836 /* staff positions */
837 head_positions (me)[my_dir] * 0.5
839 Real ideal_y = note_start + ideal_length;
842 /* Conditions for Y position */
844 /* Lowest beam of (UP) beam must never be lower than second staffline
848 Although this (additional) rule is probably correct,
849 I expect that highest beam (UP) should also never be lower
850 than middle staffline, just as normal stems.
854 Obviously not for grace beams.
856 Also, not for knees. Seems to be a good thing. */
857 SCM grace = me->get_grob_property ("grace");
858 bool grace_b = to_boolean (grace);
859 bool no_extend_b = to_boolean (me->get_grob_property ("no-stem-extend"));
860 bool knee_b = to_boolean (beam->get_grob_property ("knee"));
861 if (!grace_b && !no_extend_b && !knee_b)
863 /* Highest beam of (UP) beam must never be lower than middle
865 ideal_y = ideal_y >? 0;
866 /* Lowest beam of (UP) beam must never be lower than second staffline */
867 ideal_y = ideal_y >? (-staff_space
868 - beam_thickness + height_of_my_beams);
872 SCM shorten = beam->get_grob_property ("shorten");
873 if (gh_number_p (shorten))
874 ideal_y -= gh_scm2double (shorten);
878 gh_scm2double (robust_list_ref
880 me->get_grob_property
881 ("beamed-extreme-minimum-free-lengths")))
884 Real minimum_length = minimum_free
886 /* stem only extends to center of beam */
887 - 0.5 * beam_thickness;
889 Real minimum_y = note_start + minimum_length;
893 Real shortest_y = minimum_y * my_dir;
895 me->set_grob_property ("stem-info",
896 scm_list_n (gh_double2scm (ideal_y),
897 gh_double2scm (shortest_y),
902 Stem::beam_multiplicity (Grob *stem)
904 SCM beaming= stem->get_grob_property ("beaming");
905 Slice l = int_list_to_slice (gh_car (beaming));
906 Slice r = int_list_to_slice (gh_cdr (beaming));
913 ADD_INTERFACE (Stem,"stem-interface",
915 "up-to-staff avoid-note-head adjust-if-on-staffline thickness stem-info beamed-lengths beamed-minimum-free-lengths beamed-extreme-minimum-free-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");