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));
394 /* Stems in unnatural (forced) direction should be shortened,
395 according to [Roush & Gourlay] */
396 Interval hp = head_positions (me);
397 if (dir && dir * hp[dir] >= 0)
399 SCM sshorten = ly_assoc_get (ly_symbol2scm ("stem-shorten"), details, SCM_EOL);
400 SCM scm_shorten = scm_is_pair (sshorten)
401 ? robust_list_ref (max (duration_log (me) - 2, 0), sshorten) : SCM_EOL;
402 Real shorten_property = 2 * robust_scm2double (scm_shorten, 0);
403 /* change in length between full-size and shortened stems is executed gradually.
404 "transition area" = stems between full-sized and fully-shortened.
406 Real quarter_stem_length = 2 * scm_to_double (robust_list_ref (0, s));
407 /* shortening_step = difference in length between consecutive stem lengths
408 in transition area. The bigger the difference between full-sized
409 and shortened stems, the bigger shortening_step is.
410 (but not greater than 1/2 and not smaller than 1/4).
411 value 6 is heuristic; it determines the suggested transition slope steepnesas.
413 Real shortening_step = min (max (0.25, (shorten_property / 6)), 0.5);
414 /* Shortening of unflagged stems should begin on the first stem that sticks
415 more than 1 staffspace (2 units) out of the staff.
416 Shortening of flagged stems begins in the same moment as unflagged ones,
417 but not earlier than on the middle line note.
419 Real which_step = (min (1.0, quarter_stem_length - (2 * staff_rad) - 2.0)) + abs (hp[dir]);
420 Real shorten = min (max (0.0, (shortening_step * which_step)), shorten_property);
425 length *= robust_scm2double (me->get_property ("length-fraction"), 1.0);
428 Grob *t_flag = unsmob_grob (me->get_object ("tremolo-flag"));
429 if (t_flag && (!unsmob_grob (me->get_object ("beam")) || !calc_beam))
431 /* Crude hack: add extra space if tremolo flag is there.
433 We can't do this for the beam, since we get into a loop
434 (Stem_tremolo::raw_stencil () looks at the beam.) --hwn */
437 + 2 * Stem_tremolo::vertical_length (t_flag) / ss;
439 /* We don't want to add the whole extent of the flag because the trem
440 and the flag can overlap partly. beam_translation gives a good
444 Real beam_trans = Stem_tremolo::get_beam_translation (t_flag);
445 /* the obvious choice is (durlog - 2) here, but we need a bit more space. */
446 minlen += 2 * (durlog - 1.5) * beam_trans;
448 /* up-stems need even a little more space to avoid collisions. This
449 needs to be in sync with the tremolo positioning code in
450 Stem_tremolo::print */
452 minlen += beam_trans;
454 length = max (length, minlen + 1.0);
457 Real stem_end = dir ? hp[dir] + dir * length : 0;
459 /* TODO: change name to extend-stems to staff/center/'() */
460 bool no_extend = to_boolean (me->get_property ("no-stem-extend"));
461 if (!no_extend && dir * stem_end < 0)
467 /* The log of the duration (Number of hooks on the flag minus two) */
469 Stem::duration_log (Grob *me)
471 SCM s = me->get_property ("duration-log");
472 return (scm_is_number (s)) ? scm_to_int (s) : 2;
475 MAKE_SCHEME_CALLBACK (Stem, calc_positioning_done, 1);
477 Stem::calc_positioning_done (SCM smob)
479 Grob *me = unsmob_grob (smob);
480 if (!head_count (me))
483 me->set_property ("positioning-done", SCM_BOOL_T);
485 extract_grob_set (me, "note-heads", ro_heads);
486 vector<Grob *> heads (ro_heads);
487 vector_sort (heads, position_less);
488 Direction dir = get_grob_direction (me);
493 Real thick = thickness (me);
495 Grob *hed = support_head (me);
498 programming_error ("Stem dir must be up or down.");
500 set_grob_direction (me, dir);
503 bool is_harmonic_centered = false;
504 for (vsize i = 0; i < heads.size (); i++)
505 is_harmonic_centered = is_harmonic_centered
506 || heads[i]->get_property ("style") == ly_symbol2scm ("harmonic");
507 is_harmonic_centered = is_harmonic_centered && is_invisible (me);
509 Real w = hed->extent (hed, X_AXIS)[dir];
510 for (vsize i = 0; i < heads.size (); i++)
512 Real amount = w - heads[i]->extent (heads[i], X_AXIS)[dir];
514 if (is_harmonic_centered)
516 = hed->extent (hed, X_AXIS).linear_combination (CENTER)
517 - heads[i]->extent (heads[i], X_AXIS).linear_combination (CENTER);
519 heads[i]->translate_axis (amount, X_AXIS);
522 Real lastpos = Real (Staff_symbol_referencer::get_position (heads[0]));
523 for (vsize i = 1; i < heads.size (); i++)
525 Real p = Staff_symbol_referencer::get_position (heads[i]);
526 Real dy = fabs (lastpos - p);
529 dy should always be 0.5, 0.0, 1.0, but provide safety margin
536 Real ell = heads[i]->extent (heads[i], X_AXIS).length ();
538 Direction d = get_grob_direction (me);
540 Reversed head should be shifted ell-thickness, but this
541 looks too crowded, so we only shift ell-0.5*thickness.
543 This leads to assymetry: Normal heads overlap the
544 stem 100% whereas reversed heads only overlaps the
548 Real reverse_overlap = 0.5;
549 heads[i]->translate_axis ((ell - thick * reverse_overlap) * d,
552 if (is_invisible (me))
553 heads[i]->translate_axis (-thick * (2 - reverse_overlap) * d,
558 For some cases we should kern some more: when the
559 distance between the next or prev note is too large, we'd
560 get large white gaps, eg.
581 MAKE_SCHEME_CALLBACK (Stem, calc_direction, 1);
583 Stem::calc_direction (SCM smob)
585 Grob *me = unsmob_grob (smob);
586 Direction dir = CENTER;
587 if (Grob *beam = unsmob_grob (me->get_object ("beam")))
589 SCM ignore_me = beam->get_property ("direction");
591 dir = get_grob_direction (me);
595 SCM dd = me->get_property ("default-direction");
598 return me->get_property ("neutral-direction");
601 return scm_from_int (dir);
604 MAKE_SCHEME_CALLBACK (Stem, calc_default_direction, 1);
606 Stem::calc_default_direction (SCM smob)
608 Grob *me = unsmob_grob (smob);
610 Direction dir = CENTER;
611 int staff_center = 0;
612 Interval hp = head_positions (me);
615 int udistance = (int) (UP * hp[UP] - staff_center);
616 int ddistance = (int) (DOWN * hp[DOWN] - staff_center);
618 dir = Direction (sign (ddistance - udistance));
621 return scm_from_int (dir);
624 // note - height property necessary to trigger quantized beam positions
625 // otherwise, we could just use Grob::stencil_height_proc
626 MAKE_SCHEME_CALLBACK (Stem, height, 1);
628 Stem::height (SCM smob)
630 Grob *me = unsmob_grob (smob);
631 return ly_interval2scm (internal_height (me, true));
635 Stem::get_reference_head (Grob *me)
637 return to_boolean (me->get_property ("avoid-note-head"))
643 Stem::beam_end_corrective (Grob *me)
645 Grob *beam = unsmob_grob (me->get_object ("beam"));
646 Direction dir = get_grob_direction (me);
651 programming_error ("no stem direction");
654 return dir * Beam::get_beam_thickness (beam) * 0.5;
660 Stem::internal_height (Grob *me, bool calc_beam)
662 Grob *beam = get_beam (me);
663 if (!is_valid_stem (me) && ! beam)
666 Direction dir = get_grob_direction (me);
668 if (beam && calc_beam)
670 /* trigger set-stem-lengths. */
671 (void) beam->get_property ("quantized-positions");
674 Real y1 = robust_scm2double ((calc_beam
675 ? me->get_property ("stem-begin-position")
676 : me->get_pure_property ("stem-begin-position", 0, INT_MAX)),
679 Real y2 = dir * robust_scm2double ((calc_beam
680 ? me->get_property ("length")
681 : me->get_pure_property ("length", 0, INT_MAX)),
685 Real half_space = Staff_symbol_referencer::staff_space (me) * 0.5;
687 Interval stem_y = Interval (min (y1, y2), max (y2, y1)) * half_space;
692 MAKE_SCHEME_CALLBACK (Stem, width, 1);
696 Grob *me = unsmob_grob (e);
700 if (is_invisible (me))
704 r = Interval (-1, 1);
705 r *= thickness (me) / 2;
708 return ly_interval2scm (r);
712 Stem::thickness (Grob *me)
714 return scm_to_double (me->get_property ("thickness"))
715 * Staff_symbol_referencer::line_thickness (me);
718 MAKE_SCHEME_CALLBACK (Stem, calc_stem_begin_position, 1);
720 Stem::calc_stem_begin_position (SCM smob)
722 Grob *me = unsmob_grob (smob);
723 return scm_from_double (internal_calc_stem_begin_position (me, true));
726 MAKE_SCHEME_CALLBACK (Stem, pure_calc_stem_begin_position, 3);
728 Stem::pure_calc_stem_begin_position (SCM smob,
732 Grob *me = unsmob_grob (smob);
733 return scm_from_double (internal_calc_stem_begin_position (me, false));
737 Stem::internal_calc_stem_begin_position (Grob *me, bool calc_beam)
739 Grob *beam = get_beam (me);
740 Real ss = Staff_symbol_referencer::staff_space (me);
741 if (beam && calc_beam)
743 (void) beam->get_property ("quantized-positions");
744 return robust_scm2double (me->get_property ("stem-begin-position"), 0.0);
747 Direction d = get_grob_direction (me);
748 Grob *lh = get_reference_head (me);
753 Real pos = Staff_symbol_referencer::get_position (lh);
755 if (Grob *head = support_head (me))
757 Interval head_height = head->extent (head, Y_AXIS);
758 Real y_attach = Note_head::stem_attachment_coordinate (head, Y_AXIS);
760 y_attach = head_height.linear_combination (y_attach);
761 pos += d * y_attach * 2 / ss;
768 Stem::is_valid_stem (Grob *me)
770 /* TODO: make the stem start a direction ?
771 This is required to avoid stems passing in tablature chords. */
772 Grob *lh = get_reference_head (me);
773 Grob *beam = unsmob_grob (me->get_object ("beam"));
778 if (lh && robust_scm2int (lh->get_property ("duration-log"), 0) < 1)
781 if (is_invisible (me))
787 MAKE_SCHEME_CALLBACK (Stem, print, 1);
789 Stem::print (SCM smob)
791 Grob *me = unsmob_grob (smob);
792 if (!is_valid_stem (me))
795 Direction dir = get_grob_direction (me);
796 Real y1 = robust_scm2double (me->get_property ("stem-begin-position"), 0.0);
797 Real y2 = dir * robust_scm2double (me->get_property ("length"), 0.0) + y1;
799 Real half_space = Staff_symbol_referencer::staff_space (me) * 0.5;
801 Interval stem_y = Interval (min (y1, y2), max (y2, y1)) * half_space;
803 stem_y[dir] -= beam_end_corrective (me);
806 Real stem_width = thickness (me);
808 = me->layout ()->get_dimension (ly_symbol2scm ("blot-diameter"));
810 Box b = Box (Interval (-stem_width / 2, stem_width / 2),
814 Stencil ss = Lookup::round_filled_box (b, blot);
815 mol.add_stencil (ss);
817 return mol.smobbed_copy ();
821 move the stem to right of the notehead if it is up.
823 MAKE_SCHEME_CALLBACK (Stem, offset_callback, 1);
825 Stem::offset_callback (SCM smob)
827 Grob *me = unsmob_grob (smob);
829 extract_grob_set (me, "rests", rests);
832 Grob *rest = rests.back ();
833 Real r = rest->extent (rest, X_AXIS).center ();
834 return scm_from_double (r);
837 if (Grob *f = first_head (me))
839 Interval head_wid = f->extent (f, X_AXIS);
842 if (is_invisible (me))
845 attach = Note_head::stem_attachment_coordinate (f, X_AXIS);
847 Direction d = get_grob_direction (me);
848 Real real_attach = head_wid.linear_combination (d * attach);
849 Real r = real_attach;
851 /* If not centered: correct for stem thickness. */
852 string style = robust_symbol2string (f->get_property ("style"), "default");
853 if (attach && style != "mensural"
854 && style != "neomensural"
855 && style != "petrucci")
857 Real rule_thick = thickness (me);
858 r += -d * rule_thick * 0.5;
860 return scm_from_double (r);
863 programming_error ("Weird stem.");
864 return scm_from_double (0.0);
868 Stem::get_beam (Grob *me)
870 SCM b = me->get_object ("beam");
871 return dynamic_cast<Spanner *> (unsmob_grob (b));
875 Stem::get_stem_info (Grob *me)
878 si.dir_ = get_grob_direction (me);
880 SCM scm_info = me->get_property ("stem-info");
881 si.ideal_y_ = scm_to_double (scm_car (scm_info));
882 si.shortest_y_ = scm_to_double (scm_cadr (scm_info));
886 MAKE_SCHEME_CALLBACK (Stem, calc_stem_info, 1);
888 Stem::calc_stem_info (SCM smob)
890 Grob *me = unsmob_grob (smob);
891 Direction my_dir = get_grob_direction (me);
895 programming_error ("no stem dir set");
899 Real staff_space = Staff_symbol_referencer::staff_space (me);
900 Grob *beam = get_beam (me);
904 (void) beam->get_property ("beaming");
907 Real beam_translation = Beam::get_beam_translation (beam);
908 Real beam_thickness = Beam::get_beam_thickness (beam);
909 int beam_count = Beam::get_direction_beam_count (beam, my_dir);
911 = robust_scm2double (me->get_property ("length-fraction"), 1.0);
913 /* Simple standard stem length */
914 SCM details = me->get_property ("details");
915 SCM lengths = ly_assoc_get (ly_symbol2scm ("beamed-lengths"), details, SCM_EOL);
918 = (scm_is_pair (lengths)
919 ? (scm_to_double (robust_list_ref (beam_count - 1, lengths))
923 stem only extends to center of beam
925 - 0.5 * beam_thickness)
928 /* Condition: sane minimum free stem length (chord to beams) */
929 lengths = ly_assoc_get (ly_symbol2scm ("beamed-minimum-free-lengths"),
932 Real ideal_minimum_free
933 = (scm_is_pair (lengths)
934 ? (scm_to_double (robust_list_ref (beam_count - 1, lengths))
939 Real height_of_my_trem = 0.0;
940 Grob *trem = unsmob_grob (me->get_object ("tremolo-flag"));
944 = Stem_tremolo::vertical_length (trem)
945 /* hack a bit of space around the trem. */
950 It seems that also for ideal minimum length, we must use
951 the maximum beam count (for this direction):
953 \score { \relative c'' { a8[ a32] } }
955 must be horizontal. */
956 Real height_of_my_beams = beam_thickness
957 + (beam_count - 1) * beam_translation;
959 Real ideal_minimum_length = ideal_minimum_free
962 /* stem only extends to center of beam */
963 - 0.5 * beam_thickness;
965 ideal_length = max (ideal_length, ideal_minimum_length);
967 /* Convert to Y position, calculate for dir == UP */
969 = /* staff positions */
970 head_positions (me)[my_dir] * 0.5
971 * my_dir * staff_space;
972 Real ideal_y = note_start + ideal_length;
974 /* Conditions for Y position */
976 /* Lowest beam of (UP) beam must never be lower than second staffline
980 Although this (additional) rule is probably correct,
981 I expect that highest beam (UP) should also never be lower
982 than middle staffline, just as normal stems.
986 Obviously not for grace beams.
988 Also, not for knees. Seems to be a good thing. */
989 bool no_extend = to_boolean (me->get_property ("no-stem-extend"));
990 bool is_knee = Beam::is_knee (beam);
991 if (!no_extend && !is_knee)
993 /* Highest beam of (UP) beam must never be lower than middle
995 ideal_y = max (ideal_y, 0.0);
996 /* Lowest beam of (UP) beam must never be lower than second staffline */
997 ideal_y = max (ideal_y, (-staff_space
998 - beam_thickness + height_of_my_beams));
1001 ideal_y -= robust_scm2double (beam->get_property ("shorten"), 0);
1003 SCM bemfl = ly_assoc_get (ly_symbol2scm ("beamed-extreme-minimum-free-lengths"),
1007 = (scm_is_pair (bemfl)
1008 ? (scm_to_double (robust_list_ref (beam_count - 1, bemfl))
1013 Real minimum_length = max (minimum_free, height_of_my_trem)
1014 + height_of_my_beams
1015 /* stem only extends to center of beam */
1016 - 0.5 * beam_thickness;
1019 Real minimum_y = note_start + minimum_length;
1020 Real shortest_y = minimum_y * my_dir;
1022 return scm_list_2 (scm_from_double (ideal_y),
1023 scm_from_double (shortest_y));
1027 Stem::beam_multiplicity (Grob *stem)
1029 SCM beaming = stem->get_property ("beaming");
1030 Slice le = int_list_to_slice (scm_car (beaming));
1031 Slice ri = int_list_to_slice (scm_cdr (beaming));
1037 Stem::is_cross_staff (Grob *stem)
1039 Grob *beam = unsmob_grob (stem->get_object ("beam"));
1040 return beam && Beam::is_cross_staff (beam);
1043 MAKE_SCHEME_CALLBACK (Stem, calc_cross_staff, 1)
1045 Stem::calc_cross_staff (SCM smob)
1047 return scm_from_bool (is_cross_staff (unsmob_grob (smob)));
1051 Stem::flag (Grob *me)
1053 return unsmob_grob (me->get_object ("flag"));
1056 /* FIXME: Too many properties */
1057 ADD_INTERFACE (Stem,
1058 "The stem represents the graphical stem. In addition, it"
1059 " internally connects note heads, beams, and tremolos. Rests"
1060 " and whole notes have invisible stems.\n"
1062 "The following properties may be set in the @code{details}"
1066 "@item beamed-lengths\n"
1067 "List of stem lengths given beam multiplicity.\n"
1068 "@item beamed-minimum-free-lengths\n"
1069 "List of normal minimum free stem lengths (chord to beams)"
1070 " given beam multiplicity.\n"
1071 "@item beamed-extreme-minimum-free-lengths\n"
1072 "List of extreme minimum free stem lengths (chord to beams)"
1073 " given beam multiplicity.\n"
1075 "Default stem lengths. The list gives a length for each"
1077 "@item stem-shorten\n"
1078 "How much a stem in a forced direction should be shortened."
1079 " The list gives an amount depending on the number of flags"
1087 "beamlet-default-length "
1088 "beamlet-max-length-proportion "
1089 "default-direction "
1099 "neutral-direction "
1104 "stem-begin-position "
1111 /****************************************************************/
1113 Stem_info::Stem_info ()
1115 ideal_y_ = shortest_y_ = 0;
1120 Stem_info::scale (Real x)