2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 1996--2009 Han-Wen Nienhuys <hanwen@xs4all.nl>
5 Jan Nieuwenhuizen <janneke@gnu.org>
7 TODO: This is way too hairy
11 Stem-end, chord-start, etc. is all confusing naming.
13 LilyPond is free software: you can redistribute it and/or modify
14 it under the terms of the GNU General Public License as published by
15 the Free Software Foundation, either version 3 of the License, or
16 (at your option) any later version.
18 LilyPond is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 GNU General Public License for more details.
23 You should have received a copy of the GNU General Public License
24 along with LilyPond. If not, see <http://www.gnu.org/licenses/>.
30 #include <cmath> // rint
34 #include "directional-element-interface.hh"
35 #include "dot-column.hh"
36 #include "font-interface.hh"
37 #include "international.hh"
40 #include "note-head.hh"
41 #include "output-def.hh"
42 #include "paper-column.hh"
43 #include "pointer-group-interface.hh"
45 #include "rhythmic-head.hh"
46 #include "side-position-interface.hh"
47 #include "staff-symbol-referencer.hh"
48 #include "stem-tremolo.hh"
52 Stem::set_beaming (Grob *me, int beam_count, Direction d)
54 SCM pair = me->get_property ("beaming");
56 if (!scm_is_pair (pair))
58 pair = scm_cons (SCM_EOL, SCM_EOL);
59 me->set_property ("beaming", pair);
62 SCM lst = index_get_cell (pair, d);
64 for (int i = 0; i < beam_count; i++)
65 lst = scm_cons (scm_from_int (i), lst);
69 index_set_cell (pair, d, lst);
73 Stem::get_beaming (Grob *me, Direction d)
75 SCM pair = me->get_property ("beaming");
76 if (!scm_is_pair (pair))
79 SCM lst = index_get_cell (pair, d);
81 int len = scm_ilength (lst);
86 Stem::head_positions (Grob *me)
90 Drul_array<Grob *> e (extremal_heads (me));
91 return Interval (Staff_symbol_referencer::get_position (e[DOWN]),
92 Staff_symbol_referencer::get_position (e[UP]));
98 Stem::chord_start_y (Grob *me)
100 Interval hp = head_positions (me);
102 return hp[get_grob_direction (me)] * Staff_symbol_referencer::staff_space (me)
110 Stem::set_stemend (Grob *me, Real se)
113 Direction d = get_grob_direction (me);
115 if (d && d * head_positions (me)[get_grob_direction (me)] >= se * d)
116 me->warning (_ ("weird stem size, check for narrow beams"));
118 me->set_property ("stem-end-position", scm_from_double (se));
121 /* Note head that determines hshift for upstems
122 WARNING: triggers direction */
124 Stem::support_head (Grob *me)
126 extract_grob_set (me, "note-heads", heads);
127 if (heads.size () == 1)
130 return first_head (me);
134 Stem::head_count (Grob *me)
136 return Pointer_group_interface::count (me, ly_symbol2scm ("note-heads"));
139 /* The note head which forms one end of the stem.
140 WARNING: triggers direction */
142 Stem::first_head (Grob *me)
144 Direction d = get_grob_direction (me);
146 return extremal_heads (me)[-d];
150 /* The note head opposite to the first head. */
152 Stem::last_head (Grob *me)
154 Direction d = get_grob_direction (me);
156 return extremal_heads (me)[d];
161 START is part where stem reaches `last' head.
163 This function returns a drul with (bottom-head, top-head).
166 Stem::extremal_heads (Grob *me)
168 const int inf = INT_MAX;
169 Drul_array<int> extpos;
173 Drul_array<Grob *> exthead (0, 0);
174 extract_grob_set (me, "note-heads", heads);
176 for (vsize i = heads.size (); i--;)
179 int p = Staff_symbol_referencer::get_rounded_position (n);
184 if (d * p > d * extpos[d])
190 while (flip (&d) != DOWN);
195 /* The positions, in ascending order. */
197 Stem::note_head_positions (Grob *me)
200 extract_grob_set (me, "note-heads", heads);
202 for (vsize i = heads.size (); i--;)
205 int p = Staff_symbol_referencer::get_rounded_position (n);
210 vector_sort (ps, less<int> ());
215 Stem::add_head (Grob *me, Grob *n)
217 n->set_object ("stem", me->self_scm ());
219 if (Note_head::has_interface (n))
220 Pointer_group_interface::add_grob (me, ly_symbol2scm ("note-heads"), n);
221 else if (Rest::has_interface (n))
222 Pointer_group_interface::add_grob (me, ly_symbol2scm ("rests"), n);
226 Stem::is_invisible (Grob *me)
228 return !is_normal_stem (me)
229 && (robust_scm2double (me->get_property ("stemlet-length"),
235 Stem::is_normal_stem (Grob *me)
237 return head_count (me) && scm_to_int (me->get_property ("duration-log")) >= 1;
241 MAKE_SCHEME_CALLBACK (Stem, pure_height, 3)
243 Stem::pure_height (SCM smob,
247 Grob *me = unsmob_grob (smob);
250 if (!is_normal_stem (me))
251 return ly_interval2scm (iv);
253 Real ss = Staff_symbol_referencer::staff_space (me);
254 Real rad = Staff_symbol_referencer::staff_radius (me);
256 if (!to_boolean (me->get_property ("cross-staff")))
258 Real len = scm_to_double (calc_length (smob)) * ss / 2;
259 Direction dir = get_grob_direction (me);
261 Interval hp = head_positions (me);
263 iv = Interval (0, len);
265 iv = Interval (-len, 0);
268 iv.translate (hp[dir] * ss / 2);
270 /* extend the stem (away from the head) to cover the staff */
272 iv[UP] = max (iv[UP], rad * ss);
274 iv[DOWN] = min (iv[DOWN], -rad * ss);
277 iv = Interval (-rad * ss, rad * ss);
279 return ly_interval2scm (iv);
282 MAKE_SCHEME_CALLBACK (Stem, calc_stem_end_position, 1)
284 Stem::calc_stem_end_position (SCM smob)
286 Grob *me = unsmob_grob (smob);
288 if (!head_count (me))
289 return scm_from_double (0.0);
291 if (Grob *beam = get_beam (me))
293 (void) beam->get_property ("quantized-positions");
294 return me->get_property ("stem-end-position");
299 /* WARNING: IN HALF SPACES */
300 Real length = robust_scm2double (me->get_property ("length"), 7);
302 Direction dir = get_grob_direction (me);
303 Interval hp = head_positions (me);
304 Real stem_end = dir ? hp[dir] + dir * length : 0;
306 /* TODO: change name to extend-stems to staff/center/'() */
307 bool no_extend = to_boolean (me->get_property ("no-stem-extend"));
308 if (!no_extend && dir * stem_end < 0)
311 return scm_from_double (stem_end);
314 /* Length is in half-spaces (or: positions) here. */
315 MAKE_SCHEME_CALLBACK (Stem, calc_length, 1)
317 Stem::calc_length (SCM smob)
319 Grob *me = unsmob_grob (smob);
321 SCM details = me->get_property ("details");
322 int durlog = duration_log (me);
324 Real ss = Staff_symbol_referencer::staff_space (me);
326 SCM s = ly_assoc_get (ly_symbol2scm ("lengths"), details, SCM_EOL);
328 length = 2 * scm_to_double (robust_list_ref (durlog - 2, s));
330 Direction dir = get_grob_direction (me);
332 /* Stems in unnatural (forced) direction should be shortened,
333 according to [Roush & Gourlay] */
334 Interval hp = head_positions (me);
335 if (dir && dir * hp[dir] >= 0)
337 SCM sshorten = ly_assoc_get (ly_symbol2scm ("stem-shorten"), details, SCM_EOL);
338 SCM scm_shorten = scm_is_pair (sshorten)
339 ? robust_list_ref (max (duration_log (me) - 2, 0), sshorten) : SCM_EOL;
340 Real shorten = 2* robust_scm2double (scm_shorten, 0);
342 /* On boundary: shorten only half */
343 if (abs (head_positions (me)[dir]) <= 1)
349 length *= robust_scm2double (me->get_property ("length-fraction"), 1.0);
352 Grob *t_flag = unsmob_grob (me->get_object ("tremolo-flag"));
353 if (t_flag && !unsmob_grob (me->get_object ("beam")))
355 /* Crude hack: add extra space if tremolo flag is there.
357 We can't do this for the beam, since we get into a loop
358 (Stem_tremolo::raw_stencil () looks at the beam.) --hwn */
361 + 2 * Stem_tremolo::vertical_length (t_flag) / ss;
363 /* We don't want to add the whole extent of the flag because the trem
364 and the flag can overlap partly. beam_translation gives a good
368 Real beam_trans = Stem_tremolo::get_beam_translation (t_flag);
369 /* the obvious choice is (durlog - 2) here, but we need a bit more space. */
370 minlen += 2 * (durlog - 1.5) * beam_trans;
372 /* up-stems need even a little more space to avoid collisions. This
373 needs to be in sync with the tremolo positioning code in
374 Stem_tremolo::print */
376 minlen += beam_trans;
378 length = max (length, minlen + 1.0);
381 return scm_from_double (length);
383 /* The log of the duration (Number of hooks on the flag minus two) */
385 Stem::duration_log (Grob *me)
387 SCM s = me->get_property ("duration-log");
388 return (scm_is_number (s)) ? scm_to_int (s) : 2;
391 MAKE_SCHEME_CALLBACK (Stem, calc_positioning_done, 1);
393 Stem::calc_positioning_done (SCM smob)
395 Grob *me = unsmob_grob (smob);
396 if (!head_count (me))
399 me->set_property ("positioning-done", SCM_BOOL_T);
401 extract_grob_set (me, "note-heads", ro_heads);
402 vector<Grob*> heads (ro_heads);
403 vector_sort (heads, position_less);
404 Direction dir = get_grob_direction (me);
409 Real thick = thickness (me);
411 Grob *hed = support_head (me);
414 programming_error ("Stem dir must be up or down.");
416 set_grob_direction (me, dir);
419 bool is_harmonic_centered = false;
420 for (vsize i = 0; i < heads.size (); i++)
421 is_harmonic_centered = is_harmonic_centered
422 || heads[i]->get_property ("style") == ly_symbol2scm ("harmonic");
423 is_harmonic_centered = is_harmonic_centered && is_invisible (me);
425 Real w = hed->extent (hed, X_AXIS)[dir];
426 for (vsize i = 0; i < heads.size (); i++)
428 Real amount = w - heads[i]->extent (heads[i], X_AXIS)[dir];
430 if (is_harmonic_centered)
432 hed->extent (hed, X_AXIS).linear_combination (CENTER)
433 - heads[i]->extent (heads[i], X_AXIS).linear_combination (CENTER);
435 heads[i]->translate_axis (amount, X_AXIS);
438 Real lastpos = Real (Staff_symbol_referencer::get_position (heads[0]));
439 for (vsize i = 1; i < heads.size (); i++)
441 Real p = Staff_symbol_referencer::get_position (heads[i]);
442 Real dy = fabs (lastpos- p);
445 dy should always be 0.5, 0.0, 1.0, but provide safety margin
452 Real ell = heads[i]->extent (heads[i], X_AXIS).length ();
454 Direction d = get_grob_direction (me);
456 Reversed head should be shifted ell-thickness, but this
457 looks too crowded, so we only shift ell-0.5*thickness.
459 This leads to assymetry: Normal heads overlap the
460 stem 100% whereas reversed heads only overlaps the
464 Real reverse_overlap = 0.5;
465 heads[i]->translate_axis ((ell - thick * reverse_overlap) * d,
468 if (is_invisible (me))
469 heads[i]->translate_axis (-thick * (2 - reverse_overlap) * d,
474 For some cases we should kern some more: when the
475 distance between the next or prev note is too large, we'd
476 get large white gaps, eg.
497 MAKE_SCHEME_CALLBACK (Stem, calc_direction, 1);
499 Stem::calc_direction (SCM smob)
501 Grob *me = unsmob_grob (smob);
502 Direction dir = CENTER;
503 if (Grob *beam = unsmob_grob (me->get_object ("beam")))
505 SCM ignore_me = beam->get_property ("direction");
507 dir = get_grob_direction (me);
511 SCM dd = me->get_property ("default-direction");
514 return me->get_property ("neutral-direction");
517 return scm_from_int (dir);
520 MAKE_SCHEME_CALLBACK (Stem, calc_default_direction, 1);
522 Stem::calc_default_direction (SCM smob)
524 Grob *me = unsmob_grob (smob);
526 Direction dir = CENTER;
527 int staff_center = 0;
528 Interval hp = head_positions (me);
531 int udistance = (int) (UP * hp[UP] - staff_center);
532 int ddistance = (int) (DOWN * hp[DOWN] - staff_center);
534 dir = Direction (sign (ddistance - udistance));
537 return scm_from_int (dir);
541 MAKE_SCHEME_CALLBACK (Stem, height, 1);
543 Stem::height (SCM smob)
545 Grob *me = unsmob_grob (smob);
546 if (!is_normal_stem (me))
547 return ly_interval2scm (Interval ());
549 Direction dir = get_grob_direction (me);
551 Grob *beam = get_beam (me);
554 /* trigger set-stem-lengths. */
555 beam->get_property ("quantized-positions");
559 Can't get_stencil (), since that would cache stencils too early.
560 This causes problems with beams.
562 Stencil *stencil = unsmob_stencil (print (smob));
563 Interval iv = stencil ? stencil->extent (Y_AXIS) : Interval ();
568 programming_error ("no stem direction");
571 iv[dir] += dir * Beam::get_beam_thickness (beam) * 0.5;
574 return ly_interval2scm (iv);
578 Stem::stem_end_position (Grob *me)
580 return robust_scm2double (me->get_property ("stem-end-position"), 0);
583 MAKE_SCHEME_CALLBACK (Stem, calc_flag, 1);
585 Stem::calc_flag (SCM smob)
587 Grob *me = unsmob_grob (smob);
589 int log = duration_log (me);
591 TODO: maybe property stroke-style should take different values,
592 e.g. "" (i.e. no stroke), "single" and "double" (currently, it's
596 SCM flag_style_scm = me->get_property ("flag-style");
597 if (scm_is_symbol (flag_style_scm))
598 flag_style = ly_symbol2string (flag_style_scm);
600 if (flag_style == "no-flag")
601 return Stencil ().smobbed_copy ();
605 string staffline_offs;
606 if (flag_style == "mensural")
607 /* Mensural notation: For notes on staff lines, use different
608 flags than for notes between staff lines. The idea is that
609 flags are always vertically aligned with the staff lines,
610 regardless if the note head is on a staff line or between two
611 staff lines. In other words, the inner end of a flag always
612 touches a staff line.
617 int p = (int) (rint (stem_end_position (me)));
619 = Staff_symbol_referencer::on_line (me, p) ? "0" : "1";
622 staffline_offs = "2";
627 char dir = (get_grob_direction (me) == UP) ? 'u' : 'd';
628 string font_char = flag_style
629 + to_string (dir) + staffline_offs + to_string (log);
630 Font_metric *fm = Font_interface::get_default_font (me);
631 Stencil flag = fm->find_by_name ("flags." + font_char);
632 if (flag.is_empty ())
633 me->warning (_f ("flag `%s' not found", font_char));
635 SCM stroke_style_scm = me->get_property ("stroke-style");
636 if (scm_is_string (stroke_style_scm))
638 string stroke_style = ly_scm2string (stroke_style_scm);
639 if (!stroke_style.empty ())
641 string font_char = flag_style + to_string (dir) + stroke_style;
642 Stencil stroke = fm->find_by_name ("flags." + font_char);
643 if (stroke.is_empty ())
645 font_char = to_string (dir) + stroke_style;
646 stroke = fm->find_by_name ("flags." + font_char);
648 if (stroke.is_empty ())
649 me->warning (_f ("flag stroke `%s' not found", font_char));
651 flag.add_stencil (stroke);
655 return flag.smobbed_copy ();
660 Stem::flag (Grob *me)
662 int log = duration_log (me);
664 || unsmob_grob (me->get_object ("beam")))
667 if (!is_normal_stem (me))
670 // This get_property call already evaluates the scheme function with
671 // the grob passed as argument! Thus, we only have to check if a valid
672 // stencil is returned.
673 SCM flag_style_scm = me->get_property ("flag");
674 if (Stencil *flag = unsmob_stencil (flag_style_scm)) {
681 MAKE_SCHEME_CALLBACK (Stem, width, 1);
685 Grob *me = unsmob_grob (e);
689 if (is_invisible (me))
691 else if (unsmob_grob (me->get_object ("beam"))
692 || abs (duration_log (me)) <= 2)
694 r = Interval (-1, 1);
695 r *= thickness (me) / 2;
699 r = Interval (-1, 1) * thickness (me) * 0.5;
700 r.unite (flag (me).extent (X_AXIS));
702 return ly_interval2scm (r);
706 Stem::thickness (Grob *me)
708 return scm_to_double (me->get_property ("thickness"))
709 * Staff_symbol_referencer::line_thickness (me);
712 MAKE_SCHEME_CALLBACK (Stem, print, 1);
714 Stem::print (SCM smob)
716 Grob *me = unsmob_grob (smob);
717 Grob *beam = get_beam (me);
720 Direction d = get_grob_direction (me);
722 Real stemlet_length = robust_scm2double (me->get_property ("stemlet-length"),
724 bool stemlet = stemlet_length > 0.0;
726 /* TODO: make the stem start a direction ?
727 This is required to avoid stems passing in tablature chords. */
729 = to_boolean (me->get_property ("avoid-note-head"))
736 if (!lh && stemlet && !beam)
739 if (lh && robust_scm2int (lh->get_property ("duration-log"), 0) < 1)
742 if (is_invisible (me))
745 Real y2 = robust_scm2double (me->get_property ("stem-end-position"), 0.0);
747 Real half_space = Staff_symbol_referencer::staff_space (me) * 0.5;
750 y2 = Staff_symbol_referencer::get_position (lh);
753 Real beam_translation = Beam::get_beam_translation (beam);
754 Real beam_thickness = Beam::get_beam_thickness (beam);
755 int beam_count = beam_multiplicity (me).length () + 1;
758 * (0.5 * beam_thickness
759 + beam_translation * max (0, (beam_count - 1))
760 + stemlet_length) / half_space;
763 Interval stem_y (min (y1, y2), max (y2, y1));
765 if (Grob *head = support_head (me))
768 must not take ledgers into account.
770 Interval head_height = head->extent (head, Y_AXIS);
771 Real y_attach = Note_head::stem_attachment_coordinate (head, Y_AXIS);
773 y_attach = head_height.linear_combination (y_attach);
774 stem_y[Direction (-d)] += d * y_attach / half_space;
778 Real stem_width = thickness (me);
780 = me->layout ()->get_dimension (ly_symbol2scm ("blot-diameter"));
782 Box b = Box (Interval (-stem_width / 2, stem_width / 2),
783 Interval (stem_y[DOWN] * half_space, stem_y[UP] * half_space));
785 Stencil ss = Lookup::round_filled_box (b, blot);
786 mol.add_stencil (ss);
788 mol.add_stencil (get_translated_flag (me));
790 return mol.smobbed_copy ();
794 Stem::get_translated_flag (Grob *me)
796 Stencil fl = flag (me);
799 Direction d = get_grob_direction (me);
801 = me->layout ()->get_dimension (ly_symbol2scm ("blot-diameter"));
802 Real stem_width = thickness (me);
803 Real half_space = Staff_symbol_referencer::staff_space (me) * 0.5;
804 Real y2 = robust_scm2double (me->get_property ("stem-end-position"), 0.0);
805 fl.translate_axis (y2 * half_space - d * blot / 2, Y_AXIS);
806 fl.translate_axis (stem_width / 2, X_AXIS);
813 move the stem to right of the notehead if it is up.
815 MAKE_SCHEME_CALLBACK (Stem, offset_callback, 1);
817 Stem::offset_callback (SCM smob)
819 Grob *me = unsmob_grob (smob);
821 extract_grob_set (me, "rests", rests);
824 Grob *rest = rests.back ();
825 Real r = rest->extent (rest, X_AXIS).center ();
826 return scm_from_double (r);
830 if (Grob *f = first_head (me))
832 Interval head_wid = f->extent (f, X_AXIS);
835 if (is_invisible (me))
838 attach = Note_head::stem_attachment_coordinate (f, X_AXIS);
840 Direction d = get_grob_direction (me);
841 Real real_attach = head_wid.linear_combination (d * attach);
842 Real r = real_attach;
844 /* If not centered: correct for stem thickness. */
847 Real rule_thick = thickness (me);
848 r += -d * rule_thick * 0.5;
850 return scm_from_double (r);
853 programming_error ("Weird stem.");
854 return scm_from_double (0.0);
858 Stem::get_beam (Grob *me)
860 SCM b = me->get_object ("beam");
861 return dynamic_cast<Spanner *> (unsmob_grob (b));
865 Stem::get_stem_info (Grob *me)
868 si.dir_ = get_grob_direction (me);
870 SCM scm_info = me->get_property ("stem-info");
871 si.ideal_y_ = scm_to_double (scm_car (scm_info));
872 si.shortest_y_ = scm_to_double (scm_cadr (scm_info));
876 MAKE_SCHEME_CALLBACK (Stem, calc_stem_info, 1);
878 Stem::calc_stem_info (SCM smob)
880 Grob *me = unsmob_grob (smob);
881 Direction my_dir = get_grob_direction (me);
885 programming_error ("no stem dir set");
889 Real staff_space = Staff_symbol_referencer::staff_space (me);
890 Grob *beam = get_beam (me);
894 (void) beam->get_property ("beaming");
897 Real beam_translation = Beam::get_beam_translation (beam);
898 Real beam_thickness = Beam::get_beam_thickness (beam);
899 int beam_count = Beam::get_direction_beam_count (beam, my_dir);
901 = robust_scm2double (me->get_property ("length-fraction"), 1.0);
903 /* Simple standard stem length */
904 SCM details = me->get_property ("details");
905 SCM lengths = ly_assoc_get (ly_symbol2scm ("beamed-lengths"), details, SCM_EOL);
908 = scm_to_double (robust_list_ref (beam_count - 1, lengths))
912 /* stem only extends to center of beam
914 - 0.5 * beam_thickness;
916 /* Condition: sane minimum free stem length (chord to beams) */
917 lengths = ly_assoc_get (ly_symbol2scm ("beamed-minimum-free-lengths"), details, SCM_EOL);
919 Real ideal_minimum_free
920 = scm_to_double (robust_list_ref (beam_count - 1, lengths))
924 Real height_of_my_trem = 0.0;
925 Grob *trem = unsmob_grob (me->get_object ("tremolo-flag"));
929 = Stem_tremolo::vertical_length (trem)
930 /* hack a bit of space around the trem. */
936 It seems that also for ideal minimum length, we must use
937 the maximum beam count (for this direction):
939 \score{ \notes\relative c''{ [a8 a32] }}
941 must be horizontal. */
942 Real height_of_my_beams = beam_thickness
943 + (beam_count - 1) * beam_translation;
945 Real ideal_minimum_length = ideal_minimum_free
948 /* stem only extends to center of beam */
949 - 0.5 * beam_thickness;
951 ideal_length = max (ideal_length, ideal_minimum_length);
953 /* Convert to Y position, calculate for dir == UP */
955 = /* staff positions */
956 head_positions (me)[my_dir] * 0.5
957 * my_dir * staff_space;
958 Real ideal_y = note_start + ideal_length;
960 /* Conditions for Y position */
962 /* Lowest beam of (UP) beam must never be lower than second staffline
966 Although this (additional) rule is probably correct,
967 I expect that highest beam (UP) should also never be lower
968 than middle staffline, just as normal stems.
972 Obviously not for grace beams.
974 Also, not for knees. Seems to be a good thing. */
975 bool no_extend = to_boolean (me->get_property ("no-stem-extend"));
976 bool is_knee = to_boolean (beam->get_property ("knee"));
977 if (!no_extend && !is_knee)
979 /* Highest beam of (UP) beam must never be lower than middle
981 ideal_y = max (ideal_y, 0.0);
982 /* Lowest beam of (UP) beam must never be lower than second staffline */
983 ideal_y = max (ideal_y, (-staff_space
984 - beam_thickness + height_of_my_beams));
987 ideal_y -= robust_scm2double (beam->get_property ("shorten"), 0);
989 SCM bemfl = ly_assoc_get (ly_symbol2scm ("beamed-extreme-minimum-free-lengths"),
993 = scm_to_double (robust_list_ref (beam_count - 1, bemfl))
997 Real minimum_length = max (minimum_free, height_of_my_trem)
999 /* stem only extends to center of beam */
1000 - 0.5 * beam_thickness;
1003 Real minimum_y = note_start + minimum_length;
1004 Real shortest_y = minimum_y * my_dir;
1006 return scm_list_2 (scm_from_double (ideal_y),
1007 scm_from_double (shortest_y));
1011 Stem::beam_multiplicity (Grob *stem)
1013 SCM beaming = stem->get_property ("beaming");
1014 Slice le = int_list_to_slice (scm_car (beaming));
1015 Slice ri = int_list_to_slice (scm_cdr (beaming));
1021 Stem::is_cross_staff (Grob *stem)
1023 Grob *beam = unsmob_grob (stem->get_object ("beam"));
1024 return beam && Beam::is_cross_staff (beam);
1027 MAKE_SCHEME_CALLBACK (Stem, calc_cross_staff, 1)
1029 Stem::calc_cross_staff (SCM smob)
1031 return scm_from_bool (is_cross_staff (unsmob_grob (smob)));
1034 /* FIXME: Too many properties */
1035 ADD_INTERFACE (Stem,
1036 "The stem represents the graphical stem. In addition, it"
1037 " internally connects note heads, beams, and tremolos. Rests"
1038 " and whole notes have invisible stems.\n"
1040 "The following properties may be set in the @code{details}"
1044 "@item beamed-lengths\n"
1045 "List of stem lengths given beam multiplicity.\n"
1046 "@item beamed-minimum-free-lengths\n"
1047 "List of normal minimum free stem lengths (chord to beams)"
1048 " given beam multiplicity.\n"
1049 "@item beamed-extreme-minimum-free-lengths\n"
1050 "List of extreme minimum free stem lengths (chord to beams)"
1051 " given beam multiplicity.\n"
1053 "Default stem lengths. The list gives a length for each"
1055 "@item stem-shorten\n"
1056 "How much a stem in a forced direction should be shortened."
1057 " The list gives an amount depending on the number of flags"
1065 "beamlet-default-length "
1066 "beamlet-max-length-proportion "
1067 "default-direction "
1077 "neutral-direction "
1082 "stem-end-position "
1090 /****************************************************************/
1092 Stem_info::Stem_info ()
1094 ideal_y_ = shortest_y_ = 0;
1099 Stem_info::scale (Real x)