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 Interval height = me->pure_height (me, 0, INT_MAX);
131 Real staff_space = Staff_symbol_referencer::staff_space (me);
132 Real half_space = staff_space * 0.5;
134 height[d] = se * half_space + beam_end_corrective (me);
136 Real stemlet_length = robust_scm2double (me->get_property ("stemlet-length"),
138 bool stemlet = stemlet_length > 0.0;
140 Grob *lh = get_reference_head (me);
146 Real beam_translation = Beam::get_beam_translation (beam);
147 Real beam_thickness = Beam::get_beam_thickness (beam);
148 int beam_count = beam_multiplicity (me).length () + 1;
150 height[-d] = (height[d] - d
151 * (0.5 * beam_thickness
152 + beam_translation * max (0, (beam_count - 1))
155 else if (!stemlet && beam)
156 height[-d] = height[d];
157 else if (stemlet && !beam)
158 me->programming_error ("Can't have a stemlet without a beam.");
161 me->set_property ("stem-begin-position", scm_from_double (height[-d] * 2 / staff_space));
162 me->set_property ("length", scm_from_double (height.length () * 2 / staff_space));
165 /* Note head that determines hshift for upstems
166 WARNING: triggers direction */
168 Stem::support_head (Grob *me)
170 extract_grob_set (me, "note-heads", heads);
171 if (heads.size () == 1)
174 return first_head (me);
178 Stem::head_count (Grob *me)
180 return Pointer_group_interface::count (me, ly_symbol2scm ("note-heads"));
183 /* The note head which forms one end of the stem.
184 WARNING: triggers direction */
186 Stem::first_head (Grob *me)
188 Direction d = get_grob_direction (me);
190 return extremal_heads (me)[-d];
194 /* The note head opposite to the first head. */
196 Stem::last_head (Grob *me)
198 Direction d = get_grob_direction (me);
200 return extremal_heads (me)[d];
205 START is part where stem reaches `last' head.
207 This function returns a drul with (bottom-head, top-head).
210 Stem::extremal_heads (Grob *me)
212 const int inf = INT_MAX;
213 Drul_array<int> extpos;
217 Drul_array<Grob *> exthead (0, 0);
218 extract_grob_set (me, "note-heads", heads);
220 for (vsize i = heads.size (); i--;)
223 int p = Staff_symbol_referencer::get_rounded_position (n);
228 if (d * p > d * extpos[d])
234 while (flip (&d) != DOWN);
239 /* The positions, in ascending order. */
241 Stem::note_head_positions (Grob *me)
244 extract_grob_set (me, "note-heads", heads);
246 for (vsize i = heads.size (); i--;)
249 int p = Staff_symbol_referencer::get_rounded_position (n);
254 vector_sort (ps, less<int> ());
259 Stem::add_head (Grob *me, Grob *n)
261 n->set_object ("stem", me->self_scm ());
263 if (Note_head::has_interface (n))
264 Pointer_group_interface::add_grob (me, ly_symbol2scm ("note-heads"), n);
265 else if (Rest::has_interface (n))
266 Pointer_group_interface::add_grob (me, ly_symbol2scm ("rests"), n);
270 Stem::is_invisible (Grob *me)
272 return !is_normal_stem (me)
273 && (robust_scm2double (me->get_property ("stemlet-length"),
278 Stem::is_normal_stem (Grob *me)
280 return head_count (me) && scm_to_int (me->get_property ("duration-log")) >= 1;
283 MAKE_SCHEME_CALLBACK (Stem, pure_height, 3)
285 Stem::pure_height (SCM smob,
289 Grob *me = unsmob_grob (smob);
290 return ly_interval2scm (internal_pure_height (me, true));
294 Stem::internal_pure_height (Grob *me, bool calc_beam)
296 if (!is_normal_stem (me))
297 return Interval (0.0, 0.0);
299 Grob *beam = unsmob_grob (me->get_object ("beam"));
301 Interval iv = internal_height (me, false);
308 Direction dir = get_grob_direction (me);
311 overshoot[d] = d == dir ? dir * infinity_f : iv[d];
312 while (flip (&d) != DOWN);
314 vector<Interval> heights;
315 vector<Grob *> my_stems;
316 extract_grob_set (beam, "normal-stems", normal_stems);
317 for (vsize i = 0; i < normal_stems.size (); i++)
318 if (get_grob_direction (normal_stems[i]) == dir)
320 if (normal_stems[i] != me)
321 heights.push_back (Stem::internal_pure_height (normal_stems[i], false));
323 heights.push_back (iv);
324 my_stems.push_back (normal_stems[i]);
326 //iv.unite (heights.back ());
327 // look for cross staff effects
329 Grob *common = common_refpoint_of_array (my_stems, me, Y_AXIS);
330 Real min_pos = infinity_f;
331 Real max_pos = -infinity_f;
332 for (vsize i = 0; i < my_stems.size (); i++)
334 coords.push_back (my_stems[i]->pure_relative_y_coordinate (common, 0, INT_MAX));
335 min_pos = min (min_pos, coords[i]);
336 max_pos = max (max_pos, coords[i]);
338 for (vsize i = 0; i < heights.size (); i++)
340 heights[i][dir] += dir == DOWN
341 ? coords[i] - max_pos
342 : coords[i] - min_pos;
345 for (vsize i = 0; i < heights.size (); i++) iv.unite (heights[i]);
347 for (vsize i = 0; i < my_stems.size (); i++)
348 cache_pure_height (my_stems[i], iv, heights[i]);
349 iv.intersect (overshoot);
356 Stem::cache_pure_height (Grob *me, Interval iv, Interval my_iv)
359 Direction dir = get_grob_direction (me);
362 overshoot[d] = d == dir ? dir * infinity_f : my_iv[d];
363 while (flip (&d) != DOWN);
365 iv.intersect (overshoot);
366 dynamic_cast<Item *> (me)->cache_pure_height (iv);
369 MAKE_SCHEME_CALLBACK (Stem, calc_stem_end_position, 1)
371 Stem::calc_stem_end_position (SCM smob)
373 Grob *me = unsmob_grob (smob);
374 return scm_from_double (internal_calc_stem_end_position (me, true));
377 MAKE_SCHEME_CALLBACK (Stem, pure_calc_stem_end_position, 3)
379 Stem::pure_calc_stem_end_position (SCM smob,
383 Grob *me = unsmob_grob (smob);
384 return scm_from_double (internal_calc_stem_end_position (me, false));
388 Stem::internal_calc_stem_end_position (Grob *me, bool calc_beam)
390 if (!head_count (me))
393 Grob *beam = get_beam (me);
394 Real ss = Staff_symbol_referencer::staff_space (me);
395 Direction dir = get_grob_direction (me);
397 if (beam && calc_beam)
399 (void) beam->get_property ("quantized-positions");
400 return robust_scm2double (me->get_property ("length"), 0.0)
401 + dir * robust_scm2double (me->get_property ("stem-begin-position"), 0.0);
406 /* WARNING: IN HALF SPACES */
407 SCM details = me->get_property ("details");
408 int durlog = duration_log (me);
410 Real staff_rad = Staff_symbol_referencer::staff_radius (me);
412 SCM s = ly_assoc_get (ly_symbol2scm ("lengths"), details, SCM_EOL);
414 length = 2 * scm_to_double (robust_list_ref (durlog - 2, s));
416 /* Stems in unnatural (forced) direction should be shortened,
417 according to [Roush & Gourlay] */
418 Interval hp = head_positions (me);
419 if (dir && dir * hp[dir] >= 0)
421 SCM sshorten = ly_assoc_get (ly_symbol2scm ("stem-shorten"), details, SCM_EOL);
422 SCM scm_shorten = scm_is_pair (sshorten)
423 ? robust_list_ref (max (duration_log (me) - 2, 0), sshorten) : SCM_EOL;
424 Real shorten_property = 2 * robust_scm2double (scm_shorten, 0);
425 /* change in length between full-size and shortened stems is executed gradually.
426 "transition area" = stems between full-sized and fully-shortened.
428 Real quarter_stem_length = 2 * scm_to_double (robust_list_ref (0, s));
429 /* shortening_step = difference in length between consecutive stem lengths
430 in transition area. The bigger the difference between full-sized
431 and shortened stems, the bigger shortening_step is.
432 (but not greater than 1/2 and not smaller than 1/4).
433 value 6 is heuristic; it determines the suggested transition slope steepnesas.
435 Real shortening_step = min (max (0.25, (shorten_property / 6)), 0.5);
436 /* Shortening of unflagged stems should begin on the first stem that sticks
437 more than 1 staffspace (2 units) out of the staff.
438 Shortening of flagged stems begins in the same moment as unflagged ones,
439 but not earlier than on the middle line note.
441 Real which_step = (min (1.0, quarter_stem_length - (2 * staff_rad) - 2.0)) + abs (hp[dir]);
442 Real shorten = min (max (0.0, (shortening_step * which_step)), shorten_property);
447 length *= robust_scm2double (me->get_property ("length-fraction"), 1.0);
450 Grob *t_flag = unsmob_grob (me->get_object ("tremolo-flag"));
451 if (t_flag && (!unsmob_grob (me->get_object ("beam")) || !calc_beam))
453 /* Crude hack: add extra space if tremolo flag is there.
455 We can't do this for the beam, since we get into a loop
456 (Stem_tremolo::raw_stencil () looks at the beam.) --hwn */
459 + 2 * Stem_tremolo::vertical_length (t_flag) / ss;
461 /* We don't want to add the whole extent of the flag because the trem
462 and the flag can overlap partly. beam_translation gives a good
466 Real beam_trans = Stem_tremolo::get_beam_translation (t_flag);
467 /* the obvious choice is (durlog - 2) here, but we need a bit more space. */
468 minlen += 2 * (durlog - 1.5) * beam_trans;
470 /* up-stems need even a little more space to avoid collisions. This
471 needs to be in sync with the tremolo positioning code in
472 Stem_tremolo::print */
474 minlen += beam_trans;
476 length = max (length, minlen + 1.0);
479 Real stem_end = dir ? hp[dir] + dir * length : 0;
481 /* TODO: change name to extend-stems to staff/center/'() */
482 bool no_extend = to_boolean (me->get_property ("no-stem-extend"));
483 if (!no_extend && dir * stem_end < 0)
489 /* The log of the duration (Number of hooks on the flag minus two) */
491 Stem::duration_log (Grob *me)
493 SCM s = me->get_property ("duration-log");
494 return (scm_is_number (s)) ? scm_to_int (s) : 2;
497 MAKE_SCHEME_CALLBACK (Stem, calc_positioning_done, 1);
499 Stem::calc_positioning_done (SCM smob)
501 Grob *me = unsmob_grob (smob);
502 if (!head_count (me))
505 me->set_property ("positioning-done", SCM_BOOL_T);
507 extract_grob_set (me, "note-heads", ro_heads);
508 vector<Grob *> heads (ro_heads);
509 vector_sort (heads, position_less);
510 Direction dir = get_grob_direction (me);
515 Real thick = thickness (me);
517 Grob *hed = support_head (me);
520 programming_error ("Stem dir must be up or down.");
522 set_grob_direction (me, dir);
525 bool is_harmonic_centered = false;
526 for (vsize i = 0; i < heads.size (); i++)
527 is_harmonic_centered = is_harmonic_centered
528 || heads[i]->get_property ("style") == ly_symbol2scm ("harmonic");
529 is_harmonic_centered = is_harmonic_centered && is_invisible (me);
531 Real w = hed->extent (hed, X_AXIS)[dir];
532 for (vsize i = 0; i < heads.size (); i++)
534 Real amount = w - heads[i]->extent (heads[i], X_AXIS)[dir];
536 if (is_harmonic_centered)
538 = hed->extent (hed, X_AXIS).linear_combination (CENTER)
539 - heads[i]->extent (heads[i], X_AXIS).linear_combination (CENTER);
541 heads[i]->translate_axis (amount, X_AXIS);
544 Real lastpos = Real (Staff_symbol_referencer::get_position (heads[0]));
545 for (vsize i = 1; i < heads.size (); i++)
547 Real p = Staff_symbol_referencer::get_position (heads[i]);
548 Real dy = fabs (lastpos - p);
551 dy should always be 0.5, 0.0, 1.0, but provide safety margin
558 Real ell = heads[i]->extent (heads[i], X_AXIS).length ();
560 Direction d = get_grob_direction (me);
562 Reversed head should be shifted ell-thickness, but this
563 looks too crowded, so we only shift ell-0.5*thickness.
565 This leads to assymetry: Normal heads overlap the
566 stem 100% whereas reversed heads only overlaps the
570 Real reverse_overlap = 0.5;
571 heads[i]->translate_axis ((ell - thick * reverse_overlap) * d,
574 if (is_invisible (me))
575 heads[i]->translate_axis (-thick * (2 - reverse_overlap) * d,
580 For some cases we should kern some more: when the
581 distance between the next or prev note is too large, we'd
582 get large white gaps, eg.
603 MAKE_SCHEME_CALLBACK (Stem, calc_direction, 1);
605 Stem::calc_direction (SCM smob)
607 Grob *me = unsmob_grob (smob);
608 Direction dir = CENTER;
609 if (Grob *beam = unsmob_grob (me->get_object ("beam")))
611 SCM ignore_me = beam->get_property ("direction");
613 dir = get_grob_direction (me);
617 SCM dd = me->get_property ("default-direction");
620 return me->get_property ("neutral-direction");
623 return scm_from_int (dir);
626 MAKE_SCHEME_CALLBACK (Stem, calc_default_direction, 1);
628 Stem::calc_default_direction (SCM smob)
630 Grob *me = unsmob_grob (smob);
632 Direction dir = CENTER;
633 int staff_center = 0;
634 Interval hp = head_positions (me);
637 int udistance = (int) (UP * hp[UP] - staff_center);
638 int ddistance = (int) (DOWN * hp[DOWN] - staff_center);
640 dir = Direction (sign (ddistance - udistance));
643 return scm_from_int (dir);
646 // note - height property necessary to trigger quantized beam positions
647 // otherwise, we could just use Grob::stencil_height_proc
648 MAKE_SCHEME_CALLBACK (Stem, height, 1);
650 Stem::height (SCM smob)
652 Grob *me = unsmob_grob (smob);
653 return ly_interval2scm (internal_height (me, true));
657 Stem::get_reference_head (Grob *me)
659 return to_boolean (me->get_property ("avoid-note-head"))
665 Stem::beam_end_corrective (Grob *me)
667 Grob *beam = unsmob_grob (me->get_object ("beam"));
668 Direction dir = get_grob_direction (me);
673 programming_error ("no stem direction");
676 return dir * Beam::get_beam_thickness (beam) * 0.5;
682 Stem::internal_height (Grob *me, bool calc_beam)
684 Grob *beam = get_beam (me);
685 if (!is_valid_stem (me) && ! beam)
688 Direction dir = get_grob_direction (me);
690 if (beam && calc_beam)
692 /* trigger set-stem-lengths. */
693 (void) beam->get_property ("quantized-positions");
696 Real y1 = robust_scm2double ((calc_beam
697 ? me->get_property ("stem-begin-position")
698 : me->get_pure_property ("stem-begin-position", 0, INT_MAX)),
701 Real y2 = dir * robust_scm2double ((calc_beam
702 ? me->get_property ("length")
703 : me->get_pure_property ("length", 0, INT_MAX)),
707 Real half_space = Staff_symbol_referencer::staff_space (me) * 0.5;
709 Interval stem_y = Interval (min (y1, y2), max (y2, y1)) * half_space;
714 MAKE_SCHEME_CALLBACK (Stem, width, 1);
718 Grob *me = unsmob_grob (e);
722 if (is_invisible (me))
726 r = Interval (-1, 1);
727 r *= thickness (me) / 2;
730 return ly_interval2scm (r);
734 Stem::thickness (Grob *me)
736 return scm_to_double (me->get_property ("thickness"))
737 * Staff_symbol_referencer::line_thickness (me);
740 MAKE_SCHEME_CALLBACK (Stem, calc_stem_begin_position, 1);
742 Stem::calc_stem_begin_position (SCM smob)
744 Grob *me = unsmob_grob (smob);
745 return scm_from_double (internal_calc_stem_begin_position (me, true));
748 MAKE_SCHEME_CALLBACK (Stem, pure_calc_stem_begin_position, 3);
750 Stem::pure_calc_stem_begin_position (SCM smob,
754 Grob *me = unsmob_grob (smob);
755 return scm_from_double (internal_calc_stem_begin_position (me, false));
759 Stem::internal_calc_stem_begin_position (Grob *me, bool calc_beam)
761 Grob *beam = get_beam (me);
762 Real ss = Staff_symbol_referencer::staff_space (me);
763 if (beam && calc_beam)
765 (void) beam->get_property ("quantized-positions");
766 return robust_scm2double (me->get_property ("stem-begin-position"), 0.0);
769 Direction d = get_grob_direction (me);
770 Grob *lh = get_reference_head (me);
775 Real pos = Staff_symbol_referencer::get_position (lh);
777 if (Grob *head = support_head (me))
779 Interval head_height = head->extent (head, Y_AXIS);
780 Real y_attach = Note_head::stem_attachment_coordinate (head, Y_AXIS);
782 y_attach = head_height.linear_combination (y_attach);
783 pos += d * y_attach * 2 / ss;
790 Stem::is_valid_stem (Grob *me)
792 /* TODO: make the stem start a direction ?
793 This is required to avoid stems passing in tablature chords. */
794 Grob *lh = get_reference_head (me);
795 Grob *beam = unsmob_grob (me->get_object ("beam"));
800 if (lh && robust_scm2int (lh->get_property ("duration-log"), 0) < 1)
803 if (is_invisible (me))
809 MAKE_SCHEME_CALLBACK (Stem, print, 1);
811 Stem::print (SCM smob)
813 Grob *me = unsmob_grob (smob);
814 if (!is_valid_stem (me))
817 Direction dir = get_grob_direction (me);
818 Real y1 = robust_scm2double (me->get_property ("stem-begin-position"), 0.0);
819 Real y2 = dir * robust_scm2double (me->get_property ("length"), 0.0) + y1;
821 Real half_space = Staff_symbol_referencer::staff_space (me) * 0.5;
823 Interval stem_y = Interval (min (y1, y2), max (y2, y1)) * half_space;
825 stem_y[dir] -= beam_end_corrective (me);
828 Real stem_width = thickness (me);
830 = me->layout ()->get_dimension (ly_symbol2scm ("blot-diameter"));
832 Box b = Box (Interval (-stem_width / 2, stem_width / 2),
836 Stencil ss = Lookup::round_filled_box (b, blot);
837 mol.add_stencil (ss);
839 return mol.smobbed_copy ();
843 move the stem to right of the notehead if it is up.
845 MAKE_SCHEME_CALLBACK (Stem, offset_callback, 1);
847 Stem::offset_callback (SCM smob)
849 Grob *me = unsmob_grob (smob);
851 extract_grob_set (me, "rests", rests);
854 Grob *rest = rests.back ();
855 Real r = rest->extent (rest, X_AXIS).center ();
856 return scm_from_double (r);
859 if (Grob *f = first_head (me))
861 Interval head_wid = f->extent (f, X_AXIS);
864 if (is_invisible (me))
867 attach = Note_head::stem_attachment_coordinate (f, X_AXIS);
869 Direction d = get_grob_direction (me);
870 Real real_attach = head_wid.linear_combination (d * attach);
871 Real r = real_attach;
873 /* If not centered: correct for stem thickness. */
874 string style = robust_symbol2string (f->get_property ("style"), "default");
875 if (attach && style != "mensural"
876 && style != "neomensural"
877 && style != "petrucci")
879 Real rule_thick = thickness (me);
880 r += -d * rule_thick * 0.5;
882 return scm_from_double (r);
885 programming_error ("Weird stem.");
886 return scm_from_double (0.0);
890 Stem::get_beam (Grob *me)
892 SCM b = me->get_object ("beam");
893 return dynamic_cast<Spanner *> (unsmob_grob (b));
897 Stem::get_stem_info (Grob *me)
900 si.dir_ = get_grob_direction (me);
902 SCM scm_info = me->get_property ("stem-info");
903 si.ideal_y_ = scm_to_double (scm_car (scm_info));
904 si.shortest_y_ = scm_to_double (scm_cadr (scm_info));
908 MAKE_SCHEME_CALLBACK (Stem, calc_stem_info, 1);
910 Stem::calc_stem_info (SCM smob)
912 Grob *me = unsmob_grob (smob);
913 Direction my_dir = get_grob_direction (me);
917 programming_error ("no stem dir set");
921 Real staff_space = Staff_symbol_referencer::staff_space (me);
922 Grob *beam = get_beam (me);
926 (void) beam->get_property ("beaming");
929 Real beam_translation = Beam::get_beam_translation (beam);
930 Real beam_thickness = Beam::get_beam_thickness (beam);
931 int beam_count = Beam::get_direction_beam_count (beam, my_dir);
933 = robust_scm2double (me->get_property ("length-fraction"), 1.0);
935 /* Simple standard stem length */
936 SCM details = me->get_property ("details");
937 SCM lengths = ly_assoc_get (ly_symbol2scm ("beamed-lengths"), details, SCM_EOL);
940 = (scm_is_pair (lengths)
941 ? (scm_to_double (robust_list_ref (beam_count - 1, lengths))
945 stem only extends to center of beam
947 - 0.5 * beam_thickness)
950 /* Condition: sane minimum free stem length (chord to beams) */
951 lengths = ly_assoc_get (ly_symbol2scm ("beamed-minimum-free-lengths"),
954 Real ideal_minimum_free
955 = (scm_is_pair (lengths)
956 ? (scm_to_double (robust_list_ref (beam_count - 1, lengths))
961 Real height_of_my_trem = 0.0;
962 Grob *trem = unsmob_grob (me->get_object ("tremolo-flag"));
966 = Stem_tremolo::vertical_length (trem)
967 /* hack a bit of space around the trem. */
972 It seems that also for ideal minimum length, we must use
973 the maximum beam count (for this direction):
975 \score { \relative c'' { a8[ a32] } }
977 must be horizontal. */
978 Real height_of_my_beams = beam_thickness
979 + (beam_count - 1) * beam_translation;
981 Real ideal_minimum_length = ideal_minimum_free
984 /* stem only extends to center of beam */
985 - 0.5 * beam_thickness;
987 ideal_length = max (ideal_length, ideal_minimum_length);
989 /* Convert to Y position, calculate for dir == UP */
991 = /* staff positions */
992 head_positions (me)[my_dir] * 0.5
993 * my_dir * staff_space;
994 Real ideal_y = note_start + ideal_length;
996 /* Conditions for Y position */
998 /* Lowest beam of (UP) beam must never be lower than second staffline
1002 Although this (additional) rule is probably correct,
1003 I expect that highest beam (UP) should also never be lower
1004 than middle staffline, just as normal stems.
1008 Obviously not for grace beams.
1010 Also, not for knees. Seems to be a good thing. */
1011 bool no_extend = to_boolean (me->get_property ("no-stem-extend"));
1012 bool is_knee = Beam::is_knee (beam);
1013 if (!no_extend && !is_knee)
1015 /* Highest beam of (UP) beam must never be lower than middle
1017 ideal_y = max (ideal_y, 0.0);
1018 /* Lowest beam of (UP) beam must never be lower than second staffline */
1019 ideal_y = max (ideal_y, (-staff_space
1020 - beam_thickness + height_of_my_beams));
1023 ideal_y -= robust_scm2double (beam->get_property ("shorten"), 0);
1025 SCM bemfl = ly_assoc_get (ly_symbol2scm ("beamed-extreme-minimum-free-lengths"),
1029 = (scm_is_pair (bemfl)
1030 ? (scm_to_double (robust_list_ref (beam_count - 1, bemfl))
1035 Real minimum_length = max (minimum_free, height_of_my_trem)
1036 + height_of_my_beams
1037 /* stem only extends to center of beam */
1038 - 0.5 * beam_thickness;
1041 Real minimum_y = note_start + minimum_length;
1042 Real shortest_y = minimum_y * my_dir;
1044 return scm_list_2 (scm_from_double (ideal_y),
1045 scm_from_double (shortest_y));
1049 Stem::beam_multiplicity (Grob *stem)
1051 SCM beaming = stem->get_property ("beaming");
1052 Slice le = int_list_to_slice (scm_car (beaming));
1053 Slice ri = int_list_to_slice (scm_cdr (beaming));
1059 Stem::is_cross_staff (Grob *stem)
1061 Grob *beam = unsmob_grob (stem->get_object ("beam"));
1062 return beam && Beam::is_cross_staff (beam);
1065 MAKE_SCHEME_CALLBACK (Stem, calc_cross_staff, 1)
1067 Stem::calc_cross_staff (SCM smob)
1069 return scm_from_bool (is_cross_staff (unsmob_grob (smob)));
1073 Stem::flag (Grob *me)
1075 return unsmob_grob (me->get_object ("flag"));
1078 /* FIXME: Too many properties */
1079 ADD_INTERFACE (Stem,
1080 "The stem represents the graphical stem. In addition, it"
1081 " internally connects note heads, beams, and tremolos. Rests"
1082 " and whole notes have invisible stems.\n"
1084 "The following properties may be set in the @code{details}"
1088 "@item beamed-lengths\n"
1089 "List of stem lengths given beam multiplicity.\n"
1090 "@item beamed-minimum-free-lengths\n"
1091 "List of normal minimum free stem lengths (chord to beams)"
1092 " given beam multiplicity.\n"
1093 "@item beamed-extreme-minimum-free-lengths\n"
1094 "List of extreme minimum free stem lengths (chord to beams)"
1095 " given beam multiplicity.\n"
1097 "Default stem lengths. The list gives a length for each"
1099 "@item stem-shorten\n"
1100 "How much a stem in a forced direction should be shortened."
1101 " The list gives an amount depending on the number of flags"
1109 "beamlet-default-length "
1110 "beamlet-max-length-proportion "
1111 "default-direction "
1121 "neutral-direction "
1126 "stem-begin-position "
1133 /****************************************************************/
1135 Stem_info::Stem_info ()
1137 ideal_y_ = shortest_y_ = 0;
1142 Stem_info::scale (Real x)