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 staff positions, in ascending order.
240 * If FILTER, include the main column of noteheads only */
242 Stem::note_head_positions (Grob *me, bool filter)
245 extract_grob_set (me, "note-heads", heads);
246 Grob *xref = common_refpoint_of_array (heads, me, X_AXIS);
248 for (vsize i = heads.size (); i--;)
252 && n->relative_coordinate (xref, X_AXIS) != 0.0)
255 int p = Staff_symbol_referencer::get_rounded_position (n);
259 vector_sort (ps, less<int> ());
264 Stem::add_head (Grob *me, Grob *n)
266 n->set_object ("stem", me->self_scm ());
268 if (Note_head::has_interface (n))
269 Pointer_group_interface::add_grob (me, ly_symbol2scm ("note-heads"), n);
270 else if (Rest::has_interface (n))
271 Pointer_group_interface::add_grob (me, ly_symbol2scm ("rests"), n);
275 Stem::is_invisible (Grob *me)
277 return !is_normal_stem (me)
278 && (robust_scm2double (me->get_property ("stemlet-length"),
283 Stem::is_normal_stem (Grob *me)
285 if (!head_count (me))
288 extract_grob_set (me, "note-heads", heads);
289 SCM style = heads[0]->get_property ("style");
290 return style != ly_symbol2scm ("kievan") && scm_to_int (me->get_property ("duration-log")) >= 1;
293 MAKE_SCHEME_CALLBACK (Stem, pure_height, 3)
295 Stem::pure_height (SCM smob,
299 Grob *me = unsmob_grob (smob);
300 return ly_interval2scm (internal_pure_height (me, true));
304 Stem::internal_pure_height (Grob *me, bool calc_beam)
306 if (!is_normal_stem (me))
307 return Interval (0.0, 0.0);
309 Grob *beam = unsmob_grob (me->get_object ("beam"));
311 Interval iv = internal_height (me, false);
318 Direction dir = get_grob_direction (me);
321 overshoot[d] = d == dir ? dir * infinity_f : iv[d];
322 while (flip (&d) != DOWN);
324 vector<Interval> heights;
325 vector<Grob *> my_stems;
326 extract_grob_set (beam, "normal-stems", normal_stems);
327 for (vsize i = 0; i < normal_stems.size (); i++)
328 if (get_grob_direction (normal_stems[i]) == dir)
330 if (normal_stems[i] != me)
331 heights.push_back (Stem::internal_pure_height (normal_stems[i], false));
333 heights.push_back (iv);
334 my_stems.push_back (normal_stems[i]);
336 //iv.unite (heights.back ());
337 // look for cross staff effects
339 Grob *common = common_refpoint_of_array (my_stems, me, Y_AXIS);
340 Real min_pos = infinity_f;
341 Real max_pos = -infinity_f;
342 for (vsize i = 0; i < my_stems.size (); i++)
344 coords.push_back (my_stems[i]->pure_relative_y_coordinate (common, 0, INT_MAX));
345 min_pos = min (min_pos, coords[i]);
346 max_pos = max (max_pos, coords[i]);
348 for (vsize i = 0; i < heights.size (); i++)
350 heights[i][dir] += dir == DOWN
351 ? coords[i] - max_pos
352 : coords[i] - min_pos;
355 for (vsize i = 0; i < heights.size (); i++) iv.unite (heights[i]);
357 for (vsize i = 0; i < my_stems.size (); i++)
358 cache_pure_height (my_stems[i], iv, heights[i]);
359 iv.intersect (overshoot);
366 Stem::cache_pure_height (Grob *me, Interval iv, Interval my_iv)
369 Direction dir = get_grob_direction (me);
372 overshoot[d] = d == dir ? dir * infinity_f : my_iv[d];
373 while (flip (&d) != DOWN);
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 heads[i]->translate_axis (amount, X_AXIS);
554 Real lastpos = Real (Staff_symbol_referencer::get_position (heads[0]));
555 for (vsize i = 1; i < heads.size (); i++)
557 Real p = Staff_symbol_referencer::get_position (heads[i]);
558 Real dy = fabs (lastpos - p);
561 dy should always be 0.5, 0.0, 1.0, but provide safety margin
568 Real ell = heads[i]->extent (heads[i], X_AXIS).length ();
570 Direction d = get_grob_direction (me);
572 Reversed head should be shifted ell-thickness, but this
573 looks too crowded, so we only shift ell-0.5*thickness.
575 This leads to assymetry: Normal heads overlap the
576 stem 100% whereas reversed heads only overlaps the
580 Real reverse_overlap = 0.5;
581 heads[i]->translate_axis ((ell - thick * reverse_overlap) * d,
584 if (is_invisible (me))
585 heads[i]->translate_axis (-thick * (2 - reverse_overlap) * d,
590 For some cases we should kern some more: when the
591 distance between the next or prev note is too large, we'd
592 get large white gaps, eg.
613 MAKE_SCHEME_CALLBACK (Stem, calc_direction, 1);
615 Stem::calc_direction (SCM smob)
617 Grob *me = unsmob_grob (smob);
618 Direction dir = CENTER;
619 if (Grob *beam = unsmob_grob (me->get_object ("beam")))
621 SCM ignore_me = beam->get_property ("direction");
623 dir = get_grob_direction (me);
627 SCM dd = me->get_property ("default-direction");
630 return me->get_property ("neutral-direction");
633 return scm_from_int (dir);
636 MAKE_SCHEME_CALLBACK (Stem, calc_default_direction, 1);
638 Stem::calc_default_direction (SCM smob)
640 Grob *me = unsmob_grob (smob);
642 Direction dir = CENTER;
643 int staff_center = 0;
644 Interval hp = head_positions (me);
647 int udistance = (int) (UP * hp[UP] - staff_center);
648 int ddistance = (int) (DOWN * hp[DOWN] - staff_center);
650 dir = Direction (sign (ddistance - udistance));
653 return scm_from_int (dir);
656 // note - height property necessary to trigger quantized beam positions
657 // otherwise, we could just use Grob::stencil_height_proc
658 MAKE_SCHEME_CALLBACK (Stem, height, 1);
660 Stem::height (SCM smob)
662 Grob *me = unsmob_grob (smob);
663 return ly_interval2scm (internal_height (me, true));
667 Stem::get_reference_head (Grob *me)
669 return to_boolean (me->get_property ("avoid-note-head"))
675 Stem::beam_end_corrective (Grob *me)
677 Grob *beam = unsmob_grob (me->get_object ("beam"));
678 Direction dir = get_grob_direction (me);
683 programming_error ("no stem direction");
686 return dir * Beam::get_beam_thickness (beam) * 0.5;
692 Stem::internal_height (Grob *me, bool calc_beam)
694 Grob *beam = get_beam (me);
695 if (!is_valid_stem (me) && ! beam)
698 Direction dir = get_grob_direction (me);
700 if (beam && calc_beam)
702 /* trigger set-stem-lengths. */
703 (void) beam->get_property ("quantized-positions");
706 Real y1 = robust_scm2double ((calc_beam
707 ? me->get_property ("stem-begin-position")
708 : me->get_pure_property ("stem-begin-position", 0, INT_MAX)),
711 Real y2 = dir * robust_scm2double ((calc_beam
712 ? me->get_property ("length")
713 : me->get_pure_property ("length", 0, INT_MAX)),
717 Real half_space = Staff_symbol_referencer::staff_space (me) * 0.5;
719 Interval stem_y = Interval (min (y1, y2), max (y2, y1)) * half_space;
724 MAKE_SCHEME_CALLBACK (Stem, width, 1);
728 Grob *me = unsmob_grob (e);
732 if (is_invisible (me))
736 r = Interval (-1, 1);
737 r *= thickness (me) / 2;
740 return ly_interval2scm (r);
744 Stem::thickness (Grob *me)
746 return scm_to_double (me->get_property ("thickness"))
747 * Staff_symbol_referencer::line_thickness (me);
750 MAKE_SCHEME_CALLBACK (Stem, calc_stem_begin_position, 1);
752 Stem::calc_stem_begin_position (SCM smob)
754 Grob *me = unsmob_grob (smob);
755 return scm_from_double (internal_calc_stem_begin_position (me, true));
758 MAKE_SCHEME_CALLBACK (Stem, pure_calc_stem_begin_position, 3);
760 Stem::pure_calc_stem_begin_position (SCM smob,
764 Grob *me = unsmob_grob (smob);
765 return scm_from_double (internal_calc_stem_begin_position (me, false));
769 Stem::internal_calc_stem_begin_position (Grob *me, bool calc_beam)
771 Grob *beam = get_beam (me);
772 Real ss = Staff_symbol_referencer::staff_space (me);
773 if (beam && calc_beam)
775 (void) beam->get_property ("quantized-positions");
776 return robust_scm2double (me->get_property ("stem-begin-position"), 0.0);
779 Direction d = get_grob_direction (me);
780 Grob *lh = get_reference_head (me);
785 Real pos = Staff_symbol_referencer::get_position (lh);
787 if (Grob *head = support_head (me))
789 Interval head_height = head->extent (head, Y_AXIS);
790 Real y_attach = Note_head::stem_attachment_coordinate (head, Y_AXIS);
792 y_attach = head_height.linear_combination (y_attach);
793 pos += d * y_attach * 2 / ss;
800 Stem::is_valid_stem (Grob *me)
802 /* TODO: make the stem start a direction ?
803 This is required to avoid stems passing in tablature chords. */
804 Grob *lh = get_reference_head (me);
805 Grob *beam = unsmob_grob (me->get_object ("beam"));
810 if (lh && robust_scm2int (lh->get_property ("duration-log"), 0) < 1)
813 if (is_invisible (me))
819 MAKE_SCHEME_CALLBACK (Stem, print, 1);
821 Stem::print (SCM smob)
823 Grob *me = unsmob_grob (smob);
824 if (!is_valid_stem (me))
827 Direction dir = get_grob_direction (me);
828 Real y1 = robust_scm2double (me->get_property ("stem-begin-position"), 0.0);
829 Real y2 = dir * robust_scm2double (me->get_property ("length"), 0.0) + y1;
831 Real half_space = Staff_symbol_referencer::staff_space (me) * 0.5;
833 Interval stem_y = Interval (min (y1, y2), max (y2, y1)) * half_space;
835 stem_y[dir] -= beam_end_corrective (me);
838 Real stem_width = thickness (me);
840 = me->layout ()->get_dimension (ly_symbol2scm ("blot-diameter"));
842 Box b = Box (Interval (-stem_width / 2, stem_width / 2),
846 Stencil ss = Lookup::round_filled_box (b, blot);
847 mol.add_stencil (ss);
849 return mol.smobbed_copy ();
853 move the stem to right of the notehead if it is up.
855 MAKE_SCHEME_CALLBACK (Stem, offset_callback, 1);
857 Stem::offset_callback (SCM smob)
859 Grob *me = unsmob_grob (smob);
861 extract_grob_set (me, "rests", rests);
864 Grob *rest = rests.back ();
865 Real r = rest->extent (rest, X_AXIS).center ();
866 return scm_from_double (r);
869 if (Grob *f = first_head (me))
871 Interval head_wid = f->extent (f, X_AXIS);
874 if (is_invisible (me))
877 attach = Note_head::stem_attachment_coordinate (f, X_AXIS);
879 Direction d = get_grob_direction (me);
880 Real real_attach = head_wid.linear_combination (d * attach);
881 Real r = real_attach;
883 /* If not centered: correct for stem thickness. */
884 string style = robust_symbol2string (f->get_property ("style"), "default");
885 if (attach && style != "mensural"
886 && style != "neomensural"
887 && style != "petrucci")
889 Real rule_thick = thickness (me);
890 r += -d * rule_thick * 0.5;
892 return scm_from_double (r);
895 programming_error ("Weird stem.");
896 return scm_from_double (0.0);
900 Stem::get_beam (Grob *me)
902 SCM b = me->get_object ("beam");
903 return dynamic_cast<Spanner *> (unsmob_grob (b));
907 Stem::get_stem_info (Grob *me)
910 si.dir_ = get_grob_direction (me);
912 SCM scm_info = me->get_property ("stem-info");
913 si.ideal_y_ = scm_to_double (scm_car (scm_info));
914 si.shortest_y_ = scm_to_double (scm_cadr (scm_info));
918 MAKE_SCHEME_CALLBACK (Stem, calc_stem_info, 1);
920 Stem::calc_stem_info (SCM smob)
922 Grob *me = unsmob_grob (smob);
923 Direction my_dir = get_grob_direction (me);
927 programming_error ("no stem dir set");
931 Real staff_space = Staff_symbol_referencer::staff_space (me);
932 Grob *beam = get_beam (me);
936 (void) beam->get_property ("beaming");
939 Real beam_translation = Beam::get_beam_translation (beam);
940 Real beam_thickness = Beam::get_beam_thickness (beam);
941 int beam_count = Beam::get_direction_beam_count (beam, my_dir);
943 = robust_scm2double (me->get_property ("length-fraction"), 1.0);
945 /* Simple standard stem length */
946 SCM details = me->get_property ("details");
947 SCM lengths = ly_assoc_get (ly_symbol2scm ("beamed-lengths"), details, SCM_EOL);
950 = (scm_is_pair (lengths)
951 ? (scm_to_double (robust_list_ref (beam_count - 1, lengths))
955 stem only extends to center of beam
957 - 0.5 * beam_thickness)
960 /* Condition: sane minimum free stem length (chord to beams) */
961 lengths = ly_assoc_get (ly_symbol2scm ("beamed-minimum-free-lengths"),
964 Real ideal_minimum_free
965 = (scm_is_pair (lengths)
966 ? (scm_to_double (robust_list_ref (beam_count - 1, lengths))
971 Real height_of_my_trem = 0.0;
972 Grob *trem = unsmob_grob (me->get_object ("tremolo-flag"));
976 = Stem_tremolo::vertical_length (trem)
977 /* hack a bit of space around the trem. */
982 It seems that also for ideal minimum length, we must use
983 the maximum beam count (for this direction):
985 \score { \relative c'' { a8[ a32] } }
987 must be horizontal. */
988 Real height_of_my_beams = beam_thickness
989 + (beam_count - 1) * beam_translation;
991 Real ideal_minimum_length = ideal_minimum_free
994 /* stem only extends to center of beam */
995 - 0.5 * beam_thickness;
997 ideal_length = max (ideal_length, ideal_minimum_length);
999 /* Convert to Y position, calculate for dir == UP */
1001 = /* staff positions */
1002 head_positions (me)[my_dir] * 0.5
1003 * my_dir * staff_space;
1004 Real ideal_y = note_start + ideal_length;
1006 /* Conditions for Y position */
1008 /* Lowest beam of (UP) beam must never be lower than second staffline
1012 Although this (additional) rule is probably correct,
1013 I expect that highest beam (UP) should also never be lower
1014 than middle staffline, just as normal stems.
1018 Obviously not for grace beams.
1020 Also, not for knees. Seems to be a good thing. */
1021 bool no_extend = to_boolean (me->get_property ("no-stem-extend"));
1022 bool is_knee = Beam::is_knee (beam);
1023 if (!no_extend && !is_knee)
1025 /* Highest beam of (UP) beam must never be lower than middle
1027 ideal_y = max (ideal_y, 0.0);
1028 /* Lowest beam of (UP) beam must never be lower than second staffline */
1029 ideal_y = max (ideal_y, (-staff_space
1030 - beam_thickness + height_of_my_beams));
1033 ideal_y -= robust_scm2double (beam->get_property ("shorten"), 0);
1035 SCM bemfl = ly_assoc_get (ly_symbol2scm ("beamed-extreme-minimum-free-lengths"),
1039 = (scm_is_pair (bemfl)
1040 ? (scm_to_double (robust_list_ref (beam_count - 1, bemfl))
1045 Real minimum_length = max (minimum_free, height_of_my_trem)
1046 + height_of_my_beams
1047 /* stem only extends to center of beam */
1048 - 0.5 * beam_thickness;
1051 Real minimum_y = note_start + minimum_length;
1052 Real shortest_y = minimum_y * my_dir;
1054 return scm_list_2 (scm_from_double (ideal_y),
1055 scm_from_double (shortest_y));
1059 Stem::beam_multiplicity (Grob *stem)
1061 SCM beaming = stem->get_property ("beaming");
1062 Slice le = int_list_to_slice (scm_car (beaming));
1063 Slice ri = int_list_to_slice (scm_cdr (beaming));
1069 Stem::is_cross_staff (Grob *stem)
1071 Grob *beam = unsmob_grob (stem->get_object ("beam"));
1072 return beam && Beam::is_cross_staff (beam);
1075 MAKE_SCHEME_CALLBACK (Stem, calc_cross_staff, 1)
1077 Stem::calc_cross_staff (SCM smob)
1079 return scm_from_bool (is_cross_staff (unsmob_grob (smob)));
1083 Stem::flag (Grob *me)
1085 return unsmob_grob (me->get_object ("flag"));
1088 /* FIXME: Too many properties */
1089 ADD_INTERFACE (Stem,
1090 "The stem represents the graphical stem. In addition, it"
1091 " internally connects note heads, beams, and tremolos. Rests"
1092 " and whole notes have invisible stems.\n"
1094 "The following properties may be set in the @code{details}"
1098 "@item beamed-lengths\n"
1099 "List of stem lengths given beam multiplicity.\n"
1100 "@item beamed-minimum-free-lengths\n"
1101 "List of normal minimum free stem lengths (chord to beams)"
1102 " given beam multiplicity.\n"
1103 "@item beamed-extreme-minimum-free-lengths\n"
1104 "List of extreme minimum free stem lengths (chord to beams)"
1105 " given beam multiplicity.\n"
1107 "Default stem lengths. The list gives a length for each"
1109 "@item stem-shorten\n"
1110 "How much a stem in a forced direction should be shortened."
1111 " The list gives an amount depending on the number of flags"
1119 "beamlet-default-length "
1120 "beamlet-max-length-proportion "
1121 "default-direction "
1131 "neutral-direction "
1136 "stem-begin-position "
1144 /****************************************************************/
1146 Stem_info::Stem_info ()
1148 ideal_y_ = shortest_y_ = 0;
1153 Stem_info::scale (Real x)