2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 1996--2012 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 // trigger note collision mechanisms
131 Real stem_beg = internal_calc_stem_begin_position (me, false);
132 Real staff_space = Staff_symbol_referencer::staff_space (me);
133 Real half_space = staff_space * 0.5;
136 height[-d] = stem_beg * half_space;
137 height[d] = se * half_space + beam_end_corrective (me);
139 Real stemlet_length = robust_scm2double (me->get_property ("stemlet-length"),
141 bool stemlet = stemlet_length > 0.0;
143 Grob *lh = get_reference_head (me);
149 Real beam_translation = Beam::get_beam_translation (beam);
150 Real beam_thickness = Beam::get_beam_thickness (beam);
151 int beam_count = beam_multiplicity (me).length () + 1;
153 height[-d] = (height[d] - d
154 * (0.5 * beam_thickness
155 + beam_translation * max (0, (beam_count - 1))
158 else if (!stemlet && beam)
159 height[-d] = height[d];
160 else if (stemlet && !beam)
161 me->programming_error ("Can't have a stemlet without a beam.");
164 me->set_property ("stem-begin-position", scm_from_double (height[-d] * 2 / staff_space));
165 me->set_property ("length", scm_from_double (height.length () * 2 / staff_space));
168 /* Note head that determines hshift for upstems
169 WARNING: triggers direction */
171 Stem::support_head (Grob *me)
173 extract_grob_set (me, "note-heads", heads);
174 if (heads.size () == 1)
177 return first_head (me);
181 Stem::head_count (Grob *me)
183 return Pointer_group_interface::count (me, ly_symbol2scm ("note-heads"));
186 /* The note head which forms one end of the stem.
187 WARNING: triggers direction */
189 Stem::first_head (Grob *me)
191 Direction d = get_grob_direction (me);
193 return extremal_heads (me)[-d];
197 /* The note head opposite to the first head. */
199 Stem::last_head (Grob *me)
201 Direction d = get_grob_direction (me);
203 return extremal_heads (me)[d];
208 START is part where stem reaches `last' head.
210 This function returns a drul with (bottom-head, top-head).
213 Stem::extremal_heads (Grob *me)
215 const int inf = INT_MAX;
216 Drul_array<int> extpos;
220 Drul_array<Grob *> exthead (0, 0);
221 extract_grob_set (me, "note-heads", heads);
223 for (vsize i = heads.size (); i--;)
226 int p = Staff_symbol_referencer::get_rounded_position (n);
228 for (LEFT_and_RIGHT (d))
230 if (d * p > d * extpos[d])
240 /* The staff positions, in ascending order.
241 * If FILTER, include the main column of noteheads only */
243 Stem::note_head_positions (Grob *me, bool filter)
246 extract_grob_set (me, "note-heads", heads);
247 Grob *xref = common_refpoint_of_array (heads, me, X_AXIS);
249 for (vsize i = heads.size (); i--;)
253 && n->relative_coordinate (xref, X_AXIS) != 0.0)
256 int p = Staff_symbol_referencer::get_rounded_position (n);
260 vector_sort (ps, less<int> ());
265 Stem::add_head (Grob *me, Grob *n)
267 n->set_object ("stem", me->self_scm ());
269 if (Note_head::has_interface (n))
270 Pointer_group_interface::add_grob (me, ly_symbol2scm ("note-heads"), n);
271 else if (Rest::has_interface (n))
272 Pointer_group_interface::add_grob (me, ly_symbol2scm ("rests"), n);
276 Stem::is_invisible (Grob *me)
278 if (is_normal_stem (me))
280 else if (head_count (me))
282 else // if there are no note-heads, we might want stemlets
283 return 0.0 == robust_scm2double (me->get_property ("stemlet-length"), 0.0);
287 Stem::is_normal_stem (Grob *me)
289 if (!head_count (me))
292 extract_grob_set (me, "note-heads", heads);
293 SCM style = heads[0]->get_property ("style");
294 return style != ly_symbol2scm ("kievan") && scm_to_int (me->get_property ("duration-log")) >= 1;
297 MAKE_SCHEME_CALLBACK (Stem, pure_height, 3)
299 Stem::pure_height (SCM smob,
303 Grob *me = unsmob_grob (smob);
304 return ly_interval2scm (internal_pure_height (me, true));
308 Stem::internal_pure_height (Grob *me, bool calc_beam)
310 if (!is_normal_stem (me))
311 return Interval (0.0, 0.0);
313 Grob *beam = unsmob_grob (me->get_object ("beam"));
315 Interval iv = internal_height (me, false);
322 Direction dir = get_grob_direction (me);
323 for (DOWN_and_UP (d))
324 overshoot[d] = d == dir ? dir * infinity_f : iv[d];
326 vector<Interval> heights;
327 vector<Grob *> my_stems;
328 extract_grob_set (beam, "normal-stems", normal_stems);
329 for (vsize i = 0; i < normal_stems.size (); i++)
330 if (get_grob_direction (normal_stems[i]) == dir)
332 if (normal_stems[i] != me)
333 heights.push_back (Stem::internal_pure_height (normal_stems[i], false));
335 heights.push_back (iv);
336 my_stems.push_back (normal_stems[i]);
338 //iv.unite (heights.back ());
339 // look for cross staff effects
341 Grob *common = common_refpoint_of_array (my_stems, me, Y_AXIS);
342 Real min_pos = infinity_f;
343 Real max_pos = -infinity_f;
344 for (vsize i = 0; i < my_stems.size (); i++)
346 coords.push_back (my_stems[i]->pure_relative_y_coordinate (common, 0, INT_MAX));
347 min_pos = min (min_pos, coords[i]);
348 max_pos = max (max_pos, coords[i]);
350 for (vsize i = 0; i < heights.size (); i++)
352 heights[i][dir] += dir == DOWN
353 ? coords[i] - max_pos
354 : coords[i] - min_pos;
357 for (vsize i = 0; i < heights.size (); i++) iv.unite (heights[i]);
359 for (vsize i = 0; i < my_stems.size (); i++)
360 cache_pure_height (my_stems[i], iv, heights[i]);
361 iv.intersect (overshoot);
368 Stem::cache_pure_height (Grob *me, Interval iv, Interval my_iv)
371 Direction dir = get_grob_direction (me);
372 for (DOWN_and_UP (d))
373 overshoot[d] = d == dir ? dir * infinity_f : my_iv[d];
375 iv.intersect (overshoot);
376 dynamic_cast<Item *> (me)->cache_pure_height (iv);
379 MAKE_SCHEME_CALLBACK (Stem, calc_stem_end_position, 1)
381 Stem::calc_stem_end_position (SCM smob)
383 Grob *me = unsmob_grob (smob);
384 return scm_from_double (internal_calc_stem_end_position (me, true));
387 MAKE_SCHEME_CALLBACK (Stem, pure_calc_stem_end_position, 3)
389 Stem::pure_calc_stem_end_position (SCM smob,
393 Grob *me = unsmob_grob (smob);
394 return scm_from_double (internal_calc_stem_end_position (me, false));
398 Stem::internal_calc_stem_end_position (Grob *me, bool calc_beam)
400 if (!head_count (me))
403 Grob *beam = get_beam (me);
404 Real ss = Staff_symbol_referencer::staff_space (me);
405 Direction dir = get_grob_direction (me);
407 if (beam && calc_beam)
409 (void) beam->get_property ("quantized-positions");
410 return robust_scm2double (me->get_property ("length"), 0.0)
411 + dir * robust_scm2double (me->get_property ("stem-begin-position"), 0.0);
416 /* WARNING: IN HALF SPACES */
417 SCM details = me->get_property ("details");
418 int durlog = duration_log (me);
420 Real staff_rad = Staff_symbol_referencer::staff_radius (me);
422 SCM s = ly_assoc_get (ly_symbol2scm ("lengths"), details, SCM_EOL);
424 length = 2 * scm_to_double (robust_list_ref (durlog - 2, s));
426 /* Stems in unnatural (forced) direction should be shortened,
427 according to [Roush & Gourlay] */
428 Interval hp = head_positions (me);
429 if (dir && dir * hp[dir] >= 0)
431 SCM sshorten = ly_assoc_get (ly_symbol2scm ("stem-shorten"), details, SCM_EOL);
432 SCM scm_shorten = scm_is_pair (sshorten)
433 ? robust_list_ref (max (duration_log (me) - 2, 0), sshorten) : SCM_EOL;
434 Real shorten_property = 2 * robust_scm2double (scm_shorten, 0);
435 /* change in length between full-size and shortened stems is executed gradually.
436 "transition area" = stems between full-sized and fully-shortened.
438 Real quarter_stem_length = 2 * scm_to_double (robust_list_ref (0, s));
439 /* shortening_step = difference in length between consecutive stem lengths
440 in transition area. The bigger the difference between full-sized
441 and shortened stems, the bigger shortening_step is.
442 (but not greater than 1/2 and not smaller than 1/4).
443 value 6 is heuristic; it determines the suggested transition slope steepnesas.
445 Real shortening_step = min (max (0.25, (shorten_property / 6)), 0.5);
446 /* Shortening of unflagged stems should begin on the first stem that sticks
447 more than 1 staffspace (2 units) out of the staff.
448 Shortening of flagged stems begins in the same moment as unflagged ones,
449 but not earlier than on the middle line note.
451 Real which_step = (min (1.0, quarter_stem_length - (2 * staff_rad) - 2.0)) + abs (hp[dir]);
452 Real shorten = min (max (0.0, (shortening_step * which_step)), shorten_property);
457 length *= robust_scm2double (me->get_property ("length-fraction"), 1.0);
460 Grob *t_flag = unsmob_grob (me->get_object ("tremolo-flag"));
461 if (t_flag && (!unsmob_grob (me->get_object ("beam")) || !calc_beam))
463 /* Crude hack: add extra space if tremolo flag is there.
465 We can't do this for the beam, since we get into a loop
466 (Stem_tremolo::raw_stencil () looks at the beam.) --hwn */
469 + 2 * Stem_tremolo::vertical_length (t_flag) / ss;
471 /* We don't want to add the whole extent of the flag because the trem
472 and the flag can overlap partly. beam_translation gives a good
476 Real beam_trans = Stem_tremolo::get_beam_translation (t_flag);
477 /* the obvious choice is (durlog - 2) here, but we need a bit more space. */
478 minlen += 2 * (durlog - 1.5) * beam_trans;
480 /* up-stems need even a little more space to avoid collisions. This
481 needs to be in sync with the tremolo positioning code in
482 Stem_tremolo::print */
484 minlen += beam_trans;
486 length = max (length, minlen + 1.0);
489 Real stem_end = dir ? hp[dir] + dir * length : 0;
491 /* TODO: change name to extend-stems to staff/center/'() */
492 bool no_extend = to_boolean (me->get_property ("no-stem-extend"));
493 if (!no_extend && dir * stem_end < 0)
499 /* The log of the duration (Number of hooks on the flag minus two) */
501 Stem::duration_log (Grob *me)
503 SCM s = me->get_property ("duration-log");
504 return (scm_is_number (s)) ? scm_to_int (s) : 2;
507 MAKE_SCHEME_CALLBACK (Stem, calc_positioning_done, 1);
509 Stem::calc_positioning_done (SCM smob)
511 Grob *me = unsmob_grob (smob);
512 if (!head_count (me))
515 me->set_property ("positioning-done", SCM_BOOL_T);
517 extract_grob_set (me, "note-heads", ro_heads);
518 vector<Grob *> heads (ro_heads);
519 vector_sort (heads, position_less);
520 Direction dir = get_grob_direction (me);
525 Real thick = thickness (me);
527 Grob *hed = support_head (me);
530 programming_error ("Stem dir must be up or down.");
532 set_grob_direction (me, dir);
535 bool is_harmonic_centered = false;
536 for (vsize i = 0; i < heads.size (); i++)
537 is_harmonic_centered = is_harmonic_centered
538 || heads[i]->get_property ("style") == ly_symbol2scm ("harmonic");
539 is_harmonic_centered = is_harmonic_centered && is_invisible (me);
541 Real w = hed->extent (hed, X_AXIS)[dir];
542 for (vsize i = 0; i < heads.size (); i++)
544 Real amount = w - heads[i]->extent (heads[i], X_AXIS)[dir];
546 if (is_harmonic_centered)
548 = hed->extent (hed, X_AXIS).linear_combination (CENTER)
549 - heads[i]->extent (heads[i], X_AXIS).linear_combination (CENTER);
551 if (!isnan (amount)) // empty heads can produce NaN
552 heads[i]->translate_axis (amount, X_AXIS);
555 Real lastpos = Real (Staff_symbol_referencer::get_position (heads[0]));
556 for (vsize i = 1; i < heads.size (); i++)
558 Real p = Staff_symbol_referencer::get_position (heads[i]);
559 Real dy = fabs (lastpos - p);
562 dy should always be 0.5, 0.0, 1.0, but provide safety margin
569 Real ell = heads[i]->extent (heads[i], X_AXIS).length ();
571 Direction d = get_grob_direction (me);
573 Reversed head should be shifted ell-thickness, but this
574 looks too crowded, so we only shift ell-0.5*thickness.
576 This leads to assymetry: Normal heads overlap the
577 stem 100% whereas reversed heads only overlaps the
581 Real reverse_overlap = 0.5;
582 heads[i]->translate_axis ((ell - thick * reverse_overlap) * d,
585 if (is_invisible (me))
586 heads[i]->translate_axis (-thick * (2 - reverse_overlap) * d,
591 For some cases we should kern some more: when the
592 distance between the next or prev note is too large, we'd
593 get large white gaps, eg.
614 MAKE_SCHEME_CALLBACK (Stem, calc_direction, 1);
616 Stem::calc_direction (SCM smob)
618 Grob *me = unsmob_grob (smob);
619 Direction dir = CENTER;
620 if (Grob *beam = unsmob_grob (me->get_object ("beam")))
622 SCM ignore_me = beam->get_property ("direction");
624 dir = get_grob_direction (me);
628 SCM dd = me->get_property ("default-direction");
631 return me->get_property ("neutral-direction");
634 return scm_from_int (dir);
637 MAKE_SCHEME_CALLBACK (Stem, calc_default_direction, 1);
639 Stem::calc_default_direction (SCM smob)
641 Grob *me = unsmob_grob (smob);
643 Direction dir = CENTER;
644 int staff_center = 0;
645 Interval hp = head_positions (me);
648 int udistance = (int) (UP * hp[UP] - staff_center);
649 int ddistance = (int) (DOWN * hp[DOWN] - staff_center);
651 dir = Direction (sign (ddistance - udistance));
654 return scm_from_int (dir);
657 // note - height property necessary to trigger quantized beam positions
658 // otherwise, we could just use Grob::stencil_height_proc
659 MAKE_SCHEME_CALLBACK (Stem, height, 1);
661 Stem::height (SCM smob)
663 Grob *me = unsmob_grob (smob);
664 return ly_interval2scm (internal_height (me, true));
668 Stem::get_reference_head (Grob *me)
670 return to_boolean (me->get_property ("avoid-note-head"))
676 Stem::beam_end_corrective (Grob *me)
678 Grob *beam = unsmob_grob (me->get_object ("beam"));
679 Direction dir = get_grob_direction (me);
684 programming_error ("no stem direction");
687 return dir * Beam::get_beam_thickness (beam) * 0.5;
693 Stem::internal_height (Grob *me, bool calc_beam)
695 Grob *beam = get_beam (me);
696 if (!is_valid_stem (me) && !beam)
699 Direction dir = get_grob_direction (me);
701 if (beam && calc_beam)
703 /* trigger set-stem-lengths. */
704 (void) beam->get_property ("quantized-positions");
708 If there is a beam but no stem, slope calculations depend on this
709 routine to return where the stem end /would/ be.
711 if (calc_beam && !beam && !unsmob_stencil (me->get_property ("stencil")))
714 Real y1 = robust_scm2double ((calc_beam
715 ? me->get_property ("stem-begin-position")
716 : me->get_pure_property ("stem-begin-position", 0, INT_MAX)),
719 Real y2 = dir * robust_scm2double ((calc_beam
720 ? me->get_property ("length")
721 : me->get_pure_property ("length", 0, INT_MAX)),
725 Real half_space = Staff_symbol_referencer::staff_space (me) * 0.5;
727 Interval stem_y = Interval (min (y1, y2), max (y2, y1)) * half_space;
732 MAKE_SCHEME_CALLBACK (Stem, width, 1);
736 Grob *me = unsmob_grob (e);
740 if (is_invisible (me))
744 r = Interval (-1, 1);
745 r *= thickness (me) / 2;
748 return ly_interval2scm (r);
752 Stem::thickness (Grob *me)
754 return scm_to_double (me->get_property ("thickness"))
755 * Staff_symbol_referencer::line_thickness (me);
758 MAKE_SCHEME_CALLBACK (Stem, calc_stem_begin_position, 1);
760 Stem::calc_stem_begin_position (SCM smob)
762 Grob *me = unsmob_grob (smob);
763 return scm_from_double (internal_calc_stem_begin_position (me, true));
766 MAKE_SCHEME_CALLBACK (Stem, pure_calc_stem_begin_position, 3);
768 Stem::pure_calc_stem_begin_position (SCM smob,
772 Grob *me = unsmob_grob (smob);
773 return scm_from_double (internal_calc_stem_begin_position (me, false));
777 Stem::internal_calc_stem_begin_position (Grob *me, bool calc_beam)
779 Grob *beam = get_beam (me);
780 Real ss = Staff_symbol_referencer::staff_space (me);
781 if (beam && calc_beam)
783 (void) beam->get_property ("quantized-positions");
784 return robust_scm2double (me->get_property ("stem-begin-position"), 0.0);
787 Direction d = get_grob_direction (me);
788 Grob *lh = get_reference_head (me);
793 Real pos = Staff_symbol_referencer::get_position (lh);
795 if (Grob *head = support_head (me))
797 Interval head_height = head->extent (head, Y_AXIS);
798 Real y_attach = Note_head::stem_attachment_coordinate (head, Y_AXIS);
800 y_attach = head_height.linear_combination (y_attach);
801 if (!isinf (y_attach) && !isnan (y_attach)) // empty heads
802 pos += d * y_attach * 2 / ss;
809 Stem::is_valid_stem (Grob *me)
811 /* TODO: make the stem start a direction ?
812 This is required to avoid stems passing in tablature chords. */
813 Grob *lh = get_reference_head (me);
814 Grob *beam = unsmob_grob (me->get_object ("beam"));
819 if (is_invisible (me))
825 MAKE_SCHEME_CALLBACK (Stem, print, 1);
827 Stem::print (SCM smob)
829 Grob *me = unsmob_grob (smob);
830 if (!is_valid_stem (me))
833 Direction dir = get_grob_direction (me);
834 Real y1 = robust_scm2double (me->get_property ("stem-begin-position"), 0.0);
835 Real y2 = dir * robust_scm2double (me->get_property ("length"), 0.0) + y1;
837 Real half_space = Staff_symbol_referencer::staff_space (me) * 0.5;
839 Interval stem_y = Interval (min (y1, y2), max (y2, y1)) * half_space;
841 stem_y[dir] -= beam_end_corrective (me);
844 Real stem_width = thickness (me);
846 = me->layout ()->get_dimension (ly_symbol2scm ("blot-diameter"));
848 Box b = Box (Interval (-stem_width / 2, stem_width / 2),
852 Stencil ss = Lookup::round_filled_box (b, blot);
853 mol.add_stencil (ss);
855 return mol.smobbed_copy ();
859 move the stem to right of the notehead if it is up.
861 MAKE_SCHEME_CALLBACK (Stem, offset_callback, 1);
863 Stem::offset_callback (SCM smob)
865 Grob *me = unsmob_grob (smob);
867 extract_grob_set (me, "rests", rests);
870 Grob *rest = rests.back ();
871 Real r = rest->extent (rest, X_AXIS).center ();
872 return scm_from_double (r);
875 if (Grob *f = first_head (me))
877 Interval head_wid = f->extent (f, X_AXIS);
880 if (is_invisible (me))
883 attach = Note_head::stem_attachment_coordinate (f, X_AXIS);
885 Direction d = get_grob_direction (me);
886 Real real_attach = head_wid.linear_combination (d * attach);
887 Real r = isnan(real_attach)? 0.0: real_attach;
889 /* If not centered: correct for stem thickness. */
890 string style = robust_symbol2string (f->get_property ("style"), "default");
891 if (attach && style != "mensural"
892 && style != "neomensural"
893 && style != "petrucci")
895 Real rule_thick = thickness (me);
896 r += -d * rule_thick * 0.5;
898 return scm_from_double (r);
901 programming_error ("Weird stem.");
902 return scm_from_double (0.0);
906 Stem::get_beam (Grob *me)
908 SCM b = me->get_object ("beam");
909 return dynamic_cast<Spanner *> (unsmob_grob (b));
913 Stem::get_stem_info (Grob *me)
916 si.dir_ = get_grob_direction (me);
918 SCM scm_info = me->get_property ("stem-info");
919 si.ideal_y_ = scm_to_double (scm_car (scm_info));
920 si.shortest_y_ = scm_to_double (scm_cadr (scm_info));
924 MAKE_SCHEME_CALLBACK (Stem, calc_stem_info, 1);
926 Stem::calc_stem_info (SCM smob)
928 Grob *me = unsmob_grob (smob);
929 Direction my_dir = get_grob_direction (me);
933 programming_error ("no stem dir set");
937 Real staff_space = Staff_symbol_referencer::staff_space (me);
938 Grob *beam = get_beam (me);
942 (void) beam->get_property ("beaming");
945 Real beam_translation = Beam::get_beam_translation (beam);
946 Real beam_thickness = Beam::get_beam_thickness (beam);
947 int beam_count = Beam::get_direction_beam_count (beam, my_dir);
949 = robust_scm2double (me->get_property ("length-fraction"), 1.0);
951 /* Simple standard stem length */
952 SCM details = me->get_property ("details");
953 SCM lengths = ly_assoc_get (ly_symbol2scm ("beamed-lengths"), details, SCM_EOL);
956 = (scm_is_pair (lengths)
957 ? (scm_to_double (robust_list_ref (beam_count - 1, lengths))
961 stem only extends to center of beam
963 - 0.5 * beam_thickness)
966 /* Condition: sane minimum free stem length (chord to beams) */
967 lengths = ly_assoc_get (ly_symbol2scm ("beamed-minimum-free-lengths"),
970 Real ideal_minimum_free
971 = (scm_is_pair (lengths)
972 ? (scm_to_double (robust_list_ref (beam_count - 1, lengths))
977 Real height_of_my_trem = 0.0;
978 Grob *trem = unsmob_grob (me->get_object ("tremolo-flag"));
982 = Stem_tremolo::vertical_length (trem)
983 /* hack a bit of space around the trem. */
988 It seems that also for ideal minimum length, we must use
989 the maximum beam count (for this direction):
991 \score { \relative c'' { a8[ a32] } }
993 must be horizontal. */
994 Real height_of_my_beams = beam_thickness
995 + (beam_count - 1) * beam_translation;
997 Real ideal_minimum_length = ideal_minimum_free
1000 /* stem only extends to center of beam */
1001 - 0.5 * beam_thickness;
1003 ideal_length = max (ideal_length, ideal_minimum_length);
1005 /* Convert to Y position, calculate for dir == UP */
1007 = /* staff positions */
1008 head_positions (me)[my_dir] * 0.5
1009 * my_dir * staff_space;
1010 Real ideal_y = note_start + ideal_length;
1012 /* Conditions for Y position */
1014 /* Lowest beam of (UP) beam must never be lower than second staffline
1018 Although this (additional) rule is probably correct,
1019 I expect that highest beam (UP) should also never be lower
1020 than middle staffline, just as normal stems.
1024 Obviously not for grace beams.
1026 Also, not for knees. Seems to be a good thing. */
1027 bool no_extend = to_boolean (me->get_property ("no-stem-extend"));
1028 bool is_knee = Beam::is_knee (beam);
1029 if (!no_extend && !is_knee)
1031 /* Highest beam of (UP) beam must never be lower than middle
1033 ideal_y = max (ideal_y, 0.0);
1034 /* Lowest beam of (UP) beam must never be lower than second staffline */
1035 ideal_y = max (ideal_y, (-staff_space
1036 - beam_thickness + height_of_my_beams));
1039 ideal_y -= robust_scm2double (beam->get_property ("shorten"), 0);
1041 SCM bemfl = ly_assoc_get (ly_symbol2scm ("beamed-extreme-minimum-free-lengths"),
1045 = (scm_is_pair (bemfl)
1046 ? (scm_to_double (robust_list_ref (beam_count - 1, bemfl))
1051 Real minimum_length = max (minimum_free, height_of_my_trem)
1052 + height_of_my_beams
1053 /* stem only extends to center of beam */
1054 - 0.5 * beam_thickness;
1057 Real minimum_y = note_start + minimum_length;
1058 Real shortest_y = minimum_y * my_dir;
1060 return scm_list_2 (scm_from_double (ideal_y),
1061 scm_from_double (shortest_y));
1065 Stem::beam_multiplicity (Grob *stem)
1067 SCM beaming = stem->get_property ("beaming");
1068 Slice le = int_list_to_slice (scm_car (beaming));
1069 Slice ri = int_list_to_slice (scm_cdr (beaming));
1075 Stem::is_cross_staff (Grob *stem)
1077 Grob *beam = unsmob_grob (stem->get_object ("beam"));
1078 return beam && Beam::is_cross_staff (beam);
1081 MAKE_SCHEME_CALLBACK (Stem, calc_cross_staff, 1)
1083 Stem::calc_cross_staff (SCM smob)
1085 return scm_from_bool (is_cross_staff (unsmob_grob (smob)));
1089 Stem::flag (Grob *me)
1091 return unsmob_grob (me->get_object ("flag"));
1094 /* FIXME: Too many properties */
1095 ADD_INTERFACE (Stem,
1096 "The stem represents the graphical stem. In addition, it"
1097 " internally connects note heads, beams, and tremolos. Rests"
1098 " and whole notes have invisible stems.\n"
1100 "The following properties may be set in the @code{details}"
1104 "@item beamed-lengths\n"
1105 "List of stem lengths given beam multiplicity.\n"
1106 "@item beamed-minimum-free-lengths\n"
1107 "List of normal minimum free stem lengths (chord to beams)"
1108 " given beam multiplicity.\n"
1109 "@item beamed-extreme-minimum-free-lengths\n"
1110 "List of extreme minimum free stem lengths (chord to beams)"
1111 " given beam multiplicity.\n"
1113 "Default stem lengths. The list gives a length for each"
1115 "@item stem-shorten\n"
1116 "How much a stem in a forced direction should be shortened."
1117 " The list gives an amount depending on the number of flags"
1125 "beamlet-default-length "
1126 "beamlet-max-length-proportion "
1127 "default-direction "
1137 "neutral-direction "
1142 "stem-begin-position "
1150 /****************************************************************/
1152 Stem_info::Stem_info ()
1154 ideal_y_ = shortest_y_ = 0;
1159 Stem_info::scale (Real x)