2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 1996--2011 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));
163 (void) me->extent (me, Y_AXIS);
166 /* Note head that determines hshift for upstems
167 WARNING: triggers direction */
169 Stem::support_head (Grob *me)
171 extract_grob_set (me, "note-heads", heads);
172 if (heads.size () == 1)
175 return first_head (me);
179 Stem::head_count (Grob *me)
181 return Pointer_group_interface::count (me, ly_symbol2scm ("note-heads"));
184 /* The note head which forms one end of the stem.
185 WARNING: triggers direction */
187 Stem::first_head (Grob *me)
189 Direction d = get_grob_direction (me);
191 return extremal_heads (me)[-d];
195 /* The note head opposite to the first head. */
197 Stem::last_head (Grob *me)
199 Direction d = get_grob_direction (me);
201 return extremal_heads (me)[d];
206 START is part where stem reaches `last' head.
208 This function returns a drul with (bottom-head, top-head).
211 Stem::extremal_heads (Grob *me)
213 const int inf = INT_MAX;
214 Drul_array<int> extpos;
218 Drul_array<Grob *> exthead (0, 0);
219 extract_grob_set (me, "note-heads", heads);
221 for (vsize i = heads.size (); i--;)
224 int p = Staff_symbol_referencer::get_rounded_position (n);
229 if (d * p > d * extpos[d])
235 while (flip (&d) != DOWN);
240 /* The positions, in ascending order. */
242 Stem::note_head_positions (Grob *me)
245 extract_grob_set (me, "note-heads", heads);
247 for (vsize i = heads.size (); i--;)
250 int p = Staff_symbol_referencer::get_rounded_position (n);
255 vector_sort (ps, less<int> ());
260 Stem::add_head (Grob *me, Grob *n)
262 n->set_object ("stem", me->self_scm ());
264 if (Note_head::has_interface (n))
265 Pointer_group_interface::add_grob (me, ly_symbol2scm ("note-heads"), n);
266 else if (Rest::has_interface (n))
267 Pointer_group_interface::add_grob (me, ly_symbol2scm ("rests"), n);
271 Stem::is_invisible (Grob *me)
273 return !is_normal_stem (me)
274 && (robust_scm2double (me->get_property ("stemlet-length"),
279 Stem::is_normal_stem (Grob *me)
281 return head_count (me) && scm_to_int (me->get_property ("duration-log")) >= 1;
284 MAKE_SCHEME_CALLBACK (Stem, pure_height, 3)
286 Stem::pure_height (SCM smob,
290 Grob *me = unsmob_grob (smob);
291 return ly_interval2scm (internal_pure_height (me, true));
295 Stem::internal_pure_height (Grob *me, bool calc_beam)
297 if (!is_normal_stem (me))
298 return Interval (0.0, 0.0);
300 Grob *beam = unsmob_grob (me->get_object ("beam"));
302 Interval iv = internal_height (me, false);
306 if (!to_boolean (me->get_property ("cross-staff")) && calc_beam)
309 Direction dir = get_grob_direction (me);
312 overshoot[d] = d == dir ? dir * infinity_f : iv[d];
313 while (flip (&d) != DOWN);
315 vector<Interval> heights;
316 vector<Grob *> my_stems;
317 extract_grob_set (beam, "normal-stems", normal_stems);
318 for (vsize i = 0; i < normal_stems.size (); i++)
319 if (normal_stems[i] != me && get_grob_direction (normal_stems[i]) == dir)
321 heights.push_back (Stem::internal_pure_height (normal_stems[i], false));
322 my_stems.push_back (normal_stems[i]);
323 iv.unite (heights.back ());
325 for (vsize i = 0; i < my_stems.size (); i++)
326 cache_pure_height (my_stems[i], iv, heights[i]);
327 iv.intersect (overshoot);
334 Stem::cache_pure_height (Grob *me, Interval iv, Interval my_iv)
337 Direction dir = get_grob_direction (me);
340 overshoot[d] = d == dir ? dir * infinity_f : my_iv[d];
341 while (flip (&d) != DOWN);
343 iv.intersect (overshoot);
344 dynamic_cast<Item *> (me)->cache_pure_height (iv);
347 MAKE_SCHEME_CALLBACK (Stem, calc_stem_end_position, 1)
349 Stem::calc_stem_end_position (SCM smob)
351 Grob *me = unsmob_grob (smob);
352 return scm_from_double (internal_calc_stem_end_position (me, true));
355 MAKE_SCHEME_CALLBACK (Stem, pure_calc_stem_end_position, 3)
357 Stem::pure_calc_stem_end_position (SCM smob,
361 Grob *me = unsmob_grob (smob);
362 return scm_from_double (internal_calc_stem_end_position (me, false));
366 Stem::internal_calc_stem_end_position (Grob *me, bool calc_beam)
368 if (!head_count (me))
371 Grob *beam = get_beam (me);
372 Real ss = Staff_symbol_referencer::staff_space (me);
373 Direction dir = get_grob_direction (me);
375 if (beam && calc_beam)
377 (void) beam->get_property ("quantized-positions");
378 return robust_scm2double (me->get_property ("length"), 0.0)
379 + dir * robust_scm2double (me->get_property ("stem-begin-position"), 0.0);
384 /* WARNING: IN HALF SPACES */
385 SCM details = me->get_property ("details");
386 int durlog = duration_log (me);
388 Real staff_rad = Staff_symbol_referencer::staff_radius (me);
390 SCM s = ly_assoc_get (ly_symbol2scm ("lengths"), details, SCM_EOL);
392 length = 2 * scm_to_double (robust_list_ref (durlog - 2, s));
395 /* Stems in unnatural (forced) direction should be shortened,
396 according to [Roush & Gourlay] */
397 Interval hp = head_positions (me);
398 if (dir && dir * hp[dir] >= 0)
400 SCM sshorten = ly_assoc_get (ly_symbol2scm ("stem-shorten"), details, SCM_EOL);
401 SCM scm_shorten = scm_is_pair (sshorten)
402 ? robust_list_ref (max (duration_log (me) - 2, 0), sshorten) : SCM_EOL;
403 Real shorten_property = 2 * robust_scm2double (scm_shorten, 0);
404 /* change in length between full-size and shortened stems is executed gradually.
405 "transition area" = stems between full-sized and fully-shortened.
407 Real quarter_stem_length = 2 * scm_to_double (robust_list_ref (0, s));
408 /* shortening_step = difference in length between consecutive stem lengths
409 in transition area. The bigger the difference between full-sized
410 and shortened stems, the bigger shortening_step is.
411 (but not greater than 1/2 and not smaller than 1/4).
412 value 6 is heuristic; it determines the suggested transition slope steepnesas.
414 Real shortening_step = min (max (0.25, (shorten_property / 6)), 0.5);
415 /* Shortening of unflagged stems should begin on the first stem that sticks
416 more than 1 staffspace (2 units) out of the staff.
417 Shortening of flagged stems begins in the same moment as unflagged ones,
418 but not earlier than on the middle line note.
420 Real which_step = (min (1.0, quarter_stem_length - (2 * staff_rad) - 2.0)) + abs (hp[dir]);
421 Real shorten = min (max (0.0, (shortening_step * which_step)), shorten_property);
426 length *= robust_scm2double (me->get_property ("length-fraction"), 1.0);
429 Grob *t_flag = unsmob_grob (me->get_object ("tremolo-flag"));
430 if (t_flag && (!unsmob_grob (me->get_object ("beam")) || !calc_beam))
432 /* Crude hack: add extra space if tremolo flag is there.
434 We can't do this for the beam, since we get into a loop
435 (Stem_tremolo::raw_stencil () looks at the beam.) --hwn */
438 + 2 * Stem_tremolo::vertical_length (t_flag) / ss;
440 /* We don't want to add the whole extent of the flag because the trem
441 and the flag can overlap partly. beam_translation gives a good
445 Real beam_trans = Stem_tremolo::get_beam_translation (t_flag);
446 /* the obvious choice is (durlog - 2) here, but we need a bit more space. */
447 minlen += 2 * (durlog - 1.5) * beam_trans;
449 /* up-stems need even a little more space to avoid collisions. This
450 needs to be in sync with the tremolo positioning code in
451 Stem_tremolo::print */
453 minlen += beam_trans;
455 length = max (length, minlen + 1.0);
458 Real stem_end = dir ? hp[dir] + dir * length : 0;
460 /* TODO: change name to extend-stems to staff/center/'() */
461 bool no_extend = to_boolean (me->get_property ("no-stem-extend"));
462 if (!no_extend && dir * stem_end < 0)
468 /* The log of the duration (Number of hooks on the flag minus two) */
470 Stem::duration_log (Grob *me)
472 SCM s = me->get_property ("duration-log");
473 return (scm_is_number (s)) ? scm_to_int (s) : 2;
476 MAKE_SCHEME_CALLBACK (Stem, calc_positioning_done, 1);
478 Stem::calc_positioning_done (SCM smob)
480 Grob *me = unsmob_grob (smob);
481 if (!head_count (me))
484 me->set_property ("positioning-done", SCM_BOOL_T);
486 extract_grob_set (me, "note-heads", ro_heads);
487 vector<Grob *> heads (ro_heads);
488 vector_sort (heads, position_less);
489 Direction dir = get_grob_direction (me);
494 Real thick = thickness (me);
496 Grob *hed = support_head (me);
499 programming_error ("Stem dir must be up or down.");
501 set_grob_direction (me, dir);
504 bool is_harmonic_centered = false;
505 for (vsize i = 0; i < heads.size (); i++)
506 is_harmonic_centered = is_harmonic_centered
507 || heads[i]->get_property ("style") == ly_symbol2scm ("harmonic");
508 is_harmonic_centered = is_harmonic_centered && is_invisible (me);
510 Real w = hed->extent (hed, X_AXIS)[dir];
511 for (vsize i = 0; i < heads.size (); i++)
513 Real amount = w - heads[i]->extent (heads[i], X_AXIS)[dir];
515 if (is_harmonic_centered)
517 = hed->extent (hed, X_AXIS).linear_combination (CENTER)
518 - heads[i]->extent (heads[i], X_AXIS).linear_combination (CENTER);
520 heads[i]->translate_axis (amount, X_AXIS);
523 Real lastpos = Real (Staff_symbol_referencer::get_position (heads[0]));
524 for (vsize i = 1; i < heads.size (); i++)
526 Real p = Staff_symbol_referencer::get_position (heads[i]);
527 Real dy = fabs (lastpos - p);
530 dy should always be 0.5, 0.0, 1.0, but provide safety margin
537 Real ell = heads[i]->extent (heads[i], X_AXIS).length ();
539 Direction d = get_grob_direction (me);
541 Reversed head should be shifted ell-thickness, but this
542 looks too crowded, so we only shift ell-0.5*thickness.
544 This leads to assymetry: Normal heads overlap the
545 stem 100% whereas reversed heads only overlaps the
549 Real reverse_overlap = 0.5;
550 heads[i]->translate_axis ((ell - thick * reverse_overlap) * d,
553 if (is_invisible (me))
554 heads[i]->translate_axis (-thick * (2 - reverse_overlap) * d,
559 For some cases we should kern some more: when the
560 distance between the next or prev note is too large, we'd
561 get large white gaps, eg.
582 MAKE_SCHEME_CALLBACK (Stem, calc_direction, 1);
584 Stem::calc_direction (SCM smob)
586 Grob *me = unsmob_grob (smob);
587 Direction dir = CENTER;
588 if (Grob *beam = unsmob_grob (me->get_object ("beam")))
590 SCM ignore_me = beam->get_property ("direction");
592 dir = get_grob_direction (me);
596 SCM dd = me->get_property ("default-direction");
599 return me->get_property ("neutral-direction");
602 return scm_from_int (dir);
605 MAKE_SCHEME_CALLBACK (Stem, calc_default_direction, 1);
607 Stem::calc_default_direction (SCM smob)
609 Grob *me = unsmob_grob (smob);
611 Direction dir = CENTER;
612 int staff_center = 0;
613 Interval hp = head_positions (me);
616 int udistance = (int) (UP * hp[UP] - staff_center);
617 int ddistance = (int) (DOWN * hp[DOWN] - staff_center);
619 dir = Direction (sign (ddistance - udistance));
622 return scm_from_int (dir);
625 // note - height property necessary to trigger quantized beam positions
626 // otherwise, we could just use Grob::stencil_height_proc
627 MAKE_SCHEME_CALLBACK (Stem, height, 1);
629 Stem::height (SCM smob)
631 Grob *me = unsmob_grob (smob);
632 return ly_interval2scm (internal_height (me, true));
636 Stem::get_reference_head (Grob *me)
638 return to_boolean (me->get_property ("avoid-note-head"))
644 Stem::beam_end_corrective (Grob *me)
646 Grob *beam = unsmob_grob (me->get_object ("beam"));
647 Direction dir = get_grob_direction (me);
652 programming_error ("no stem direction");
655 return dir * Beam::get_beam_thickness (beam) * 0.5;
661 Stem::internal_height (Grob *me, bool calc_beam)
663 Grob *beam = get_beam (me);
664 if (!is_valid_stem (me) && ! beam)
667 Direction dir = get_grob_direction (me);
669 if (beam && calc_beam)
671 /* trigger set-stem-lengths. */
672 (void) beam->get_property ("quantized-positions");
675 Real y1 = robust_scm2double ((calc_beam
676 ? me->get_property ("stem-begin-position")
677 : me->get_pure_property ("stem-begin-position", 0, INT_MAX)),
680 Real y2 = dir * robust_scm2double ((calc_beam
681 ? me->get_property ("length")
682 : me->get_pure_property ("length", 0, INT_MAX)),
686 Real half_space = Staff_symbol_referencer::staff_space (me) * 0.5;
688 Interval stem_y = Interval (min (y1, y2), max (y2, y1)) * half_space;
693 MAKE_SCHEME_CALLBACK (Stem, width, 1);
697 Grob *me = unsmob_grob (e);
701 if (is_invisible (me))
705 r = Interval (-1, 1);
706 r *= thickness (me) / 2;
709 return ly_interval2scm (r);
713 Stem::thickness (Grob *me)
715 return scm_to_double (me->get_property ("thickness"))
716 * Staff_symbol_referencer::line_thickness (me);
719 MAKE_SCHEME_CALLBACK (Stem, calc_stem_begin_position, 1);
721 Stem::calc_stem_begin_position (SCM smob)
723 Grob *me = unsmob_grob (smob);
724 return scm_from_double (internal_calc_stem_begin_position (me, true));
727 MAKE_SCHEME_CALLBACK (Stem, pure_calc_stem_begin_position, 3);
729 Stem::pure_calc_stem_begin_position (SCM smob,
733 Grob *me = unsmob_grob (smob);
734 return scm_from_double (internal_calc_stem_begin_position (me, false));
738 Stem::internal_calc_stem_begin_position (Grob *me, bool calc_beam)
740 Grob *beam = get_beam (me);
741 Real ss = Staff_symbol_referencer::staff_space (me);
742 if (beam && calc_beam)
744 (void) beam->get_property ("quantized-positions");
745 return robust_scm2double (me->get_property ("stem-begin-position"), 0.0);
748 Direction d = get_grob_direction (me);
749 Grob *lh = get_reference_head (me);
754 Real pos = Staff_symbol_referencer::get_position (lh);
756 if (Grob *head = support_head (me))
758 Interval head_height = head->extent (head, Y_AXIS);
759 Real y_attach = Note_head::stem_attachment_coordinate (head, Y_AXIS);
761 y_attach = head_height.linear_combination (y_attach);
762 pos += d * y_attach * 2 / ss;
769 Stem::is_valid_stem (Grob *me)
771 /* TODO: make the stem start a direction ?
772 This is required to avoid stems passing in tablature chords. */
773 Grob *lh = get_reference_head (me);
774 Grob *beam = unsmob_grob (me->get_object ("beam"));
779 if (lh && robust_scm2int (lh->get_property ("duration-log"), 0) < 1)
782 if (is_invisible (me))
788 MAKE_SCHEME_CALLBACK (Stem, print, 1);
790 Stem::print (SCM smob)
792 Grob *me = unsmob_grob (smob);
793 if (!is_valid_stem (me))
796 Direction dir = get_grob_direction (me);
797 Real y1 = robust_scm2double (me->get_property ("stem-begin-position"), 0.0);
798 Real y2 = dir * robust_scm2double (me->get_property ("length"), 0.0) + y1;
800 Real half_space = Staff_symbol_referencer::staff_space (me) * 0.5;
802 Interval stem_y = Interval (min (y1, y2), max (y2, y1)) * half_space;
804 stem_y[dir] -= beam_end_corrective (me);
807 Real stem_width = thickness (me);
809 = me->layout ()->get_dimension (ly_symbol2scm ("blot-diameter"));
811 Box b = Box (Interval (-stem_width / 2, stem_width / 2),
815 Stencil ss = Lookup::round_filled_box (b, blot);
816 mol.add_stencil (ss);
818 return mol.smobbed_copy ();
822 move the stem to right of the notehead if it is up.
824 MAKE_SCHEME_CALLBACK (Stem, offset_callback, 1);
826 Stem::offset_callback (SCM smob)
828 Grob *me = unsmob_grob (smob);
830 extract_grob_set (me, "rests", rests);
833 Grob *rest = rests.back ();
834 Real r = rest->extent (rest, X_AXIS).center ();
835 return scm_from_double (r);
838 if (Grob *f = first_head (me))
840 Interval head_wid = f->extent (f, X_AXIS);
843 if (is_invisible (me))
846 attach = Note_head::stem_attachment_coordinate (f, X_AXIS);
848 Direction d = get_grob_direction (me);
849 Real real_attach = head_wid.linear_combination (d * attach);
850 Real r = real_attach;
852 /* If not centered: correct for stem thickness. */
855 Real rule_thick = thickness (me);
856 r += -d * rule_thick * 0.5;
858 return scm_from_double (r);
861 programming_error ("Weird stem.");
862 return scm_from_double (0.0);
866 Stem::get_beam (Grob *me)
868 SCM b = me->get_object ("beam");
869 return dynamic_cast<Spanner *> (unsmob_grob (b));
873 Stem::get_stem_info (Grob *me)
876 si.dir_ = get_grob_direction (me);
878 SCM scm_info = me->get_property ("stem-info");
879 si.ideal_y_ = scm_to_double (scm_car (scm_info));
880 si.shortest_y_ = scm_to_double (scm_cadr (scm_info));
884 MAKE_SCHEME_CALLBACK (Stem, calc_stem_info, 1);
886 Stem::calc_stem_info (SCM smob)
888 Grob *me = unsmob_grob (smob);
889 Direction my_dir = get_grob_direction (me);
893 programming_error ("no stem dir set");
897 Real staff_space = Staff_symbol_referencer::staff_space (me);
898 Grob *beam = get_beam (me);
902 (void) beam->get_property ("beaming");
905 Real beam_translation = Beam::get_beam_translation (beam);
906 Real beam_thickness = Beam::get_beam_thickness (beam);
907 int beam_count = Beam::get_direction_beam_count (beam, my_dir);
909 = robust_scm2double (me->get_property ("length-fraction"), 1.0);
911 /* Simple standard stem length */
912 SCM details = me->get_property ("details");
913 SCM lengths = ly_assoc_get (ly_symbol2scm ("beamed-lengths"), details, SCM_EOL);
916 = (scm_is_pair (lengths)
917 ? (scm_to_double (robust_list_ref (beam_count - 1, lengths))
921 stem only extends to center of beam
923 - 0.5 * beam_thickness)
926 /* Condition: sane minimum free stem length (chord to beams) */
927 lengths = ly_assoc_get (ly_symbol2scm ("beamed-minimum-free-lengths"),
930 Real ideal_minimum_free
931 = (scm_is_pair (lengths)
932 ? (scm_to_double (robust_list_ref (beam_count - 1, lengths))
937 Real height_of_my_trem = 0.0;
938 Grob *trem = unsmob_grob (me->get_object ("tremolo-flag"));
942 = Stem_tremolo::vertical_length (trem)
943 /* hack a bit of space around the trem. */
948 It seems that also for ideal minimum length, we must use
949 the maximum beam count (for this direction):
951 \score { \relative c'' { a8[ a32] } }
953 must be horizontal. */
954 Real height_of_my_beams = beam_thickness
955 + (beam_count - 1) * beam_translation;
957 Real ideal_minimum_length = ideal_minimum_free
960 /* stem only extends to center of beam */
961 - 0.5 * beam_thickness;
963 ideal_length = max (ideal_length, ideal_minimum_length);
965 /* Convert to Y position, calculate for dir == UP */
967 = /* staff positions */
968 head_positions (me)[my_dir] * 0.5
969 * my_dir * staff_space;
970 Real ideal_y = note_start + ideal_length;
972 /* Conditions for Y position */
974 /* Lowest beam of (UP) beam must never be lower than second staffline
978 Although this (additional) rule is probably correct,
979 I expect that highest beam (UP) should also never be lower
980 than middle staffline, just as normal stems.
984 Obviously not for grace beams.
986 Also, not for knees. Seems to be a good thing. */
987 bool no_extend = to_boolean (me->get_property ("no-stem-extend"));
988 bool is_knee = to_boolean (beam->get_property ("knee"));
989 if (!no_extend && !is_knee)
991 /* Highest beam of (UP) beam must never be lower than middle
993 ideal_y = max (ideal_y, 0.0);
994 /* Lowest beam of (UP) beam must never be lower than second staffline */
995 ideal_y = max (ideal_y, (-staff_space
996 - beam_thickness + height_of_my_beams));
999 ideal_y -= robust_scm2double (beam->get_property ("shorten"), 0);
1001 SCM bemfl = ly_assoc_get (ly_symbol2scm ("beamed-extreme-minimum-free-lengths"),
1005 = (scm_is_pair (bemfl)
1006 ? (scm_to_double (robust_list_ref (beam_count - 1, bemfl))
1011 Real minimum_length = max (minimum_free, height_of_my_trem)
1012 + height_of_my_beams
1013 /* stem only extends to center of beam */
1014 - 0.5 * beam_thickness;
1017 Real minimum_y = note_start + minimum_length;
1018 Real shortest_y = minimum_y * my_dir;
1020 return scm_list_2 (scm_from_double (ideal_y),
1021 scm_from_double (shortest_y));
1025 Stem::beam_multiplicity (Grob *stem)
1027 SCM beaming = stem->get_property ("beaming");
1028 Slice le = int_list_to_slice (scm_car (beaming));
1029 Slice ri = int_list_to_slice (scm_cdr (beaming));
1035 Stem::is_cross_staff (Grob *stem)
1037 Grob *beam = unsmob_grob (stem->get_object ("beam"));
1038 return beam && Beam::is_cross_staff (beam);
1041 MAKE_SCHEME_CALLBACK (Stem, calc_cross_staff, 1)
1043 Stem::calc_cross_staff (SCM smob)
1045 return scm_from_bool (is_cross_staff (unsmob_grob (smob)));
1049 Stem::flag (Grob *me)
1051 return unsmob_grob (me->get_object ("flag"));
1054 /* FIXME: Too many properties */
1055 ADD_INTERFACE (Stem,
1056 "The stem represents the graphical stem. In addition, it"
1057 " internally connects note heads, beams, and tremolos. Rests"
1058 " and whole notes have invisible stems.\n"
1060 "The following properties may be set in the @code{details}"
1064 "@item beamed-lengths\n"
1065 "List of stem lengths given beam multiplicity.\n"
1066 "@item beamed-minimum-free-lengths\n"
1067 "List of normal minimum free stem lengths (chord to beams)"
1068 " given beam multiplicity.\n"
1069 "@item beamed-extreme-minimum-free-lengths\n"
1070 "List of extreme minimum free stem lengths (chord to beams)"
1071 " given beam multiplicity.\n"
1073 "Default stem lengths. The list gives a length for each"
1075 "@item stem-shorten\n"
1076 "How much a stem in a forced direction should be shortened."
1077 " The list gives an amount depending on the number of flags"
1085 "beamlet-default-length "
1086 "beamlet-max-length-proportion "
1087 "default-direction "
1096 "neutral-direction "
1101 "stem-begin-position "
1108 /****************************************************************/
1110 Stem_info::Stem_info ()
1112 ideal_y_ = shortest_y_ = 0;
1117 Stem_info::scale (Real x)