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 if (!head_count (me))
283 extract_grob_set (me, "note-heads", heads);
284 SCM style = heads[0]->get_property ("style");
285 return style != ly_symbol2scm ("kievan") && scm_to_int (me->get_property ("duration-log")) >= 1;
288 MAKE_SCHEME_CALLBACK (Stem, pure_height, 3)
290 Stem::pure_height (SCM smob,
294 Grob *me = unsmob_grob (smob);
295 return ly_interval2scm (internal_pure_height (me, true));
299 Stem::internal_pure_height (Grob *me, bool calc_beam)
301 if (!is_normal_stem (me))
302 return Interval (0.0, 0.0);
304 Grob *beam = unsmob_grob (me->get_object ("beam"));
306 Interval iv = internal_height (me, false);
313 Direction dir = get_grob_direction (me);
316 overshoot[d] = d == dir ? dir * infinity_f : iv[d];
317 while (flip (&d) != DOWN);
319 vector<Interval> heights;
320 vector<Grob *> my_stems;
321 extract_grob_set (beam, "normal-stems", normal_stems);
322 for (vsize i = 0; i < normal_stems.size (); i++)
323 if (get_grob_direction (normal_stems[i]) == dir)
325 if (normal_stems[i] != me)
326 heights.push_back (Stem::internal_pure_height (normal_stems[i], false));
328 heights.push_back (iv);
329 my_stems.push_back (normal_stems[i]);
331 //iv.unite (heights.back ());
332 // look for cross staff effects
334 Grob *common = common_refpoint_of_array (my_stems, me, Y_AXIS);
335 Real min_pos = infinity_f;
336 Real max_pos = -infinity_f;
337 for (vsize i = 0; i < my_stems.size (); i++)
339 coords.push_back (my_stems[i]->pure_relative_y_coordinate (common, 0, INT_MAX));
340 min_pos = min (min_pos, coords[i]);
341 max_pos = max (max_pos, coords[i]);
343 for (vsize i = 0; i < heights.size (); i++)
345 heights[i][dir] += dir == DOWN
346 ? coords[i] - max_pos
347 : coords[i] - min_pos;
350 for (vsize i = 0; i < heights.size (); i++) iv.unite (heights[i]);
352 for (vsize i = 0; i < my_stems.size (); i++)
353 cache_pure_height (my_stems[i], iv, heights[i]);
354 iv.intersect (overshoot);
361 Stem::cache_pure_height (Grob *me, Interval iv, Interval my_iv)
364 Direction dir = get_grob_direction (me);
367 overshoot[d] = d == dir ? dir * infinity_f : my_iv[d];
368 while (flip (&d) != DOWN);
370 iv.intersect (overshoot);
371 dynamic_cast<Item *> (me)->cache_pure_height (iv);
374 MAKE_SCHEME_CALLBACK (Stem, calc_stem_end_position, 1)
376 Stem::calc_stem_end_position (SCM smob)
378 Grob *me = unsmob_grob (smob);
379 return scm_from_double (internal_calc_stem_end_position (me, true));
382 MAKE_SCHEME_CALLBACK (Stem, pure_calc_stem_end_position, 3)
384 Stem::pure_calc_stem_end_position (SCM smob,
388 Grob *me = unsmob_grob (smob);
389 return scm_from_double (internal_calc_stem_end_position (me, false));
393 Stem::internal_calc_stem_end_position (Grob *me, bool calc_beam)
395 if (!head_count (me))
398 Grob *beam = get_beam (me);
399 Real ss = Staff_symbol_referencer::staff_space (me);
400 Direction dir = get_grob_direction (me);
402 if (beam && calc_beam)
404 (void) beam->get_property ("quantized-positions");
405 return robust_scm2double (me->get_property ("length"), 0.0)
406 + dir * robust_scm2double (me->get_property ("stem-begin-position"), 0.0);
411 /* WARNING: IN HALF SPACES */
412 SCM details = me->get_property ("details");
413 int durlog = duration_log (me);
415 Real staff_rad = Staff_symbol_referencer::staff_radius (me);
417 SCM s = ly_assoc_get (ly_symbol2scm ("lengths"), details, SCM_EOL);
419 length = 2 * scm_to_double (robust_list_ref (durlog - 2, s));
421 /* Stems in unnatural (forced) direction should be shortened,
422 according to [Roush & Gourlay] */
423 Interval hp = head_positions (me);
424 if (dir && dir * hp[dir] >= 0)
426 SCM sshorten = ly_assoc_get (ly_symbol2scm ("stem-shorten"), details, SCM_EOL);
427 SCM scm_shorten = scm_is_pair (sshorten)
428 ? robust_list_ref (max (duration_log (me) - 2, 0), sshorten) : SCM_EOL;
429 Real shorten_property = 2 * robust_scm2double (scm_shorten, 0);
430 /* change in length between full-size and shortened stems is executed gradually.
431 "transition area" = stems between full-sized and fully-shortened.
433 Real quarter_stem_length = 2 * scm_to_double (robust_list_ref (0, s));
434 /* shortening_step = difference in length between consecutive stem lengths
435 in transition area. The bigger the difference between full-sized
436 and shortened stems, the bigger shortening_step is.
437 (but not greater than 1/2 and not smaller than 1/4).
438 value 6 is heuristic; it determines the suggested transition slope steepnesas.
440 Real shortening_step = min (max (0.25, (shorten_property / 6)), 0.5);
441 /* Shortening of unflagged stems should begin on the first stem that sticks
442 more than 1 staffspace (2 units) out of the staff.
443 Shortening of flagged stems begins in the same moment as unflagged ones,
444 but not earlier than on the middle line note.
446 Real which_step = (min (1.0, quarter_stem_length - (2 * staff_rad) - 2.0)) + abs (hp[dir]);
447 Real shorten = min (max (0.0, (shortening_step * which_step)), shorten_property);
452 length *= robust_scm2double (me->get_property ("length-fraction"), 1.0);
455 Grob *t_flag = unsmob_grob (me->get_object ("tremolo-flag"));
456 if (t_flag && (!unsmob_grob (me->get_object ("beam")) || !calc_beam))
458 /* Crude hack: add extra space if tremolo flag is there.
460 We can't do this for the beam, since we get into a loop
461 (Stem_tremolo::raw_stencil () looks at the beam.) --hwn */
464 + 2 * Stem_tremolo::vertical_length (t_flag) / ss;
466 /* We don't want to add the whole extent of the flag because the trem
467 and the flag can overlap partly. beam_translation gives a good
471 Real beam_trans = Stem_tremolo::get_beam_translation (t_flag);
472 /* the obvious choice is (durlog - 2) here, but we need a bit more space. */
473 minlen += 2 * (durlog - 1.5) * beam_trans;
475 /* up-stems need even a little more space to avoid collisions. This
476 needs to be in sync with the tremolo positioning code in
477 Stem_tremolo::print */
479 minlen += beam_trans;
481 length = max (length, minlen + 1.0);
484 Real stem_end = dir ? hp[dir] + dir * length : 0;
486 /* TODO: change name to extend-stems to staff/center/'() */
487 bool no_extend = to_boolean (me->get_property ("no-stem-extend"));
488 if (!no_extend && dir * stem_end < 0)
494 /* The log of the duration (Number of hooks on the flag minus two) */
496 Stem::duration_log (Grob *me)
498 SCM s = me->get_property ("duration-log");
499 return (scm_is_number (s)) ? scm_to_int (s) : 2;
502 MAKE_SCHEME_CALLBACK (Stem, calc_positioning_done, 1);
504 Stem::calc_positioning_done (SCM smob)
506 Grob *me = unsmob_grob (smob);
507 if (!head_count (me))
510 me->set_property ("positioning-done", SCM_BOOL_T);
512 extract_grob_set (me, "note-heads", ro_heads);
513 vector<Grob *> heads (ro_heads);
514 vector_sort (heads, position_less);
515 Direction dir = get_grob_direction (me);
520 Real thick = thickness (me);
522 Grob *hed = support_head (me);
525 programming_error ("Stem dir must be up or down.");
527 set_grob_direction (me, dir);
530 bool is_harmonic_centered = false;
531 for (vsize i = 0; i < heads.size (); i++)
532 is_harmonic_centered = is_harmonic_centered
533 || heads[i]->get_property ("style") == ly_symbol2scm ("harmonic");
534 is_harmonic_centered = is_harmonic_centered && is_invisible (me);
536 Real w = hed->extent (hed, X_AXIS)[dir];
537 for (vsize i = 0; i < heads.size (); i++)
539 Real amount = w - heads[i]->extent (heads[i], X_AXIS)[dir];
541 if (is_harmonic_centered)
543 = hed->extent (hed, X_AXIS).linear_combination (CENTER)
544 - heads[i]->extent (heads[i], X_AXIS).linear_combination (CENTER);
546 heads[i]->translate_axis (amount, X_AXIS);
549 Real lastpos = Real (Staff_symbol_referencer::get_position (heads[0]));
550 for (vsize i = 1; i < heads.size (); i++)
552 Real p = Staff_symbol_referencer::get_position (heads[i]);
553 Real dy = fabs (lastpos - p);
556 dy should always be 0.5, 0.0, 1.0, but provide safety margin
563 Real ell = heads[i]->extent (heads[i], X_AXIS).length ();
565 Direction d = get_grob_direction (me);
567 Reversed head should be shifted ell-thickness, but this
568 looks too crowded, so we only shift ell-0.5*thickness.
570 This leads to assymetry: Normal heads overlap the
571 stem 100% whereas reversed heads only overlaps the
575 Real reverse_overlap = 0.5;
576 heads[i]->translate_axis ((ell - thick * reverse_overlap) * d,
579 if (is_invisible (me))
580 heads[i]->translate_axis (-thick * (2 - reverse_overlap) * d,
585 For some cases we should kern some more: when the
586 distance between the next or prev note is too large, we'd
587 get large white gaps, eg.
608 MAKE_SCHEME_CALLBACK (Stem, calc_direction, 1);
610 Stem::calc_direction (SCM smob)
612 Grob *me = unsmob_grob (smob);
613 Direction dir = CENTER;
614 if (Grob *beam = unsmob_grob (me->get_object ("beam")))
616 SCM ignore_me = beam->get_property ("direction");
618 dir = get_grob_direction (me);
622 SCM dd = me->get_property ("default-direction");
625 return me->get_property ("neutral-direction");
628 return scm_from_int (dir);
631 MAKE_SCHEME_CALLBACK (Stem, calc_default_direction, 1);
633 Stem::calc_default_direction (SCM smob)
635 Grob *me = unsmob_grob (smob);
637 Direction dir = CENTER;
638 int staff_center = 0;
639 Interval hp = head_positions (me);
642 int udistance = (int) (UP * hp[UP] - staff_center);
643 int ddistance = (int) (DOWN * hp[DOWN] - staff_center);
645 dir = Direction (sign (ddistance - udistance));
648 return scm_from_int (dir);
651 // note - height property necessary to trigger quantized beam positions
652 // otherwise, we could just use Grob::stencil_height_proc
653 MAKE_SCHEME_CALLBACK (Stem, height, 1);
655 Stem::height (SCM smob)
657 Grob *me = unsmob_grob (smob);
658 return ly_interval2scm (internal_height (me, true));
662 Stem::get_reference_head (Grob *me)
664 return to_boolean (me->get_property ("avoid-note-head"))
670 Stem::beam_end_corrective (Grob *me)
672 Grob *beam = unsmob_grob (me->get_object ("beam"));
673 Direction dir = get_grob_direction (me);
678 programming_error ("no stem direction");
681 return dir * Beam::get_beam_thickness (beam) * 0.5;
687 Stem::internal_height (Grob *me, bool calc_beam)
689 Grob *beam = get_beam (me);
690 if (!is_valid_stem (me) && ! beam)
693 Direction dir = get_grob_direction (me);
695 if (beam && calc_beam)
697 /* trigger set-stem-lengths. */
698 (void) beam->get_property ("quantized-positions");
701 Real y1 = robust_scm2double ((calc_beam
702 ? me->get_property ("stem-begin-position")
703 : me->get_pure_property ("stem-begin-position", 0, INT_MAX)),
706 Real y2 = dir * robust_scm2double ((calc_beam
707 ? me->get_property ("length")
708 : me->get_pure_property ("length", 0, INT_MAX)),
712 Real half_space = Staff_symbol_referencer::staff_space (me) * 0.5;
714 Interval stem_y = Interval (min (y1, y2), max (y2, y1)) * half_space;
719 MAKE_SCHEME_CALLBACK (Stem, width, 1);
723 Grob *me = unsmob_grob (e);
727 if (is_invisible (me))
731 r = Interval (-1, 1);
732 r *= thickness (me) / 2;
735 return ly_interval2scm (r);
739 Stem::thickness (Grob *me)
741 return scm_to_double (me->get_property ("thickness"))
742 * Staff_symbol_referencer::line_thickness (me);
745 MAKE_SCHEME_CALLBACK (Stem, calc_stem_begin_position, 1);
747 Stem::calc_stem_begin_position (SCM smob)
749 Grob *me = unsmob_grob (smob);
750 return scm_from_double (internal_calc_stem_begin_position (me, true));
753 MAKE_SCHEME_CALLBACK (Stem, pure_calc_stem_begin_position, 3);
755 Stem::pure_calc_stem_begin_position (SCM smob,
759 Grob *me = unsmob_grob (smob);
760 return scm_from_double (internal_calc_stem_begin_position (me, false));
764 Stem::internal_calc_stem_begin_position (Grob *me, bool calc_beam)
766 Grob *beam = get_beam (me);
767 Real ss = Staff_symbol_referencer::staff_space (me);
768 if (beam && calc_beam)
770 (void) beam->get_property ("quantized-positions");
771 return robust_scm2double (me->get_property ("stem-begin-position"), 0.0);
774 Direction d = get_grob_direction (me);
775 Grob *lh = get_reference_head (me);
780 Real pos = Staff_symbol_referencer::get_position (lh);
782 if (Grob *head = support_head (me))
784 Interval head_height = head->extent (head, Y_AXIS);
785 Real y_attach = Note_head::stem_attachment_coordinate (head, Y_AXIS);
787 y_attach = head_height.linear_combination (y_attach);
788 pos += d * y_attach * 2 / ss;
795 Stem::is_valid_stem (Grob *me)
797 /* TODO: make the stem start a direction ?
798 This is required to avoid stems passing in tablature chords. */
799 Grob *lh = get_reference_head (me);
800 Grob *beam = unsmob_grob (me->get_object ("beam"));
805 if (lh && robust_scm2int (lh->get_property ("duration-log"), 0) < 1)
808 if (is_invisible (me))
814 MAKE_SCHEME_CALLBACK (Stem, print, 1);
816 Stem::print (SCM smob)
818 Grob *me = unsmob_grob (smob);
819 if (!is_valid_stem (me))
822 Direction dir = get_grob_direction (me);
823 Real y1 = robust_scm2double (me->get_property ("stem-begin-position"), 0.0);
824 Real y2 = dir * robust_scm2double (me->get_property ("length"), 0.0) + y1;
826 Real half_space = Staff_symbol_referencer::staff_space (me) * 0.5;
828 Interval stem_y = Interval (min (y1, y2), max (y2, y1)) * half_space;
830 stem_y[dir] -= beam_end_corrective (me);
833 Real stem_width = thickness (me);
835 = me->layout ()->get_dimension (ly_symbol2scm ("blot-diameter"));
837 Box b = Box (Interval (-stem_width / 2, stem_width / 2),
841 Stencil ss = Lookup::round_filled_box (b, blot);
842 mol.add_stencil (ss);
844 return mol.smobbed_copy ();
848 move the stem to right of the notehead if it is up.
850 MAKE_SCHEME_CALLBACK (Stem, offset_callback, 1);
852 Stem::offset_callback (SCM smob)
854 Grob *me = unsmob_grob (smob);
856 extract_grob_set (me, "rests", rests);
859 Grob *rest = rests.back ();
860 Real r = rest->extent (rest, X_AXIS).center ();
861 return scm_from_double (r);
864 if (Grob *f = first_head (me))
866 Interval head_wid = f->extent (f, X_AXIS);
869 if (is_invisible (me))
872 attach = Note_head::stem_attachment_coordinate (f, X_AXIS);
874 Direction d = get_grob_direction (me);
875 Real real_attach = head_wid.linear_combination (d * attach);
876 Real r = real_attach;
878 /* If not centered: correct for stem thickness. */
879 string style = robust_symbol2string (f->get_property ("style"), "default");
880 if (attach && style != "mensural"
881 && style != "neomensural"
882 && style != "petrucci")
884 Real rule_thick = thickness (me);
885 r += -d * rule_thick * 0.5;
887 return scm_from_double (r);
890 programming_error ("Weird stem.");
891 return scm_from_double (0.0);
895 Stem::get_beam (Grob *me)
897 SCM b = me->get_object ("beam");
898 return dynamic_cast<Spanner *> (unsmob_grob (b));
902 Stem::get_stem_info (Grob *me)
905 si.dir_ = get_grob_direction (me);
907 SCM scm_info = me->get_property ("stem-info");
908 si.ideal_y_ = scm_to_double (scm_car (scm_info));
909 si.shortest_y_ = scm_to_double (scm_cadr (scm_info));
913 MAKE_SCHEME_CALLBACK (Stem, calc_stem_info, 1);
915 Stem::calc_stem_info (SCM smob)
917 Grob *me = unsmob_grob (smob);
918 Direction my_dir = get_grob_direction (me);
922 programming_error ("no stem dir set");
926 Real staff_space = Staff_symbol_referencer::staff_space (me);
927 Grob *beam = get_beam (me);
931 (void) beam->get_property ("beaming");
934 Real beam_translation = Beam::get_beam_translation (beam);
935 Real beam_thickness = Beam::get_beam_thickness (beam);
936 int beam_count = Beam::get_direction_beam_count (beam, my_dir);
938 = robust_scm2double (me->get_property ("length-fraction"), 1.0);
940 /* Simple standard stem length */
941 SCM details = me->get_property ("details");
942 SCM lengths = ly_assoc_get (ly_symbol2scm ("beamed-lengths"), details, SCM_EOL);
945 = (scm_is_pair (lengths)
946 ? (scm_to_double (robust_list_ref (beam_count - 1, lengths))
950 stem only extends to center of beam
952 - 0.5 * beam_thickness)
955 /* Condition: sane minimum free stem length (chord to beams) */
956 lengths = ly_assoc_get (ly_symbol2scm ("beamed-minimum-free-lengths"),
959 Real ideal_minimum_free
960 = (scm_is_pair (lengths)
961 ? (scm_to_double (robust_list_ref (beam_count - 1, lengths))
966 Real height_of_my_trem = 0.0;
967 Grob *trem = unsmob_grob (me->get_object ("tremolo-flag"));
971 = Stem_tremolo::vertical_length (trem)
972 /* hack a bit of space around the trem. */
977 It seems that also for ideal minimum length, we must use
978 the maximum beam count (for this direction):
980 \score { \relative c'' { a8[ a32] } }
982 must be horizontal. */
983 Real height_of_my_beams = beam_thickness
984 + (beam_count - 1) * beam_translation;
986 Real ideal_minimum_length = ideal_minimum_free
989 /* stem only extends to center of beam */
990 - 0.5 * beam_thickness;
992 ideal_length = max (ideal_length, ideal_minimum_length);
994 /* Convert to Y position, calculate for dir == UP */
996 = /* staff positions */
997 head_positions (me)[my_dir] * 0.5
998 * my_dir * staff_space;
999 Real ideal_y = note_start + ideal_length;
1001 /* Conditions for Y position */
1003 /* Lowest beam of (UP) beam must never be lower than second staffline
1007 Although this (additional) rule is probably correct,
1008 I expect that highest beam (UP) should also never be lower
1009 than middle staffline, just as normal stems.
1013 Obviously not for grace beams.
1015 Also, not for knees. Seems to be a good thing. */
1016 bool no_extend = to_boolean (me->get_property ("no-stem-extend"));
1017 bool is_knee = Beam::is_knee (beam);
1018 if (!no_extend && !is_knee)
1020 /* Highest beam of (UP) beam must never be lower than middle
1022 ideal_y = max (ideal_y, 0.0);
1023 /* Lowest beam of (UP) beam must never be lower than second staffline */
1024 ideal_y = max (ideal_y, (-staff_space
1025 - beam_thickness + height_of_my_beams));
1028 ideal_y -= robust_scm2double (beam->get_property ("shorten"), 0);
1030 SCM bemfl = ly_assoc_get (ly_symbol2scm ("beamed-extreme-minimum-free-lengths"),
1034 = (scm_is_pair (bemfl)
1035 ? (scm_to_double (robust_list_ref (beam_count - 1, bemfl))
1040 Real minimum_length = max (minimum_free, height_of_my_trem)
1041 + height_of_my_beams
1042 /* stem only extends to center of beam */
1043 - 0.5 * beam_thickness;
1046 Real minimum_y = note_start + minimum_length;
1047 Real shortest_y = minimum_y * my_dir;
1049 return scm_list_2 (scm_from_double (ideal_y),
1050 scm_from_double (shortest_y));
1054 Stem::beam_multiplicity (Grob *stem)
1056 SCM beaming = stem->get_property ("beaming");
1057 Slice le = int_list_to_slice (scm_car (beaming));
1058 Slice ri = int_list_to_slice (scm_cdr (beaming));
1064 Stem::is_cross_staff (Grob *stem)
1066 Grob *beam = unsmob_grob (stem->get_object ("beam"));
1067 return beam && Beam::is_cross_staff (beam);
1070 MAKE_SCHEME_CALLBACK (Stem, calc_cross_staff, 1)
1072 Stem::calc_cross_staff (SCM smob)
1074 return scm_from_bool (is_cross_staff (unsmob_grob (smob)));
1078 Stem::flag (Grob *me)
1080 return unsmob_grob (me->get_object ("flag"));
1083 /* FIXME: Too many properties */
1084 ADD_INTERFACE (Stem,
1085 "The stem represents the graphical stem. In addition, it"
1086 " internally connects note heads, beams, and tremolos. Rests"
1087 " and whole notes have invisible stems.\n"
1089 "The following properties may be set in the @code{details}"
1093 "@item beamed-lengths\n"
1094 "List of stem lengths given beam multiplicity.\n"
1095 "@item beamed-minimum-free-lengths\n"
1096 "List of normal minimum free stem lengths (chord to beams)"
1097 " given beam multiplicity.\n"
1098 "@item beamed-extreme-minimum-free-lengths\n"
1099 "List of extreme minimum free stem lengths (chord to beams)"
1100 " given beam multiplicity.\n"
1102 "Default stem lengths. The list gives a length for each"
1104 "@item stem-shorten\n"
1105 "How much a stem in a forced direction should be shortened."
1106 " The list gives an amount depending on the number of flags"
1114 "beamlet-default-length "
1115 "beamlet-max-length-proportion "
1116 "default-direction "
1126 "neutral-direction "
1131 "stem-begin-position "
1139 /****************************************************************/
1141 Stem_info::Stem_info ()
1143 ideal_y_ = shortest_y_ = 0;
1148 Stem_info::scale (Real x)