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 return !is_normal_stem (me)
279 && (robust_scm2double (me->get_property ("stemlet-length"),
284 Stem::is_normal_stem (Grob *me)
286 if (!head_count (me))
289 extract_grob_set (me, "note-heads", heads);
290 SCM style = heads[0]->get_property ("style");
291 return style != ly_symbol2scm ("kievan") && scm_to_int (me->get_property ("duration-log")) >= 1;
294 MAKE_SCHEME_CALLBACK (Stem, pure_height, 3)
296 Stem::pure_height (SCM smob,
300 Grob *me = unsmob_grob (smob);
301 return ly_interval2scm (internal_pure_height (me, true));
305 Stem::internal_pure_height (Grob *me, bool calc_beam)
307 if (!is_normal_stem (me))
308 return Interval (0.0, 0.0);
310 Grob *beam = unsmob_grob (me->get_object ("beam"));
312 Interval iv = internal_height (me, false);
319 Direction dir = get_grob_direction (me);
320 for (DOWN_and_UP (d))
321 overshoot[d] = d == dir ? dir * infinity_f : iv[d];
323 vector<Interval> heights;
324 vector<Grob *> my_stems;
325 extract_grob_set (beam, "normal-stems", normal_stems);
326 for (vsize i = 0; i < normal_stems.size (); i++)
327 if (get_grob_direction (normal_stems[i]) == dir)
329 if (normal_stems[i] != me)
330 heights.push_back (Stem::internal_pure_height (normal_stems[i], false));
332 heights.push_back (iv);
333 my_stems.push_back (normal_stems[i]);
335 //iv.unite (heights.back ());
336 // look for cross staff effects
338 Grob *common = common_refpoint_of_array (my_stems, me, Y_AXIS);
339 Real min_pos = infinity_f;
340 Real max_pos = -infinity_f;
341 for (vsize i = 0; i < my_stems.size (); i++)
343 coords.push_back (my_stems[i]->pure_relative_y_coordinate (common, 0, INT_MAX));
344 min_pos = min (min_pos, coords[i]);
345 max_pos = max (max_pos, coords[i]);
347 for (vsize i = 0; i < heights.size (); i++)
349 heights[i][dir] += dir == DOWN
350 ? coords[i] - max_pos
351 : coords[i] - min_pos;
354 for (vsize i = 0; i < heights.size (); i++) iv.unite (heights[i]);
356 for (vsize i = 0; i < my_stems.size (); i++)
357 cache_pure_height (my_stems[i], iv, heights[i]);
358 iv.intersect (overshoot);
365 Stem::cache_pure_height (Grob *me, Interval iv, Interval my_iv)
368 Direction dir = get_grob_direction (me);
369 for (DOWN_and_UP (d))
370 overshoot[d] = d == dir ? dir * infinity_f : my_iv[d];
372 iv.intersect (overshoot);
373 dynamic_cast<Item *> (me)->cache_pure_height (iv);
376 MAKE_SCHEME_CALLBACK (Stem, calc_stem_end_position, 1)
378 Stem::calc_stem_end_position (SCM smob)
380 Grob *me = unsmob_grob (smob);
381 return scm_from_double (internal_calc_stem_end_position (me, true));
384 MAKE_SCHEME_CALLBACK (Stem, pure_calc_stem_end_position, 3)
386 Stem::pure_calc_stem_end_position (SCM smob,
390 Grob *me = unsmob_grob (smob);
391 return scm_from_double (internal_calc_stem_end_position (me, false));
395 Stem::internal_calc_stem_end_position (Grob *me, bool calc_beam)
397 if (!head_count (me))
400 Grob *beam = get_beam (me);
401 Real ss = Staff_symbol_referencer::staff_space (me);
402 Direction dir = get_grob_direction (me);
404 if (beam && calc_beam)
406 (void) beam->get_property ("quantized-positions");
407 return robust_scm2double (me->get_property ("length"), 0.0)
408 + dir * robust_scm2double (me->get_property ("stem-begin-position"), 0.0);
413 /* WARNING: IN HALF SPACES */
414 SCM details = me->get_property ("details");
415 int durlog = duration_log (me);
417 Real staff_rad = Staff_symbol_referencer::staff_radius (me);
419 SCM s = ly_assoc_get (ly_symbol2scm ("lengths"), details, SCM_EOL);
421 length = 2 * scm_to_double (robust_list_ref (durlog - 2, s));
423 /* Stems in unnatural (forced) direction should be shortened,
424 according to [Roush & Gourlay] */
425 Interval hp = head_positions (me);
426 if (dir && dir * hp[dir] >= 0)
428 SCM sshorten = ly_assoc_get (ly_symbol2scm ("stem-shorten"), details, SCM_EOL);
429 SCM scm_shorten = scm_is_pair (sshorten)
430 ? robust_list_ref (max (duration_log (me) - 2, 0), sshorten) : SCM_EOL;
431 Real shorten_property = 2 * robust_scm2double (scm_shorten, 0);
432 /* change in length between full-size and shortened stems is executed gradually.
433 "transition area" = stems between full-sized and fully-shortened.
435 Real quarter_stem_length = 2 * scm_to_double (robust_list_ref (0, s));
436 /* shortening_step = difference in length between consecutive stem lengths
437 in transition area. The bigger the difference between full-sized
438 and shortened stems, the bigger shortening_step is.
439 (but not greater than 1/2 and not smaller than 1/4).
440 value 6 is heuristic; it determines the suggested transition slope steepnesas.
442 Real shortening_step = min (max (0.25, (shorten_property / 6)), 0.5);
443 /* Shortening of unflagged stems should begin on the first stem that sticks
444 more than 1 staffspace (2 units) out of the staff.
445 Shortening of flagged stems begins in the same moment as unflagged ones,
446 but not earlier than on the middle line note.
448 Real which_step = (min (1.0, quarter_stem_length - (2 * staff_rad) - 2.0)) + abs (hp[dir]);
449 Real shorten = min (max (0.0, (shortening_step * which_step)), shorten_property);
454 length *= robust_scm2double (me->get_property ("length-fraction"), 1.0);
457 Grob *t_flag = unsmob_grob (me->get_object ("tremolo-flag"));
458 if (t_flag && (!unsmob_grob (me->get_object ("beam")) || !calc_beam))
460 /* Crude hack: add extra space if tremolo flag is there.
462 We can't do this for the beam, since we get into a loop
463 (Stem_tremolo::raw_stencil () looks at the beam.) --hwn */
466 + 2 * Stem_tremolo::vertical_length (t_flag) / ss;
468 /* We don't want to add the whole extent of the flag because the trem
469 and the flag can overlap partly. beam_translation gives a good
473 Real beam_trans = Stem_tremolo::get_beam_translation (t_flag);
474 /* the obvious choice is (durlog - 2) here, but we need a bit more space. */
475 minlen += 2 * (durlog - 1.5) * beam_trans;
477 /* up-stems need even a little more space to avoid collisions. This
478 needs to be in sync with the tremolo positioning code in
479 Stem_tremolo::print */
481 minlen += beam_trans;
483 length = max (length, minlen + 1.0);
486 Real stem_end = dir ? hp[dir] + dir * length : 0;
488 /* TODO: change name to extend-stems to staff/center/'() */
489 bool no_extend = to_boolean (me->get_property ("no-stem-extend"));
490 if (!no_extend && dir * stem_end < 0)
496 /* The log of the duration (Number of hooks on the flag minus two) */
498 Stem::duration_log (Grob *me)
500 SCM s = me->get_property ("duration-log");
501 return (scm_is_number (s)) ? scm_to_int (s) : 2;
504 MAKE_SCHEME_CALLBACK (Stem, calc_positioning_done, 1);
506 Stem::calc_positioning_done (SCM smob)
508 Grob *me = unsmob_grob (smob);
509 if (!head_count (me))
512 me->set_property ("positioning-done", SCM_BOOL_T);
514 extract_grob_set (me, "note-heads", ro_heads);
515 vector<Grob *> heads (ro_heads);
516 vector_sort (heads, position_less);
517 Direction dir = get_grob_direction (me);
522 Real thick = thickness (me);
524 Grob *hed = support_head (me);
527 programming_error ("Stem dir must be up or down.");
529 set_grob_direction (me, dir);
532 bool is_harmonic_centered = false;
533 for (vsize i = 0; i < heads.size (); i++)
534 is_harmonic_centered = is_harmonic_centered
535 || heads[i]->get_property ("style") == ly_symbol2scm ("harmonic");
536 is_harmonic_centered = is_harmonic_centered && is_invisible (me);
538 Real w = hed->extent (hed, X_AXIS)[dir];
539 for (vsize i = 0; i < heads.size (); i++)
541 Real amount = w - heads[i]->extent (heads[i], X_AXIS)[dir];
543 if (is_harmonic_centered)
545 = hed->extent (hed, X_AXIS).linear_combination (CENTER)
546 - heads[i]->extent (heads[i], X_AXIS).linear_combination (CENTER);
548 heads[i]->translate_axis (amount, X_AXIS);
551 Real lastpos = Real (Staff_symbol_referencer::get_position (heads[0]));
552 for (vsize i = 1; i < heads.size (); i++)
554 Real p = Staff_symbol_referencer::get_position (heads[i]);
555 Real dy = fabs (lastpos - p);
558 dy should always be 0.5, 0.0, 1.0, but provide safety margin
565 Real ell = heads[i]->extent (heads[i], X_AXIS).length ();
567 Direction d = get_grob_direction (me);
569 Reversed head should be shifted ell-thickness, but this
570 looks too crowded, so we only shift ell-0.5*thickness.
572 This leads to assymetry: Normal heads overlap the
573 stem 100% whereas reversed heads only overlaps the
577 Real reverse_overlap = 0.5;
578 heads[i]->translate_axis ((ell - thick * reverse_overlap) * d,
581 if (is_invisible (me))
582 heads[i]->translate_axis (-thick * (2 - reverse_overlap) * d,
587 For some cases we should kern some more: when the
588 distance between the next or prev note is too large, we'd
589 get large white gaps, eg.
610 MAKE_SCHEME_CALLBACK (Stem, calc_direction, 1);
612 Stem::calc_direction (SCM smob)
614 Grob *me = unsmob_grob (smob);
615 Direction dir = CENTER;
616 if (Grob *beam = unsmob_grob (me->get_object ("beam")))
618 SCM ignore_me = beam->get_property ("direction");
620 dir = get_grob_direction (me);
624 SCM dd = me->get_property ("default-direction");
627 return me->get_property ("neutral-direction");
630 return scm_from_int (dir);
633 MAKE_SCHEME_CALLBACK (Stem, calc_default_direction, 1);
635 Stem::calc_default_direction (SCM smob)
637 Grob *me = unsmob_grob (smob);
639 Direction dir = CENTER;
640 int staff_center = 0;
641 Interval hp = head_positions (me);
644 int udistance = (int) (UP * hp[UP] - staff_center);
645 int ddistance = (int) (DOWN * hp[DOWN] - staff_center);
647 dir = Direction (sign (ddistance - udistance));
650 return scm_from_int (dir);
653 // note - height property necessary to trigger quantized beam positions
654 // otherwise, we could just use Grob::stencil_height_proc
655 MAKE_SCHEME_CALLBACK (Stem, height, 1);
657 Stem::height (SCM smob)
659 Grob *me = unsmob_grob (smob);
660 return ly_interval2scm (internal_height (me, true));
664 Stem::get_reference_head (Grob *me)
666 return to_boolean (me->get_property ("avoid-note-head"))
672 Stem::beam_end_corrective (Grob *me)
674 Grob *beam = unsmob_grob (me->get_object ("beam"));
675 Direction dir = get_grob_direction (me);
680 programming_error ("no stem direction");
683 return dir * Beam::get_beam_thickness (beam) * 0.5;
689 Stem::internal_height (Grob *me, bool calc_beam)
691 Grob *beam = get_beam (me);
692 if (!is_valid_stem (me) && !beam)
695 Direction dir = get_grob_direction (me);
697 if (beam && calc_beam)
699 /* trigger set-stem-lengths. */
700 (void) beam->get_property ("quantized-positions");
704 If there is a beam but no stem, slope calculations depend on this
705 routine to return where the stem end /would/ be.
707 if (calc_beam && !beam && !unsmob_stencil (me->get_property ("stencil")))
710 Real y1 = robust_scm2double ((calc_beam
711 ? me->get_property ("stem-begin-position")
712 : me->get_pure_property ("stem-begin-position", 0, INT_MAX)),
715 Real y2 = dir * robust_scm2double ((calc_beam
716 ? me->get_property ("length")
717 : me->get_pure_property ("length", 0, INT_MAX)),
721 Real half_space = Staff_symbol_referencer::staff_space (me) * 0.5;
723 Interval stem_y = Interval (min (y1, y2), max (y2, y1)) * half_space;
728 MAKE_SCHEME_CALLBACK (Stem, width, 1);
732 Grob *me = unsmob_grob (e);
736 if (is_invisible (me))
740 r = Interval (-1, 1);
741 r *= thickness (me) / 2;
744 return ly_interval2scm (r);
748 Stem::thickness (Grob *me)
750 return scm_to_double (me->get_property ("thickness"))
751 * Staff_symbol_referencer::line_thickness (me);
754 MAKE_SCHEME_CALLBACK (Stem, calc_stem_begin_position, 1);
756 Stem::calc_stem_begin_position (SCM smob)
758 Grob *me = unsmob_grob (smob);
759 return scm_from_double (internal_calc_stem_begin_position (me, true));
762 MAKE_SCHEME_CALLBACK (Stem, pure_calc_stem_begin_position, 3);
764 Stem::pure_calc_stem_begin_position (SCM smob,
768 Grob *me = unsmob_grob (smob);
769 return scm_from_double (internal_calc_stem_begin_position (me, false));
773 Stem::internal_calc_stem_begin_position (Grob *me, bool calc_beam)
775 Grob *beam = get_beam (me);
776 Real ss = Staff_symbol_referencer::staff_space (me);
777 if (beam && calc_beam)
779 (void) beam->get_property ("quantized-positions");
780 return robust_scm2double (me->get_property ("stem-begin-position"), 0.0);
783 Direction d = get_grob_direction (me);
784 Grob *lh = get_reference_head (me);
789 Real pos = Staff_symbol_referencer::get_position (lh);
791 if (Grob *head = support_head (me))
793 Interval head_height = head->extent (head, Y_AXIS);
794 Real y_attach = Note_head::stem_attachment_coordinate (head, Y_AXIS);
796 y_attach = head_height.linear_combination (y_attach);
797 pos += d * y_attach * 2 / ss;
804 Stem::is_valid_stem (Grob *me)
806 /* TODO: make the stem start a direction ?
807 This is required to avoid stems passing in tablature chords. */
808 Grob *lh = get_reference_head (me);
809 Grob *beam = unsmob_grob (me->get_object ("beam"));
814 if (lh && robust_scm2int (lh->get_property ("duration-log"), 0) < 1)
817 if (is_invisible (me))
823 MAKE_SCHEME_CALLBACK (Stem, print, 1);
825 Stem::print (SCM smob)
827 Grob *me = unsmob_grob (smob);
828 if (!is_valid_stem (me))
831 Direction dir = get_grob_direction (me);
832 Real y1 = robust_scm2double (me->get_property ("stem-begin-position"), 0.0);
833 Real y2 = dir * robust_scm2double (me->get_property ("length"), 0.0) + y1;
835 Real half_space = Staff_symbol_referencer::staff_space (me) * 0.5;
837 Interval stem_y = Interval (min (y1, y2), max (y2, y1)) * half_space;
839 stem_y[dir] -= beam_end_corrective (me);
842 Real stem_width = thickness (me);
844 = me->layout ()->get_dimension (ly_symbol2scm ("blot-diameter"));
846 Box b = Box (Interval (-stem_width / 2, stem_width / 2),
850 Stencil ss = Lookup::round_filled_box (b, blot);
851 mol.add_stencil (ss);
853 return mol.smobbed_copy ();
857 move the stem to right of the notehead if it is up.
859 MAKE_SCHEME_CALLBACK (Stem, offset_callback, 1);
861 Stem::offset_callback (SCM smob)
863 Grob *me = unsmob_grob (smob);
865 extract_grob_set (me, "rests", rests);
868 Grob *rest = rests.back ();
869 Real r = rest->extent (rest, X_AXIS).center ();
870 return scm_from_double (r);
873 if (Grob *f = first_head (me))
875 Interval head_wid = f->extent (f, X_AXIS);
878 if (is_invisible (me))
881 attach = Note_head::stem_attachment_coordinate (f, X_AXIS);
883 Direction d = get_grob_direction (me);
884 Real real_attach = head_wid.linear_combination (d * attach);
885 Real r = real_attach;
887 /* If not centered: correct for stem thickness. */
888 string style = robust_symbol2string (f->get_property ("style"), "default");
889 if (attach && style != "mensural"
890 && style != "neomensural"
891 && style != "petrucci")
893 Real rule_thick = thickness (me);
894 r += -d * rule_thick * 0.5;
896 return scm_from_double (r);
899 programming_error ("Weird stem.");
900 return scm_from_double (0.0);
904 Stem::get_beam (Grob *me)
906 SCM b = me->get_object ("beam");
907 return dynamic_cast<Spanner *> (unsmob_grob (b));
911 Stem::get_stem_info (Grob *me)
914 si.dir_ = get_grob_direction (me);
916 SCM scm_info = me->get_property ("stem-info");
917 si.ideal_y_ = scm_to_double (scm_car (scm_info));
918 si.shortest_y_ = scm_to_double (scm_cadr (scm_info));
922 MAKE_SCHEME_CALLBACK (Stem, calc_stem_info, 1);
924 Stem::calc_stem_info (SCM smob)
926 Grob *me = unsmob_grob (smob);
927 Direction my_dir = get_grob_direction (me);
931 programming_error ("no stem dir set");
935 Real staff_space = Staff_symbol_referencer::staff_space (me);
936 Grob *beam = get_beam (me);
940 (void) beam->get_property ("beaming");
943 Real beam_translation = Beam::get_beam_translation (beam);
944 Real beam_thickness = Beam::get_beam_thickness (beam);
945 int beam_count = Beam::get_direction_beam_count (beam, my_dir);
947 = robust_scm2double (me->get_property ("length-fraction"), 1.0);
949 /* Simple standard stem length */
950 SCM details = me->get_property ("details");
951 SCM lengths = ly_assoc_get (ly_symbol2scm ("beamed-lengths"), details, SCM_EOL);
954 = (scm_is_pair (lengths)
955 ? (scm_to_double (robust_list_ref (beam_count - 1, lengths))
959 stem only extends to center of beam
961 - 0.5 * beam_thickness)
964 /* Condition: sane minimum free stem length (chord to beams) */
965 lengths = ly_assoc_get (ly_symbol2scm ("beamed-minimum-free-lengths"),
968 Real ideal_minimum_free
969 = (scm_is_pair (lengths)
970 ? (scm_to_double (robust_list_ref (beam_count - 1, lengths))
975 Real height_of_my_trem = 0.0;
976 Grob *trem = unsmob_grob (me->get_object ("tremolo-flag"));
980 = Stem_tremolo::vertical_length (trem)
981 /* hack a bit of space around the trem. */
986 It seems that also for ideal minimum length, we must use
987 the maximum beam count (for this direction):
989 \score { \relative c'' { a8[ a32] } }
991 must be horizontal. */
992 Real height_of_my_beams = beam_thickness
993 + (beam_count - 1) * beam_translation;
995 Real ideal_minimum_length = ideal_minimum_free
998 /* stem only extends to center of beam */
999 - 0.5 * beam_thickness;
1001 ideal_length = max (ideal_length, ideal_minimum_length);
1003 /* Convert to Y position, calculate for dir == UP */
1005 = /* staff positions */
1006 head_positions (me)[my_dir] * 0.5
1007 * my_dir * staff_space;
1008 Real ideal_y = note_start + ideal_length;
1010 /* Conditions for Y position */
1012 /* Lowest beam of (UP) beam must never be lower than second staffline
1016 Although this (additional) rule is probably correct,
1017 I expect that highest beam (UP) should also never be lower
1018 than middle staffline, just as normal stems.
1022 Obviously not for grace beams.
1024 Also, not for knees. Seems to be a good thing. */
1025 bool no_extend = to_boolean (me->get_property ("no-stem-extend"));
1026 bool is_knee = Beam::is_knee (beam);
1027 if (!no_extend && !is_knee)
1029 /* Highest beam of (UP) beam must never be lower than middle
1031 ideal_y = max (ideal_y, 0.0);
1032 /* Lowest beam of (UP) beam must never be lower than second staffline */
1033 ideal_y = max (ideal_y, (-staff_space
1034 - beam_thickness + height_of_my_beams));
1037 ideal_y -= robust_scm2double (beam->get_property ("shorten"), 0);
1039 SCM bemfl = ly_assoc_get (ly_symbol2scm ("beamed-extreme-minimum-free-lengths"),
1043 = (scm_is_pair (bemfl)
1044 ? (scm_to_double (robust_list_ref (beam_count - 1, bemfl))
1049 Real minimum_length = max (minimum_free, height_of_my_trem)
1050 + height_of_my_beams
1051 /* stem only extends to center of beam */
1052 - 0.5 * beam_thickness;
1055 Real minimum_y = note_start + minimum_length;
1056 Real shortest_y = minimum_y * my_dir;
1058 return scm_list_2 (scm_from_double (ideal_y),
1059 scm_from_double (shortest_y));
1063 Stem::beam_multiplicity (Grob *stem)
1065 SCM beaming = stem->get_property ("beaming");
1066 Slice le = int_list_to_slice (scm_car (beaming));
1067 Slice ri = int_list_to_slice (scm_cdr (beaming));
1073 Stem::is_cross_staff (Grob *stem)
1075 Grob *beam = unsmob_grob (stem->get_object ("beam"));
1076 return beam && Beam::is_cross_staff (beam);
1079 MAKE_SCHEME_CALLBACK (Stem, calc_cross_staff, 1)
1081 Stem::calc_cross_staff (SCM smob)
1083 return scm_from_bool (is_cross_staff (unsmob_grob (smob)));
1087 Stem::flag (Grob *me)
1089 return unsmob_grob (me->get_object ("flag"));
1092 /* FIXME: Too many properties */
1093 ADD_INTERFACE (Stem,
1094 "The stem represents the graphical stem. In addition, it"
1095 " internally connects note heads, beams, and tremolos. Rests"
1096 " and whole notes have invisible stems.\n"
1098 "The following properties may be set in the @code{details}"
1102 "@item beamed-lengths\n"
1103 "List of stem lengths given beam multiplicity.\n"
1104 "@item beamed-minimum-free-lengths\n"
1105 "List of normal minimum free stem lengths (chord to beams)"
1106 " given beam multiplicity.\n"
1107 "@item beamed-extreme-minimum-free-lengths\n"
1108 "List of extreme minimum free stem lengths (chord to beams)"
1109 " given beam multiplicity.\n"
1111 "Default stem lengths. The list gives a length for each"
1113 "@item stem-shorten\n"
1114 "How much a stem in a forced direction should be shortened."
1115 " The list gives an amount depending on the number of flags"
1123 "beamlet-default-length "
1124 "beamlet-max-length-proportion "
1125 "default-direction "
1135 "neutral-direction "
1140 "stem-begin-position "
1148 /****************************************************************/
1150 Stem_info::Stem_info ()
1152 ideal_y_ = shortest_y_ = 0;
1157 Stem_info::scale (Real x)