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));
165 /* Note head that determines hshift for upstems
166 WARNING: triggers direction */
168 Stem::support_head (Grob *me)
170 extract_grob_set (me, "note-heads", heads);
171 if (heads.size () == 1)
174 return first_head (me);
178 Stem::head_count (Grob *me)
180 return Pointer_group_interface::count (me, ly_symbol2scm ("note-heads"));
183 /* The note head which forms one end of the stem.
184 WARNING: triggers direction */
186 Stem::first_head (Grob *me)
188 Direction d = get_grob_direction (me);
190 return extremal_heads (me)[-d];
194 /* The note head opposite to the first head. */
196 Stem::last_head (Grob *me)
198 Direction d = get_grob_direction (me);
200 return extremal_heads (me)[d];
205 START is part where stem reaches `last' head.
207 This function returns a drul with (bottom-head, top-head).
210 Stem::extremal_heads (Grob *me)
212 const int inf = INT_MAX;
213 Drul_array<int> extpos;
217 Drul_array<Grob *> exthead (0, 0);
218 extract_grob_set (me, "note-heads", heads);
220 for (vsize i = heads.size (); i--;)
223 int p = Staff_symbol_referencer::get_rounded_position (n);
228 if (d * p > d * extpos[d])
234 while (flip (&d) != DOWN);
239 /* The positions, in ascending order. */
241 Stem::note_head_positions (Grob *me)
244 extract_grob_set (me, "note-heads", heads);
246 for (vsize i = heads.size (); i--;)
249 int p = Staff_symbol_referencer::get_rounded_position (n);
254 vector_sort (ps, less<int> ());
259 Stem::add_head (Grob *me, Grob *n)
261 n->set_object ("stem", me->self_scm ());
263 if (Note_head::has_interface (n))
264 Pointer_group_interface::add_grob (me, ly_symbol2scm ("note-heads"), n);
265 else if (Rest::has_interface (n))
266 Pointer_group_interface::add_grob (me, ly_symbol2scm ("rests"), n);
270 Stem::is_invisible (Grob *me)
272 return !is_normal_stem (me)
273 && (robust_scm2double (me->get_property ("stemlet-length"),
278 Stem::is_normal_stem (Grob *me)
280 return head_count (me) && scm_to_int (me->get_property ("duration-log")) >= 1;
283 MAKE_SCHEME_CALLBACK (Stem, pure_height, 3)
285 Stem::pure_height (SCM smob,
289 Grob *me = unsmob_grob (smob);
290 return ly_interval2scm (internal_pure_height (me, true));
294 Stem::internal_pure_height (Grob *me, bool calc_beam)
296 if (!is_normal_stem (me))
297 return Interval (0.0, 0.0);
299 Grob *beam = unsmob_grob (me->get_object ("beam"));
301 Interval iv = internal_height (me, false);
305 if (!to_boolean (me->get_property ("cross-staff")) && calc_beam)
308 Direction dir = get_grob_direction (me);
311 overshoot[d] = d == dir ? dir * infinity_f : iv[d];
312 while (flip (&d) != DOWN);
314 vector<Interval> heights;
315 vector<Grob *> my_stems;
316 extract_grob_set (beam, "normal-stems", normal_stems);
317 for (vsize i = 0; i < normal_stems.size (); i++)
318 if (normal_stems[i] != me && get_grob_direction (normal_stems[i]) == dir)
320 heights.push_back (Stem::internal_pure_height (normal_stems[i], false));
321 my_stems.push_back (normal_stems[i]);
322 iv.unite (heights.back ());
324 for (vsize i = 0; i < my_stems.size (); i++)
325 cache_pure_height (my_stems[i], iv, heights[i]);
326 iv.intersect (overshoot);
333 Stem::cache_pure_height (Grob *me, Interval iv, Interval my_iv)
336 Direction dir = get_grob_direction (me);
339 overshoot[d] = d == dir ? dir * infinity_f : my_iv[d];
340 while (flip (&d) != DOWN);
342 iv.intersect (overshoot);
343 dynamic_cast<Item *> (me)->cache_pure_height (iv);
346 MAKE_SCHEME_CALLBACK (Stem, calc_stem_end_position, 1)
348 Stem::calc_stem_end_position (SCM smob)
350 Grob *me = unsmob_grob (smob);
351 return scm_from_double (internal_calc_stem_end_position (me, true));
354 MAKE_SCHEME_CALLBACK (Stem, pure_calc_stem_end_position, 3)
356 Stem::pure_calc_stem_end_position (SCM smob,
360 Grob *me = unsmob_grob (smob);
361 return scm_from_double (internal_calc_stem_end_position (me, false));
365 Stem::internal_calc_stem_end_position (Grob *me, bool calc_beam)
367 if (!head_count (me))
370 Grob *beam = get_beam (me);
371 Real ss = Staff_symbol_referencer::staff_space (me);
372 Direction dir = get_grob_direction (me);
374 if (beam && calc_beam)
376 (void) beam->get_property ("quantized-positions");
377 return robust_scm2double (me->get_property ("length"), 0.0)
378 + dir * robust_scm2double (me->get_property ("stem-begin-position"), 0.0);
383 /* WARNING: IN HALF SPACES */
384 SCM details = me->get_property ("details");
385 int durlog = duration_log (me);
387 Real staff_rad = Staff_symbol_referencer::staff_radius (me);
389 SCM s = ly_assoc_get (ly_symbol2scm ("lengths"), details, SCM_EOL);
391 length = 2 * scm_to_double (robust_list_ref (durlog - 2, s));
393 /* Stems in unnatural (forced) direction should be shortened,
394 according to [Roush & Gourlay] */
395 Interval hp = head_positions (me);
396 if (dir && dir * hp[dir] >= 0)
398 SCM sshorten = ly_assoc_get (ly_symbol2scm ("stem-shorten"), details, SCM_EOL);
399 SCM scm_shorten = scm_is_pair (sshorten)
400 ? robust_list_ref (max (duration_log (me) - 2, 0), sshorten) : SCM_EOL;
401 Real shorten_property = 2 * robust_scm2double (scm_shorten, 0);
402 /* change in length between full-size and shortened stems is executed gradually.
403 "transition area" = stems between full-sized and fully-shortened.
405 Real quarter_stem_length = 2 * scm_to_double (robust_list_ref (0, s));
406 /* shortening_step = difference in length between consecutive stem lengths
407 in transition area. The bigger the difference between full-sized
408 and shortened stems, the bigger shortening_step is.
409 (but not greater than 1/2 and not smaller than 1/4).
410 value 6 is heuristic; it determines the suggested transition slope steepnesas.
412 Real shortening_step = min (max (0.25, (shorten_property / 6)), 0.5);
413 /* Shortening of unflagged stems should begin on the first stem that sticks
414 more than 1 staffspace (2 units) out of the staff.
415 Shortening of flagged stems begins in the same moment as unflagged ones,
416 but not earlier than on the middle line note.
418 Real which_step = (min (1.0, quarter_stem_length - (2 * staff_rad) - 2.0)) + abs (hp[dir]);
419 Real shorten = min (max (0.0, (shortening_step * which_step)), shorten_property);
424 length *= robust_scm2double (me->get_property ("length-fraction"), 1.0);
427 Grob *t_flag = unsmob_grob (me->get_object ("tremolo-flag"));
428 if (t_flag && (!unsmob_grob (me->get_object ("beam")) || !calc_beam))
430 /* Crude hack: add extra space if tremolo flag is there.
432 We can't do this for the beam, since we get into a loop
433 (Stem_tremolo::raw_stencil () looks at the beam.) --hwn */
436 + 2 * Stem_tremolo::vertical_length (t_flag) / ss;
438 /* We don't want to add the whole extent of the flag because the trem
439 and the flag can overlap partly. beam_translation gives a good
443 Real beam_trans = Stem_tremolo::get_beam_translation (t_flag);
444 /* the obvious choice is (durlog - 2) here, but we need a bit more space. */
445 minlen += 2 * (durlog - 1.5) * beam_trans;
447 /* up-stems need even a little more space to avoid collisions. This
448 needs to be in sync with the tremolo positioning code in
449 Stem_tremolo::print */
451 minlen += beam_trans;
453 length = max (length, minlen + 1.0);
456 Real stem_end = dir ? hp[dir] + dir * length : 0;
458 /* TODO: change name to extend-stems to staff/center/'() */
459 bool no_extend = to_boolean (me->get_property ("no-stem-extend"));
460 if (!no_extend && dir * stem_end < 0)
466 /* The log of the duration (Number of hooks on the flag minus two) */
468 Stem::duration_log (Grob *me)
470 SCM s = me->get_property ("duration-log");
471 return (scm_is_number (s)) ? scm_to_int (s) : 2;
474 MAKE_SCHEME_CALLBACK (Stem, calc_positioning_done, 1);
476 Stem::calc_positioning_done (SCM smob)
478 Grob *me = unsmob_grob (smob);
479 if (!head_count (me))
482 me->set_property ("positioning-done", SCM_BOOL_T);
484 extract_grob_set (me, "note-heads", ro_heads);
485 vector<Grob *> heads (ro_heads);
486 vector_sort (heads, position_less);
487 Direction dir = get_grob_direction (me);
492 Real thick = thickness (me);
494 Grob *hed = support_head (me);
497 programming_error ("Stem dir must be up or down.");
499 set_grob_direction (me, dir);
502 bool is_harmonic_centered = false;
503 for (vsize i = 0; i < heads.size (); i++)
504 is_harmonic_centered = is_harmonic_centered
505 || heads[i]->get_property ("style") == ly_symbol2scm ("harmonic");
506 is_harmonic_centered = is_harmonic_centered && is_invisible (me);
508 Real w = hed->extent (hed, X_AXIS)[dir];
509 for (vsize i = 0; i < heads.size (); i++)
511 Real amount = w - heads[i]->extent (heads[i], X_AXIS)[dir];
513 if (is_harmonic_centered)
515 = hed->extent (hed, X_AXIS).linear_combination (CENTER)
516 - heads[i]->extent (heads[i], X_AXIS).linear_combination (CENTER);
518 heads[i]->translate_axis (amount, X_AXIS);
521 Real lastpos = Real (Staff_symbol_referencer::get_position (heads[0]));
522 for (vsize i = 1; i < heads.size (); i++)
524 Real p = Staff_symbol_referencer::get_position (heads[i]);
525 Real dy = fabs (lastpos - p);
528 dy should always be 0.5, 0.0, 1.0, but provide safety margin
535 Real ell = heads[i]->extent (heads[i], X_AXIS).length ();
537 Direction d = get_grob_direction (me);
539 Reversed head should be shifted ell-thickness, but this
540 looks too crowded, so we only shift ell-0.5*thickness.
542 This leads to assymetry: Normal heads overlap the
543 stem 100% whereas reversed heads only overlaps the
547 Real reverse_overlap = 0.5;
548 heads[i]->translate_axis ((ell - thick * reverse_overlap) * d,
551 if (is_invisible (me))
552 heads[i]->translate_axis (-thick * (2 - reverse_overlap) * d,
557 For some cases we should kern some more: when the
558 distance between the next or prev note is too large, we'd
559 get large white gaps, eg.
580 MAKE_SCHEME_CALLBACK (Stem, calc_direction, 1);
582 Stem::calc_direction (SCM smob)
584 Grob *me = unsmob_grob (smob);
585 Direction dir = CENTER;
586 if (Grob *beam = unsmob_grob (me->get_object ("beam")))
588 SCM ignore_me = beam->get_property ("direction");
590 dir = get_grob_direction (me);
594 SCM dd = me->get_property ("default-direction");
597 return me->get_property ("neutral-direction");
600 return scm_from_int (dir);
603 MAKE_SCHEME_CALLBACK (Stem, calc_default_direction, 1);
605 Stem::calc_default_direction (SCM smob)
607 Grob *me = unsmob_grob (smob);
609 Direction dir = CENTER;
610 int staff_center = 0;
611 Interval hp = head_positions (me);
614 int udistance = (int) (UP * hp[UP] - staff_center);
615 int ddistance = (int) (DOWN * hp[DOWN] - staff_center);
617 dir = Direction (sign (ddistance - udistance));
620 return scm_from_int (dir);
623 // note - height property necessary to trigger quantized beam positions
624 // otherwise, we could just use Grob::stencil_height_proc
625 MAKE_SCHEME_CALLBACK (Stem, height, 1);
627 Stem::height (SCM smob)
629 Grob *me = unsmob_grob (smob);
630 return ly_interval2scm (internal_height (me, true));
634 Stem::get_reference_head (Grob *me)
636 return to_boolean (me->get_property ("avoid-note-head"))
642 Stem::beam_end_corrective (Grob *me)
644 Grob *beam = unsmob_grob (me->get_object ("beam"));
645 Direction dir = get_grob_direction (me);
650 programming_error ("no stem direction");
653 return dir * Beam::get_beam_thickness (beam) * 0.5;
659 Stem::internal_height (Grob *me, bool calc_beam)
661 Grob *beam = get_beam (me);
662 if (!is_valid_stem (me) && ! beam)
665 Direction dir = get_grob_direction (me);
667 if (beam && calc_beam)
669 /* trigger set-stem-lengths. */
670 (void) beam->get_property ("quantized-positions");
673 Real y1 = robust_scm2double ((calc_beam
674 ? me->get_property ("stem-begin-position")
675 : me->get_pure_property ("stem-begin-position", 0, INT_MAX)),
678 Real y2 = dir * robust_scm2double ((calc_beam
679 ? me->get_property ("length")
680 : me->get_pure_property ("length", 0, INT_MAX)),
684 Real half_space = Staff_symbol_referencer::staff_space (me) * 0.5;
686 Interval stem_y = Interval (min (y1, y2), max (y2, y1)) * half_space;
691 MAKE_SCHEME_CALLBACK (Stem, width, 1);
695 Grob *me = unsmob_grob (e);
699 if (is_invisible (me))
703 r = Interval (-1, 1);
704 r *= thickness (me) / 2;
707 return ly_interval2scm (r);
711 Stem::thickness (Grob *me)
713 return scm_to_double (me->get_property ("thickness"))
714 * Staff_symbol_referencer::line_thickness (me);
717 MAKE_SCHEME_CALLBACK (Stem, calc_stem_begin_position, 1);
719 Stem::calc_stem_begin_position (SCM smob)
721 Grob *me = unsmob_grob (smob);
722 return scm_from_double (internal_calc_stem_begin_position (me, true));
725 MAKE_SCHEME_CALLBACK (Stem, pure_calc_stem_begin_position, 3);
727 Stem::pure_calc_stem_begin_position (SCM smob,
731 Grob *me = unsmob_grob (smob);
732 return scm_from_double (internal_calc_stem_begin_position (me, false));
736 Stem::internal_calc_stem_begin_position (Grob *me, bool calc_beam)
738 Grob *beam = get_beam (me);
739 Real ss = Staff_symbol_referencer::staff_space (me);
740 if (beam && calc_beam)
742 (void) beam->get_property ("quantized-positions");
743 return robust_scm2double (me->get_property ("stem-begin-position"), 0.0);
746 Direction d = get_grob_direction (me);
747 Grob *lh = get_reference_head (me);
752 Real pos = Staff_symbol_referencer::get_position (lh);
754 if (Grob *head = support_head (me))
756 Interval head_height = head->extent (head, Y_AXIS);
757 Real y_attach = Note_head::stem_attachment_coordinate (head, Y_AXIS);
759 y_attach = head_height.linear_combination (y_attach);
760 pos += d * y_attach * 2 / ss;
767 Stem::is_valid_stem (Grob *me)
769 /* TODO: make the stem start a direction ?
770 This is required to avoid stems passing in tablature chords. */
771 Grob *lh = get_reference_head (me);
772 Grob *beam = unsmob_grob (me->get_object ("beam"));
777 if (lh && robust_scm2int (lh->get_property ("duration-log"), 0) < 1)
780 if (is_invisible (me))
786 MAKE_SCHEME_CALLBACK (Stem, print, 1);
788 Stem::print (SCM smob)
790 Grob *me = unsmob_grob (smob);
791 if (!is_valid_stem (me))
794 Direction dir = get_grob_direction (me);
795 Real y1 = robust_scm2double (me->get_property ("stem-begin-position"), 0.0);
796 Real y2 = dir * robust_scm2double (me->get_property ("length"), 0.0) + y1;
798 Real half_space = Staff_symbol_referencer::staff_space (me) * 0.5;
800 Interval stem_y = Interval (min (y1, y2), max (y2, y1)) * half_space;
802 stem_y[dir] -= beam_end_corrective (me);
805 Real stem_width = thickness (me);
807 = me->layout ()->get_dimension (ly_symbol2scm ("blot-diameter"));
809 Box b = Box (Interval (-stem_width / 2, stem_width / 2),
813 Stencil ss = Lookup::round_filled_box (b, blot);
814 mol.add_stencil (ss);
816 return mol.smobbed_copy ();
820 move the stem to right of the notehead if it is up.
822 MAKE_SCHEME_CALLBACK (Stem, offset_callback, 1);
824 Stem::offset_callback (SCM smob)
826 Grob *me = unsmob_grob (smob);
828 extract_grob_set (me, "rests", rests);
831 Grob *rest = rests.back ();
832 Real r = rest->extent (rest, X_AXIS).center ();
833 return scm_from_double (r);
836 if (Grob *f = first_head (me))
838 Interval head_wid = f->extent (f, X_AXIS);
841 if (is_invisible (me))
844 attach = Note_head::stem_attachment_coordinate (f, X_AXIS);
846 Direction d = get_grob_direction (me);
847 Real real_attach = head_wid.linear_combination (d * attach);
848 Real r = real_attach;
850 /* If not centered: correct for stem thickness. */
851 string style = robust_symbol2string (f->get_property ("style"), "default");
852 if (attach && style != "mensural"
853 && style != "neomensural"
854 && style != "petrucci")
856 Real rule_thick = thickness (me);
857 r += -d * rule_thick * 0.5;
859 return scm_from_double (r);
862 programming_error ("Weird stem.");
863 return scm_from_double (0.0);
867 Stem::get_beam (Grob *me)
869 SCM b = me->get_object ("beam");
870 return dynamic_cast<Spanner *> (unsmob_grob (b));
874 Stem::get_stem_info (Grob *me)
877 si.dir_ = get_grob_direction (me);
879 SCM scm_info = me->get_property ("stem-info");
880 si.ideal_y_ = scm_to_double (scm_car (scm_info));
881 si.shortest_y_ = scm_to_double (scm_cadr (scm_info));
885 MAKE_SCHEME_CALLBACK (Stem, calc_stem_info, 1);
887 Stem::calc_stem_info (SCM smob)
889 Grob *me = unsmob_grob (smob);
890 Direction my_dir = get_grob_direction (me);
894 programming_error ("no stem dir set");
898 Real staff_space = Staff_symbol_referencer::staff_space (me);
899 Grob *beam = get_beam (me);
903 (void) beam->get_property ("beaming");
906 Real beam_translation = Beam::get_beam_translation (beam);
907 Real beam_thickness = Beam::get_beam_thickness (beam);
908 int beam_count = Beam::get_direction_beam_count (beam, my_dir);
910 = robust_scm2double (me->get_property ("length-fraction"), 1.0);
912 /* Simple standard stem length */
913 SCM details = me->get_property ("details");
914 SCM lengths = ly_assoc_get (ly_symbol2scm ("beamed-lengths"), details, SCM_EOL);
917 = (scm_is_pair (lengths)
918 ? (scm_to_double (robust_list_ref (beam_count - 1, lengths))
922 stem only extends to center of beam
924 - 0.5 * beam_thickness)
927 /* Condition: sane minimum free stem length (chord to beams) */
928 lengths = ly_assoc_get (ly_symbol2scm ("beamed-minimum-free-lengths"),
931 Real ideal_minimum_free
932 = (scm_is_pair (lengths)
933 ? (scm_to_double (robust_list_ref (beam_count - 1, lengths))
938 Real height_of_my_trem = 0.0;
939 Grob *trem = unsmob_grob (me->get_object ("tremolo-flag"));
943 = Stem_tremolo::vertical_length (trem)
944 /* hack a bit of space around the trem. */
949 It seems that also for ideal minimum length, we must use
950 the maximum beam count (for this direction):
952 \score { \relative c'' { a8[ a32] } }
954 must be horizontal. */
955 Real height_of_my_beams = beam_thickness
956 + (beam_count - 1) * beam_translation;
958 Real ideal_minimum_length = ideal_minimum_free
961 /* stem only extends to center of beam */
962 - 0.5 * beam_thickness;
964 ideal_length = max (ideal_length, ideal_minimum_length);
966 /* Convert to Y position, calculate for dir == UP */
968 = /* staff positions */
969 head_positions (me)[my_dir] * 0.5
970 * my_dir * staff_space;
971 Real ideal_y = note_start + ideal_length;
973 /* Conditions for Y position */
975 /* Lowest beam of (UP) beam must never be lower than second staffline
979 Although this (additional) rule is probably correct,
980 I expect that highest beam (UP) should also never be lower
981 than middle staffline, just as normal stems.
985 Obviously not for grace beams.
987 Also, not for knees. Seems to be a good thing. */
988 bool no_extend = to_boolean (me->get_property ("no-stem-extend"));
989 bool is_knee = Beam::is_knee (beam);
990 if (!no_extend && !is_knee)
992 /* Highest beam of (UP) beam must never be lower than middle
994 ideal_y = max (ideal_y, 0.0);
995 /* Lowest beam of (UP) beam must never be lower than second staffline */
996 ideal_y = max (ideal_y, (-staff_space
997 - beam_thickness + height_of_my_beams));
1000 ideal_y -= robust_scm2double (beam->get_property ("shorten"), 0);
1002 SCM bemfl = ly_assoc_get (ly_symbol2scm ("beamed-extreme-minimum-free-lengths"),
1006 = (scm_is_pair (bemfl)
1007 ? (scm_to_double (robust_list_ref (beam_count - 1, bemfl))
1012 Real minimum_length = max (minimum_free, height_of_my_trem)
1013 + height_of_my_beams
1014 /* stem only extends to center of beam */
1015 - 0.5 * beam_thickness;
1018 Real minimum_y = note_start + minimum_length;
1019 Real shortest_y = minimum_y * my_dir;
1021 return scm_list_2 (scm_from_double (ideal_y),
1022 scm_from_double (shortest_y));
1026 Stem::beam_multiplicity (Grob *stem)
1028 SCM beaming = stem->get_property ("beaming");
1029 Slice le = int_list_to_slice (scm_car (beaming));
1030 Slice ri = int_list_to_slice (scm_cdr (beaming));
1036 Stem::is_cross_staff (Grob *stem)
1038 Grob *beam = unsmob_grob (stem->get_object ("beam"));
1039 return beam && Beam::is_cross_staff (beam);
1042 MAKE_SCHEME_CALLBACK (Stem, calc_cross_staff, 1)
1044 Stem::calc_cross_staff (SCM smob)
1046 return scm_from_bool (is_cross_staff (unsmob_grob (smob)));
1050 Stem::flag (Grob *me)
1052 return unsmob_grob (me->get_object ("flag"));
1055 /* FIXME: Too many properties */
1056 ADD_INTERFACE (Stem,
1057 "The stem represents the graphical stem. In addition, it"
1058 " internally connects note heads, beams, and tremolos. Rests"
1059 " and whole notes have invisible stems.\n"
1061 "The following properties may be set in the @code{details}"
1065 "@item beamed-lengths\n"
1066 "List of stem lengths given beam multiplicity.\n"
1067 "@item beamed-minimum-free-lengths\n"
1068 "List of normal minimum free stem lengths (chord to beams)"
1069 " given beam multiplicity.\n"
1070 "@item beamed-extreme-minimum-free-lengths\n"
1071 "List of extreme minimum free stem lengths (chord to beams)"
1072 " given beam multiplicity.\n"
1074 "Default stem lengths. The list gives a length for each"
1076 "@item stem-shorten\n"
1077 "How much a stem in a forced direction should be shortened."
1078 " The list gives an amount depending on the number of flags"
1086 "beamlet-default-length "
1087 "beamlet-max-length-proportion "
1088 "default-direction "
1098 "neutral-direction "
1103 "stem-begin-position "
1110 /****************************************************************/
1112 Stem_info::Stem_info ()
1114 ideal_y_ = shortest_y_ = 0;
1119 Stem_info::scale (Real x)