2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 1996--2011 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/>.
28 Note that several internal functions have a calc_beam bool argument.
29 This argument means: "If set, acknowledge the fact that there is a beam
30 and deal with it. If not, give me the measurements as if there is no beam."
31 Most pure functions are called WITHOUT calc_beam, whereas non-pure functions
32 are called WITH calc_beam.
34 The only exception to this is ::pure_height, which calls internal_pure_height
35 with "true" for calc_beam in order to trigger the calculations of other
36 pure heights in case there is a beam. It passes false, however, to
37 internal_height and internal_pure_height for all subsequent iterations.
43 #include <cmath> // rint
47 #include "directional-element-interface.hh"
48 #include "dot-column.hh"
49 #include "font-interface.hh"
50 #include "international.hh"
53 #include "note-head.hh"
54 #include "output-def.hh"
55 #include "paper-column.hh"
56 #include "pointer-group-interface.hh"
58 #include "rhythmic-head.hh"
59 #include "side-position-interface.hh"
60 #include "staff-symbol-referencer.hh"
61 #include "stem-tremolo.hh"
65 Stem::set_beaming (Grob *me, int beam_count, Direction d)
67 SCM pair = me->get_property ("beaming");
69 if (!scm_is_pair (pair))
71 pair = scm_cons (SCM_EOL, SCM_EOL);
72 me->set_property ("beaming", pair);
75 SCM lst = index_get_cell (pair, d);
77 for (int i = 0; i < beam_count; i++)
78 lst = scm_cons (scm_from_int (i), lst);
82 index_set_cell (pair, d, lst);
86 Stem::get_beaming (Grob *me, Direction d)
88 SCM pair = me->get_property ("beaming");
89 if (!scm_is_pair (pair))
92 SCM lst = index_get_cell (pair, d);
94 int len = scm_ilength (lst);
99 Stem::head_positions (Grob *me)
103 Drul_array<Grob *> e (extremal_heads (me));
104 return Interval (Staff_symbol_referencer::get_position (e[DOWN]),
105 Staff_symbol_referencer::get_position (e[UP]));
111 Stem::chord_start_y (Grob *me)
113 Interval hp = head_positions (me);
115 return hp[get_grob_direction (me)] * Staff_symbol_referencer::staff_space (me)
121 Stem::set_stem_positions (Grob *me, Real se)
124 Direction d = get_grob_direction (me);
126 Grob *beam = unsmob_grob (me->get_object ("beam"));
127 if (d && d * head_positions (me)[get_grob_direction (me)] >= se * d)
128 me->warning (_ ("weird stem size, check for narrow beams"));
130 Interval height = me->pure_height (me, 0, INT_MAX);
131 Real half_space = Staff_symbol_referencer::staff_space (me) * 0.5;
133 height[d] = se * half_space + beam_end_corrective (me);
135 Real stemlet_length = robust_scm2double (me->get_property ("stemlet-length"),
137 bool stemlet = stemlet_length > 0.0;
139 Grob *lh = get_reference_head (me);
145 Real beam_translation = Beam::get_beam_translation (beam);
146 Real beam_thickness = Beam::get_beam_thickness (beam);
147 int beam_count = beam_multiplicity (me).length () + 1;
149 height[-d] = (height[d] - d
150 * (0.5 * beam_thickness
151 + beam_translation * max (0, (beam_count - 1))
154 else if (!stemlet && beam)
155 height[-d] = height[d];
156 else if (stemlet && !beam)
157 me->programming_error ("Can't have a stemlet without a beam.");
160 me->set_property ("Y-extent", ly_interval2scm (height));
163 /* Note head that determines hshift for upstems
164 WARNING: triggers direction */
166 Stem::support_head (Grob *me)
168 extract_grob_set (me, "note-heads", heads);
169 if (heads.size () == 1)
172 return first_head (me);
176 Stem::head_count (Grob *me)
178 return Pointer_group_interface::count (me, ly_symbol2scm ("note-heads"));
181 /* The note head which forms one end of the stem.
182 WARNING: triggers direction */
184 Stem::first_head (Grob *me)
186 Direction d = get_grob_direction (me);
188 return extremal_heads (me)[-d];
192 /* The note head opposite to the first head. */
194 Stem::last_head (Grob *me)
196 Direction d = get_grob_direction (me);
198 return extremal_heads (me)[d];
203 START is part where stem reaches `last' head.
205 This function returns a drul with (bottom-head, top-head).
208 Stem::extremal_heads (Grob *me)
210 const int inf = INT_MAX;
211 Drul_array<int> extpos;
215 Drul_array<Grob *> exthead (0, 0);
216 extract_grob_set (me, "note-heads", heads);
218 for (vsize i = heads.size (); i--;)
221 int p = Staff_symbol_referencer::get_rounded_position (n);
226 if (d * p > d * extpos[d])
232 while (flip (&d) != DOWN);
237 /* The positions, in ascending order. */
239 Stem::note_head_positions (Grob *me)
242 extract_grob_set (me, "note-heads", heads);
244 for (vsize i = heads.size (); i--;)
247 int p = Staff_symbol_referencer::get_rounded_position (n);
252 vector_sort (ps, less<int> ());
257 Stem::add_head (Grob *me, Grob *n)
259 n->set_object ("stem", me->self_scm ());
261 if (Note_head::has_interface (n))
262 Pointer_group_interface::add_grob (me, ly_symbol2scm ("note-heads"), n);
263 else if (Rest::has_interface (n))
264 Pointer_group_interface::add_grob (me, ly_symbol2scm ("rests"), n);
268 Stem::is_invisible (Grob *me)
270 return !is_normal_stem (me)
271 && (robust_scm2double (me->get_property ("stemlet-length"),
276 Stem::is_normal_stem (Grob *me)
278 return head_count (me) && scm_to_int (me->get_property ("duration-log")) >= 1;
281 MAKE_SCHEME_CALLBACK (Stem, pure_height, 3)
283 Stem::pure_height (SCM smob,
287 Grob *me = unsmob_grob (smob);
288 return ly_interval2scm (internal_pure_height (me, true));
292 Stem::internal_pure_height (Grob *me, bool calc_beam)
294 if (!is_normal_stem (me))
295 return Interval (0.0, 0.0);
297 Grob *beam = unsmob_grob (me->get_object ("beam"));
299 Interval iv = internal_height (me, false);
303 if (!to_boolean (me->get_property ("cross-staff")) && calc_beam)
306 Direction dir = get_grob_direction (me);
309 overshoot[d] = d == dir ? dir * infinity_f : iv[d];
310 while (flip (&d) != DOWN);
312 vector<Interval> heights;
313 vector<Grob *> my_stems;
314 extract_grob_set (beam, "normal-stems", normal_stems);
315 for (vsize i = 0; i < normal_stems.size (); i++)
316 if (normal_stems[i] != me && get_grob_direction (normal_stems[i]) == dir)
318 heights.push_back (Stem::internal_pure_height (normal_stems[i], false));
319 my_stems.push_back (normal_stems[i]);
320 iv.unite (heights.back ());
322 for (vsize i = 0; i < my_stems.size (); i++)
323 cache_pure_height (my_stems[i], iv, heights[i]);
324 iv.intersect (overshoot);
331 Stem::cache_pure_height (Grob *me, Interval iv, Interval my_iv)
334 Direction dir = get_grob_direction (me);
337 overshoot[d] = d == dir ? dir * infinity_f : my_iv[d];
338 while (flip (&d) != DOWN);
340 iv.intersect (overshoot);
341 dynamic_cast<Item *> (me)->cache_pure_height (iv);
344 MAKE_SCHEME_CALLBACK (Stem, calc_stem_end_position, 1)
346 Stem::calc_stem_end_position (SCM smob)
348 Grob *me = unsmob_grob (smob);
349 return scm_from_double (internal_calc_stem_end_position (me, true));
352 MAKE_SCHEME_CALLBACK (Stem, pure_calc_stem_end_position, 3)
354 Stem::pure_calc_stem_end_position (SCM smob,
358 Grob *me = unsmob_grob (smob);
359 return scm_from_double (internal_calc_stem_end_position (me, false));
363 Stem::internal_calc_stem_end_position (Grob *me, bool calc_beam)
365 if (!head_count (me))
368 Grob *beam = get_beam (me);
369 Real ss = Staff_symbol_referencer::staff_space (me);
370 if (beam && calc_beam)
372 (void) beam->get_property ("quantized-positions");
373 return me->extent (me, Y_AXIS)[get_grob_direction (me)] * ss * 2;
378 /* WARNING: IN HALF SPACES */
379 SCM details = me->get_property ("details");
380 int durlog = duration_log (me);
382 Real staff_rad = Staff_symbol_referencer::staff_radius (me);
384 SCM s = ly_assoc_get (ly_symbol2scm ("lengths"), details, SCM_EOL);
386 length = 2 * scm_to_double (robust_list_ref (durlog - 2, s));
388 Direction dir = get_grob_direction (me);
390 /* Stems in unnatural (forced) direction should be shortened,
391 according to [Roush & Gourlay] */
392 Interval hp = head_positions (me);
393 if (dir && dir * hp[dir] >= 0)
395 SCM sshorten = ly_assoc_get (ly_symbol2scm ("stem-shorten"), details, SCM_EOL);
396 SCM scm_shorten = scm_is_pair (sshorten)
397 ? robust_list_ref (max (duration_log (me) - 2, 0), sshorten) : SCM_EOL;
398 Real shorten_property = 2 * robust_scm2double (scm_shorten, 0);
399 /* change in length between full-size and shortened stems is executed gradually.
400 "transition area" = stems between full-sized and fully-shortened.
402 Real quarter_stem_length = 2 * scm_to_double (robust_list_ref (0, s));
403 /* shortening_step = difference in length between consecutive stem lengths
404 in transition area. The bigger the difference between full-sized
405 and shortened stems, the bigger shortening_step is.
406 (but not greater than 1/2 and not smaller than 1/4).
407 value 6 is heuristic; it determines the suggested transition slope steepnesas.
409 Real shortening_step = min (max (0.25, (shorten_property / 6)), 0.5);
410 /* Shortening of unflagged stems should begin on the first stem that sticks
411 more than 1 staffspace (2 units) out of the staff.
412 Shortening of flagged stems begins in the same moment as unflagged ones,
413 but not earlier than on the middle line note.
415 Real which_step = (min (1.0, quarter_stem_length - (2 * staff_rad) - 2.0)) + abs (hp[dir]);
416 Real shorten = min (max (0.0, (shortening_step * which_step)), shorten_property);
421 length *= robust_scm2double (me->get_property ("length-fraction"), 1.0);
424 Grob *t_flag = unsmob_grob (me->get_object ("tremolo-flag"));
425 if (t_flag && (!unsmob_grob (me->get_object ("beam")) || !calc_beam))
427 /* Crude hack: add extra space if tremolo flag is there.
429 We can't do this for the beam, since we get into a loop
430 (Stem_tremolo::raw_stencil () looks at the beam.) --hwn */
433 + 2 * Stem_tremolo::vertical_length (t_flag) / ss;
435 /* We don't want to add the whole extent of the flag because the trem
436 and the flag can overlap partly. beam_translation gives a good
440 Real beam_trans = Stem_tremolo::get_beam_translation (t_flag);
441 /* the obvious choice is (durlog - 2) here, but we need a bit more space. */
442 minlen += 2 * (durlog - 1.5) * beam_trans;
444 /* up-stems need even a little more space to avoid collisions. This
445 needs to be in sync with the tremolo positioning code in
446 Stem_tremolo::print */
448 minlen += beam_trans;
450 length = max (length, minlen + 1.0);
453 Real stem_end = dir ? hp[dir] + dir * length : 0;
455 /* TODO: change name to extend-stems to staff/center/'() */
456 bool no_extend = to_boolean (me->get_property ("no-stem-extend"));
457 if (!no_extend && dir * stem_end < 0)
463 /* The log of the duration (Number of hooks on the flag minus two) */
465 Stem::duration_log (Grob *me)
467 SCM s = me->get_property ("duration-log");
468 return (scm_is_number (s)) ? scm_to_int (s) : 2;
471 MAKE_SCHEME_CALLBACK (Stem, calc_positioning_done, 1);
473 Stem::calc_positioning_done (SCM smob)
475 Grob *me = unsmob_grob (smob);
476 if (!head_count (me))
479 me->set_property ("positioning-done", SCM_BOOL_T);
481 extract_grob_set (me, "note-heads", ro_heads);
482 vector<Grob *> heads (ro_heads);
483 vector_sort (heads, position_less);
484 Direction dir = get_grob_direction (me);
489 Real thick = thickness (me);
491 Grob *hed = support_head (me);
494 programming_error ("Stem dir must be up or down.");
496 set_grob_direction (me, dir);
499 bool is_harmonic_centered = false;
500 for (vsize i = 0; i < heads.size (); i++)
501 is_harmonic_centered = is_harmonic_centered
502 || heads[i]->get_property ("style") == ly_symbol2scm ("harmonic");
503 is_harmonic_centered = is_harmonic_centered && is_invisible (me);
505 Real w = hed->extent (hed, X_AXIS)[dir];
506 for (vsize i = 0; i < heads.size (); i++)
508 Real amount = w - heads[i]->extent (heads[i], X_AXIS)[dir];
510 if (is_harmonic_centered)
512 = hed->extent (hed, X_AXIS).linear_combination (CENTER)
513 - heads[i]->extent (heads[i], X_AXIS).linear_combination (CENTER);
515 heads[i]->translate_axis (amount, X_AXIS);
518 Real lastpos = Real (Staff_symbol_referencer::get_position (heads[0]));
519 for (vsize i = 1; i < heads.size (); i++)
521 Real p = Staff_symbol_referencer::get_position (heads[i]);
522 Real dy = fabs (lastpos - p);
525 dy should always be 0.5, 0.0, 1.0, but provide safety margin
532 Real ell = heads[i]->extent (heads[i], X_AXIS).length ();
534 Direction d = get_grob_direction (me);
536 Reversed head should be shifted ell-thickness, but this
537 looks too crowded, so we only shift ell-0.5*thickness.
539 This leads to assymetry: Normal heads overlap the
540 stem 100% whereas reversed heads only overlaps the
544 Real reverse_overlap = 0.5;
545 heads[i]->translate_axis ((ell - thick * reverse_overlap) * d,
548 if (is_invisible (me))
549 heads[i]->translate_axis (-thick * (2 - reverse_overlap) * d,
554 For some cases we should kern some more: when the
555 distance between the next or prev note is too large, we'd
556 get large white gaps, eg.
577 MAKE_SCHEME_CALLBACK (Stem, calc_direction, 1);
579 Stem::calc_direction (SCM smob)
581 Grob *me = unsmob_grob (smob);
582 Direction dir = CENTER;
583 if (Grob *beam = unsmob_grob (me->get_object ("beam")))
585 SCM ignore_me = beam->get_property ("direction");
587 dir = get_grob_direction (me);
591 SCM dd = me->get_property ("default-direction");
594 return me->get_property ("neutral-direction");
597 return scm_from_int (dir);
600 MAKE_SCHEME_CALLBACK (Stem, calc_default_direction, 1);
602 Stem::calc_default_direction (SCM smob)
604 Grob *me = unsmob_grob (smob);
606 Direction dir = CENTER;
607 int staff_center = 0;
608 Interval hp = head_positions (me);
611 int udistance = (int) (UP * hp[UP] - staff_center);
612 int ddistance = (int) (DOWN * hp[DOWN] - staff_center);
614 dir = Direction (sign (ddistance - udistance));
617 return scm_from_int (dir);
620 // note - height property necessary to trigger quantized beam positions
621 // otherwise, we could just use Grob::stencil_height_proc
622 MAKE_SCHEME_CALLBACK (Stem, height, 1);
624 Stem::height (SCM smob)
626 Grob *me = unsmob_grob (smob);
627 return ly_interval2scm (internal_height (me, true));
631 Stem::get_reference_head (Grob *me)
633 return to_boolean (me->get_property ("avoid-note-head"))
639 Stem::beam_end_corrective (Grob *me)
641 Grob *beam = unsmob_grob (me->get_object ("beam"));
642 Direction dir = get_grob_direction (me);
647 programming_error ("no stem direction");
650 return dir * Beam::get_beam_thickness (beam) * 0.5;
656 Stem::internal_height (Grob *me, bool calc_beam)
658 if (!is_valid_stem (me))
661 Direction dir = get_grob_direction (me);
663 Grob *beam = get_beam (me);
664 if (beam && calc_beam)
666 /* trigger set-stem-lengths. */
667 (void) beam->get_property ("quantized-positions");
668 return me->extent (me, Y_AXIS);
671 Real y2 = internal_calc_stem_end_position (me, calc_beam);
672 Real y1 = internal_calc_stem_begin_position (me, calc_beam);
674 Real half_space = Staff_symbol_referencer::staff_space (me) * 0.5;
676 Interval stem_y = Interval (min (y1, y2), max (y2, y1)) * half_space;
677 stem_y[dir] += beam_end_corrective (me);
682 MAKE_SCHEME_CALLBACK (Stem, width, 1);
686 Grob *me = unsmob_grob (e);
690 if (is_invisible (me))
694 r = Interval (-1, 1);
695 r *= thickness (me) / 2;
698 return ly_interval2scm (r);
702 Stem::thickness (Grob *me)
704 return scm_to_double (me->get_property ("thickness"))
705 * Staff_symbol_referencer::line_thickness (me);
708 MAKE_SCHEME_CALLBACK (Stem, calc_stem_begin_position, 1);
710 Stem::calc_stem_begin_position (SCM smob)
712 Grob *me = unsmob_grob (smob);
713 return scm_from_double (internal_calc_stem_begin_position (me, true));
716 MAKE_SCHEME_CALLBACK (Stem, pure_calc_stem_begin_position, 3);
718 Stem::pure_calc_stem_begin_position (SCM smob,
722 Grob *me = unsmob_grob (smob);
723 return scm_from_double (internal_calc_stem_begin_position (me, false));
727 Stem::internal_calc_stem_begin_position (Grob *me, bool calc_beam)
729 Grob *beam = get_beam (me);
730 Real ss = Staff_symbol_referencer::staff_space (me);
731 if (beam && calc_beam)
733 (void) beam->get_property ("quantized-positions");
734 return me->extent (me, Y_AXIS)[-get_grob_direction (me)] * ss * 2;
737 Direction d = get_grob_direction (me);
738 Grob *lh = get_reference_head (me);
740 Real pos = Staff_symbol_referencer::get_position (lh);
742 if (Grob *head = support_head (me))
744 Interval head_height = head->extent (head, Y_AXIS);
745 Real y_attach = Note_head::stem_attachment_coordinate (head, Y_AXIS);
747 y_attach = head_height.linear_combination (y_attach);
748 pos += d * y_attach * 2 / ss;
755 Stem::is_valid_stem (Grob *me)
757 /* TODO: make the stem start a direction ?
758 This is required to avoid stems passing in tablature chords. */
759 Real stemlet_length = robust_scm2double (me->get_property ("stemlet-length"),
761 bool stemlet = stemlet_length > 0.0;
763 Grob *lh = get_reference_head (me);
764 Grob *beam = unsmob_grob (me->get_object ("beam"));
769 if (!lh && stemlet && !beam)
772 if (lh && robust_scm2int (lh->get_property ("duration-log"), 0) < 1)
775 if (is_invisible (me))
781 MAKE_SCHEME_CALLBACK (Stem, print, 1);
783 Stem::print (SCM smob)
785 Grob *me = unsmob_grob (smob);
786 if (!is_valid_stem (me))
789 Interval stem_y = me->extent (me, Y_AXIS);
790 Direction dir = get_grob_direction (me);
792 stem_y[dir] -= beam_end_corrective (me);
795 Real stem_width = thickness (me);
797 = me->layout ()->get_dimension (ly_symbol2scm ("blot-diameter"));
799 Box b = Box (Interval (-stem_width / 2, stem_width / 2),
803 Stencil ss = Lookup::round_filled_box (b, blot);
804 mol.add_stencil (ss);
806 return mol.smobbed_copy ();
810 move the stem to right of the notehead if it is up.
812 MAKE_SCHEME_CALLBACK (Stem, offset_callback, 1);
814 Stem::offset_callback (SCM smob)
816 Grob *me = unsmob_grob (smob);
818 extract_grob_set (me, "rests", rests);
821 Grob *rest = rests.back ();
822 Real r = rest->extent (rest, X_AXIS).center ();
823 return scm_from_double (r);
826 if (Grob *f = first_head (me))
828 Interval head_wid = f->extent (f, X_AXIS);
831 if (is_invisible (me))
834 attach = Note_head::stem_attachment_coordinate (f, X_AXIS);
836 Direction d = get_grob_direction (me);
837 Real real_attach = head_wid.linear_combination (d * attach);
838 Real r = real_attach;
840 /* If not centered: correct for stem thickness. */
843 Real rule_thick = thickness (me);
844 r += -d * rule_thick * 0.5;
846 return scm_from_double (r);
849 programming_error ("Weird stem.");
850 return scm_from_double (0.0);
854 Stem::get_beam (Grob *me)
856 SCM b = me->get_object ("beam");
857 return dynamic_cast<Spanner *> (unsmob_grob (b));
861 Stem::get_stem_info (Grob *me)
864 si.dir_ = get_grob_direction (me);
866 SCM scm_info = me->get_property ("stem-info");
867 si.ideal_y_ = scm_to_double (scm_car (scm_info));
868 si.shortest_y_ = scm_to_double (scm_cadr (scm_info));
872 MAKE_SCHEME_CALLBACK (Stem, calc_stem_info, 1);
874 Stem::calc_stem_info (SCM smob)
876 Grob *me = unsmob_grob (smob);
877 Direction my_dir = get_grob_direction (me);
881 programming_error ("no stem dir set");
885 Real staff_space = Staff_symbol_referencer::staff_space (me);
886 Grob *beam = get_beam (me);
890 (void) beam->get_property ("beaming");
893 Real beam_translation = Beam::get_beam_translation (beam);
894 Real beam_thickness = Beam::get_beam_thickness (beam);
895 int beam_count = Beam::get_direction_beam_count (beam, my_dir);
897 = robust_scm2double (me->get_property ("length-fraction"), 1.0);
899 /* Simple standard stem length */
900 SCM details = me->get_property ("details");
901 SCM lengths = ly_assoc_get (ly_symbol2scm ("beamed-lengths"), details, SCM_EOL);
904 = (scm_is_pair (lengths)
905 ? (scm_to_double (robust_list_ref (beam_count - 1, lengths))
909 stem only extends to center of beam
911 - 0.5 * beam_thickness)
914 /* Condition: sane minimum free stem length (chord to beams) */
915 lengths = ly_assoc_get (ly_symbol2scm ("beamed-minimum-free-lengths"),
918 Real ideal_minimum_free
919 = (scm_is_pair (lengths)
920 ? (scm_to_double (robust_list_ref (beam_count - 1, lengths))
925 Real height_of_my_trem = 0.0;
926 Grob *trem = unsmob_grob (me->get_object ("tremolo-flag"));
930 = Stem_tremolo::vertical_length (trem)
931 /* 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 { \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_is_pair (bemfl)
994 ? (scm_to_double (robust_list_ref (beam_count - 1, bemfl))
999 Real minimum_length = max (minimum_free, height_of_my_trem)
1000 + height_of_my_beams
1001 /* stem only extends to center of beam */
1002 - 0.5 * beam_thickness;
1005 Real minimum_y = note_start + minimum_length;
1006 Real shortest_y = minimum_y * my_dir;
1008 return scm_list_2 (scm_from_double (ideal_y),
1009 scm_from_double (shortest_y));
1013 Stem::beam_multiplicity (Grob *stem)
1015 SCM beaming = stem->get_property ("beaming");
1016 Slice le = int_list_to_slice (scm_car (beaming));
1017 Slice ri = int_list_to_slice (scm_cdr (beaming));
1023 Stem::is_cross_staff (Grob *stem)
1025 Grob *beam = unsmob_grob (stem->get_object ("beam"));
1026 return beam && Beam::is_cross_staff (beam);
1029 MAKE_SCHEME_CALLBACK (Stem, calc_cross_staff, 1)
1031 Stem::calc_cross_staff (SCM smob)
1033 return scm_from_bool (is_cross_staff (unsmob_grob (smob)));
1037 Stem::flag (Grob *me)
1039 return unsmob_grob (me->get_object ("flag"));
1042 /* FIXME: Too many properties */
1043 ADD_INTERFACE (Stem,
1044 "The stem represents the graphical stem. In addition, it"
1045 " internally connects note heads, beams, and tremolos. Rests"
1046 " and whole notes have invisible stems.\n"
1048 "The following properties may be set in the @code{details}"
1052 "@item beamed-lengths\n"
1053 "List of stem lengths given beam multiplicity.\n"
1054 "@item beamed-minimum-free-lengths\n"
1055 "List of normal minimum free stem lengths (chord to beams)"
1056 " given beam multiplicity.\n"
1057 "@item beamed-extreme-minimum-free-lengths\n"
1058 "List of extreme minimum free stem lengths (chord to beams)"
1059 " given beam multiplicity.\n"
1061 "Default stem lengths. The list gives a length for each"
1063 "@item stem-shorten\n"
1064 "How much a stem in a forced direction should be shortened."
1065 " The list gives an amount depending on the number of flags"
1073 "beamlet-default-length "
1074 "beamlet-max-length-proportion "
1075 "default-direction "
1083 "neutral-direction "
1094 /****************************************************************/
1096 Stem_info::Stem_info ()
1098 ideal_y_ = shortest_y_ = 0;
1103 Stem_info::scale (Real x)