2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 1996--2015 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
46 #include "directional-element-interface.hh"
47 #include "dot-column.hh"
48 #include "font-interface.hh"
49 #include "international.hh"
52 #include "note-head.hh"
53 #include "output-def.hh"
54 #include "paper-column.hh"
55 #include "pointer-group-interface.hh"
57 #include "rhythmic-head.hh"
58 #include "side-position-interface.hh"
59 #include "staff-symbol-referencer.hh"
60 #include "stem-tremolo.hh"
67 Stem::set_beaming (Grob *me, int beam_count, Direction d)
69 SCM pair = me->get_property ("beaming");
71 if (!scm_is_pair (pair))
73 pair = scm_cons (SCM_EOL, SCM_EOL);
74 me->set_property ("beaming", pair);
77 SCM lst = index_get_cell (pair, d);
79 for (int i = 0; i < beam_count; i++)
80 lst = scm_cons (scm_from_int (i), lst);
84 index_set_cell (pair, d, lst);
88 Stem::get_beaming (Grob *me, Direction d)
90 SCM pair = me->get_property ("beaming");
91 if (!scm_is_pair (pair))
94 SCM lst = index_get_cell (pair, d);
96 int len = scm_ilength (lst);
97 return std::max (len, 0);
101 Stem::head_positions (Grob *me)
105 Drul_array<Grob *> e (extremal_heads (me));
106 return Interval (Staff_symbol_referencer::get_position (e[DOWN]),
107 Staff_symbol_referencer::get_position (e[UP]));
113 Stem::chord_start_y (Grob *me)
116 return Staff_symbol_referencer::get_position (last_head (me))
117 * Staff_symbol_referencer::staff_space (me) * 0.5;
123 Stem::set_stem_positions (Grob *me, Real se)
126 Direction d = get_grob_direction (me);
128 Grob *beam = unsmob<Grob> (me->get_object ("beam"));
129 if (d && d * head_positions (me)[get_grob_direction (me)] >= se * d)
130 me->warning (_ ("weird stem size, check for narrow beams"));
132 // trigger note collision mechanisms
133 Real stem_beg = internal_calc_stem_begin_position (me, false);
134 Real staff_space = Staff_symbol_referencer::staff_space (me);
135 Real half_space = staff_space * 0.5;
138 height[-d] = stem_beg * half_space;
139 height[d] = se * half_space + beam_end_corrective (me);
141 Real stemlet_length = robust_scm2double (me->get_property ("stemlet-length"),
143 bool stemlet = stemlet_length > 0.0;
145 Grob *lh = get_reference_head (me);
151 Real beam_translation = Beam::get_beam_translation (beam);
152 Real beam_thickness = Beam::get_beam_thickness (beam);
153 int beam_count = beam_multiplicity (me).length () + 1;
155 height[-d] = (height[d] - d
156 * (0.5 * beam_thickness
157 + beam_translation * std::max (0, (beam_count - 1))
160 else if (!stemlet && beam)
161 height[-d] = height[d];
162 else if (stemlet && !beam)
163 me->programming_error ("Can't have a stemlet without a beam.");
166 me->set_property ("stem-begin-position", scm_from_double (height[-d] * 2 / staff_space));
167 me->set_property ("length", scm_from_double (height.length () * 2 / staff_space));
170 /* Note head that determines hshift for upstems
171 WARNING: triggers direction */
173 Stem::support_head (Grob *me)
175 extract_grob_set (me, "note-heads", heads);
176 if (heads.size () == 1)
179 return first_head (me);
183 Stem::head_count (Grob *me)
185 return Pointer_group_interface::count (me, ly_symbol2scm ("note-heads"));
188 /* The note head which forms one end of the stem.
189 WARNING: triggers direction */
191 Stem::first_head (Grob *me)
193 Direction d = get_grob_direction (me);
195 return extremal_heads (me)[-d];
199 /* The note head opposite to the first head. */
201 Stem::last_head (Grob *me)
203 Direction d = get_grob_direction (me);
205 return extremal_heads (me)[d];
210 START is part where stem reaches `last' head.
212 This function returns a drul with (bottom-head, top-head).
215 Stem::extremal_heads (Grob *me)
217 const int inf = INT_MAX;
218 Drul_array<int> extpos;
222 Drul_array<Grob *> exthead (0, 0);
223 extract_grob_set (me, "note-heads", heads);
225 for (vsize i = heads.size (); i--;)
228 int p = Staff_symbol_referencer::get_rounded_position (n);
230 for (LEFT_and_RIGHT (d))
232 if (d * p > d * extpos[d])
242 /* The staff positions, in ascending order.
243 * If FILTER, include the main column of noteheads only */
245 Stem::note_head_positions (Grob *me, bool filter)
248 extract_grob_set (me, "note-heads", heads);
249 Grob *xref = common_refpoint_of_array (heads, me, X_AXIS);
251 for (vsize i = heads.size (); i--;)
255 && n->relative_coordinate (xref, X_AXIS) != 0.0)
258 int p = Staff_symbol_referencer::get_rounded_position (n);
262 vector_sort (ps, std::less<int> ());
267 Stem::add_head (Grob *me, Grob *n)
269 n->set_object ("stem", me->self_scm ());
271 if (has_interface<Note_head> (n))
272 Pointer_group_interface::add_grob (me, ly_symbol2scm ("note-heads"), n);
273 else if (has_interface<Rest> (n))
274 Pointer_group_interface::add_grob (me, ly_symbol2scm ("rests"), n);
278 Stem::is_invisible (Grob *me)
280 if (is_normal_stem (me))
282 else if (head_count (me))
284 else // if there are no note-heads, we might want stemlets
285 return 0.0 == robust_scm2double (me->get_property ("stemlet-length"), 0.0);
289 Stem::is_normal_stem (Grob *me)
291 if (!head_count (me))
294 return scm_to_int (me->get_property ("duration-log")) >= 1;
297 MAKE_SCHEME_CALLBACK (Stem, pure_height, 3)
299 Stem::pure_height (SCM smob,
303 Grob *me = unsmob<Grob> (smob);
304 return ly_interval2scm (internal_pure_height (me, true));
308 Stem::internal_pure_height (Grob *me, bool calc_beam)
310 if (!is_normal_stem (me))
311 return Interval (0.0, 0.0);
313 Grob *beam = unsmob<Grob> (me->get_object ("beam"));
315 Interval iv = internal_height (me, false);
322 Direction dir = get_grob_direction (me);
323 for (DOWN_and_UP (d))
324 overshoot[d] = d == dir ? dir * infinity_f : iv[d];
326 vector<Interval> heights;
327 vector<Grob *> my_stems;
328 extract_grob_set (beam, "normal-stems", normal_stems);
329 for (vsize i = 0; i < normal_stems.size (); i++)
330 if (get_grob_direction (normal_stems[i]) == dir)
332 if (normal_stems[i] != me)
333 heights.push_back (Stem::internal_pure_height (normal_stems[i], false));
335 heights.push_back (iv);
336 my_stems.push_back (normal_stems[i]);
338 //iv.unite (heights.back ());
339 // look for cross staff effects
341 Grob *common = common_refpoint_of_array (my_stems, me, Y_AXIS);
342 Real min_pos = infinity_f;
343 Real max_pos = -infinity_f;
344 for (vsize i = 0; i < my_stems.size (); i++)
346 coords.push_back (my_stems[i]->pure_relative_y_coordinate (common, 0, INT_MAX));
347 min_pos = std::min (min_pos, coords[i]);
348 max_pos = std::max (max_pos, coords[i]);
350 for (vsize i = 0; i < heights.size (); i++)
352 heights[i][dir] += dir == DOWN
353 ? coords[i] - max_pos
354 : coords[i] - min_pos;
357 for (vsize i = 0; i < heights.size (); i++) iv.unite (heights[i]);
359 for (vsize i = 0; i < my_stems.size (); i++)
360 cache_pure_height (my_stems[i], iv, heights[i]);
361 iv.intersect (overshoot);
368 Stem::cache_pure_height (Grob *me, Interval iv, Interval my_iv)
371 Direction dir = get_grob_direction (me);
372 for (DOWN_and_UP (d))
373 overshoot[d] = d == dir ? dir * infinity_f : my_iv[d];
375 iv.intersect (overshoot);
376 dynamic_cast<Item *> (me)->cache_pure_height (iv);
379 MAKE_SCHEME_CALLBACK (Stem, calc_stem_end_position, 1)
381 Stem::calc_stem_end_position (SCM smob)
383 Grob *me = unsmob<Grob> (smob);
384 return scm_from_double (internal_calc_stem_end_position (me, true));
387 MAKE_SCHEME_CALLBACK (Stem, pure_calc_stem_end_position, 3)
389 Stem::pure_calc_stem_end_position (SCM smob,
393 Grob *me = unsmob<Grob> (smob);
394 return scm_from_double (internal_calc_stem_end_position (me, false));
398 Stem::internal_calc_stem_end_position (Grob *me, bool calc_beam)
400 if (!head_count (me))
403 Grob *beam = get_beam (me);
404 Real ss = Staff_symbol_referencer::staff_space (me);
405 Direction dir = get_grob_direction (me);
407 if (beam && calc_beam)
409 (void) beam->get_property ("quantized-positions");
410 return robust_scm2double (me->get_property ("length"), 0.0)
411 + dir * robust_scm2double (me->get_property ("stem-begin-position"), 0.0);
416 /* WARNING: IN HALF SPACES */
417 SCM details = me->get_property ("details");
418 int durlog = duration_log (me);
420 Real staff_rad = Staff_symbol_referencer::staff_radius (me);
422 SCM s = ly_assoc_get (ly_symbol2scm ("lengths"), details, SCM_EOL);
424 length = 2 * scm_to_double (robust_list_ref (durlog - 2, s));
426 /* Stems in unnatural (forced) direction should be shortened,
427 according to [Roush & Gourlay] */
428 Interval hp = head_positions (me);
429 if (dir && dir * hp[dir] >= 0)
431 SCM sshorten = ly_assoc_get (ly_symbol2scm ("stem-shorten"), details, SCM_EOL);
432 SCM scm_shorten = scm_is_pair (sshorten)
433 ? robust_list_ref (std::max (duration_log (me) - 2, 0), sshorten) : SCM_EOL;
434 Real shorten_property = 2 * robust_scm2double (scm_shorten, 0);
435 /* change in length between full-size and shortened stems is executed gradually.
436 "transition area" = stems between full-sized and fully-shortened.
438 Real quarter_stem_length = 2 * scm_to_double (robust_list_ref (0, s));
439 /* shortening_step = difference in length between consecutive stem lengths
440 in transition area. The bigger the difference between full-sized
441 and shortened stems, the bigger shortening_step is.
442 (but not greater than 1/2 and not smaller than 1/4).
443 value 6 is heuristic; it determines the suggested transition slope steepnesas.
445 Real shortening_step = std::min (std::max (0.25, (shorten_property / 6)), 0.5);
446 /* Shortening of unflagged stems should begin on the first stem that sticks
447 more than 1 staffspace (2 units) out of the staff.
448 Shortening of flagged stems begins in the same moment as unflagged ones,
449 but not earlier than on the middle line note.
451 Real which_step = (std::min (1.0, quarter_stem_length - (2 * staff_rad) - 2.0)) + abs (hp[dir]);
452 Real shorten = std::min (std::max (0.0, (shortening_step * which_step)), shorten_property);
457 length *= robust_scm2double (me->get_property ("length-fraction"), 1.0);
460 Grob *t_flag = unsmob<Grob> (me->get_object ("tremolo-flag"));
461 if (t_flag && (!unsmob<Grob> (me->get_object ("beam")) || !calc_beam))
463 /* Crude hack: add extra space if tremolo flag is there.
465 We can't do this for the beam, since we get into a loop
466 (Stem_tremolo::raw_stencil () looks at the beam.) --hwn */
469 + 2 * Stem_tremolo::vertical_length (t_flag) / ss;
471 /* We don't want to add the whole extent of the flag because the trem
472 and the flag can overlap partly. beam_translation gives a good
476 Real beam_trans = Stem_tremolo::get_beam_translation (t_flag);
477 /* the obvious choice is (durlog - 2) here, but we need a bit more space. */
478 minlen += 2 * (durlog - 1.5) * beam_trans;
480 /* up-stems need even a little more space to avoid collisions. This
481 needs to be in sync with the tremolo positioning code in
482 Stem_tremolo::print */
484 minlen += beam_trans;
486 length = std::max (length, minlen + 1.0);
489 Real stem_end = dir ? hp[dir] + dir * length : 0;
491 /* TODO: change name to extend-stems to staff/center/'() */
492 bool no_extend = to_boolean (me->get_property ("no-stem-extend"));
493 if (!no_extend && dir * stem_end < 0)
499 /* The log of the duration (Number of hooks on the flag minus two) */
501 Stem::duration_log (Grob *me)
503 SCM s = me->get_property ("duration-log");
504 return (scm_is_number (s)) ? scm_to_int (s) : 2;
507 MAKE_SCHEME_CALLBACK (Stem, calc_positioning_done, 1);
509 Stem::calc_positioning_done (SCM smob)
511 Grob *me = unsmob<Grob> (smob);
512 if (!head_count (me))
515 me->set_property ("positioning-done", SCM_BOOL_T);
517 extract_grob_set (me, "note-heads", ro_heads);
518 vector<Grob *> heads (ro_heads);
519 vector_sort (heads, position_less);
520 Direction dir = get_grob_direction (me);
525 Real thick = thickness (me);
527 Grob *hed = support_head (me);
530 programming_error ("Stem dir must be up or down.");
532 set_grob_direction (me, dir);
535 bool is_harmonic_centered = false;
536 for (vsize i = 0; i < heads.size (); i++)
537 is_harmonic_centered = is_harmonic_centered
538 || scm_is_eq (heads[i]->get_property ("style"),
539 ly_symbol2scm ("harmonic"));
540 is_harmonic_centered = is_harmonic_centered && is_invisible (me);
542 Real w = hed->extent (hed, X_AXIS)[dir];
543 for (vsize i = 0; i < heads.size (); i++)
545 Real amount = w - heads[i]->extent (heads[i], X_AXIS)[dir];
547 if (is_harmonic_centered)
549 = hed->extent (hed, X_AXIS).linear_combination (CENTER)
550 - heads[i]->extent (heads[i], X_AXIS).linear_combination (CENTER);
552 if (!isnan (amount)) // empty heads can produce NaN
553 heads[i]->translate_axis (amount, X_AXIS);
556 Real lastpos = Real (Staff_symbol_referencer::get_position (heads[0]));
557 for (vsize i = 1; i < heads.size (); i++)
559 Real p = Staff_symbol_referencer::get_position (heads[i]);
560 Real dy = fabs (lastpos - p);
563 dy should always be 0.5, 0.0, 1.0, but provide safety margin
570 Real ell = heads[i]->extent (heads[i], X_AXIS).length ();
572 Direction d = get_grob_direction (me);
574 Reversed head should be shifted ell-thickness, but this
575 looks too crowded, so we only shift ell-0.5*thickness.
577 This leads to assymetry: Normal heads overlap the
578 stem 100% whereas reversed heads only overlaps the
581 Real reverse_overlap = 0.5;
584 However, the first reverse head has to be shifted even
585 more than the full reverse overlap if it is the same
586 height as the first head or there will be a gap
587 because of the head slant (issue 346).
590 if (i == 1 && dy < 0.1)
591 reverse_overlap = 1.1;
593 if (is_invisible (me))
595 // Semibreves and longer are tucked in considerably
596 // to be recognizable as chorded rather than
597 // parallel voices. During the course of issue 346
598 // there was a discussion to change this for unisons
599 // (dy < 0.1) to reduce overlap but without reaching
600 // agreement and with Gould being rather on the
601 // overlapping front.
605 heads[i]->translate_axis ((ell - thick * reverse_overlap) * d,
610 For some cases we should kern some more: when the
611 distance between the next or prev note is too large, we'd
612 get large white gaps, eg.
633 MAKE_SCHEME_CALLBACK (Stem, calc_direction, 1);
635 Stem::calc_direction (SCM smob)
637 Grob *me = unsmob<Grob> (smob);
638 Direction dir = CENTER;
639 if (Grob *beam = unsmob<Grob> (me->get_object ("beam")))
641 SCM ignore_me = beam->get_property ("direction");
643 dir = get_grob_direction (me);
647 SCM dd = me->get_property ("default-direction");
650 return me->get_property ("neutral-direction");
653 return scm_from_int (dir);
656 MAKE_SCHEME_CALLBACK (Stem, calc_default_direction, 1);
658 Stem::calc_default_direction (SCM smob)
660 Grob *me = unsmob<Grob> (smob);
662 Direction dir = CENTER;
663 int staff_center = 0;
666 Interval hp = head_positions (me);
667 int udistance = (int) (UP * hp[UP] - staff_center);
668 int ddistance = (int) (DOWN * hp[DOWN] - staff_center);
670 dir = Direction (sign (ddistance - udistance));
673 return scm_from_int (dir);
676 // note - height property necessary to trigger quantized beam positions
677 // otherwise, we could just use Grob::stencil_height_proc
678 MAKE_SCHEME_CALLBACK (Stem, height, 1);
680 Stem::height (SCM smob)
682 Grob *me = unsmob<Grob> (smob);
683 return ly_interval2scm (internal_height (me, true));
687 Stem::get_reference_head (Grob *me)
689 return to_boolean (me->get_property ("avoid-note-head"))
695 Stem::beam_end_corrective (Grob *me)
697 Grob *beam = unsmob<Grob> (me->get_object ("beam"));
698 Direction dir = get_grob_direction (me);
703 programming_error ("no stem direction");
706 return dir * Beam::get_beam_thickness (beam) * 0.5;
712 Stem::internal_height (Grob *me, bool calc_beam)
714 Grob *beam = get_beam (me);
715 if (!is_valid_stem (me) && !beam)
718 Direction dir = get_grob_direction (me);
720 if (beam && calc_beam)
722 /* trigger set-stem-lengths. */
723 (void) beam->get_property ("quantized-positions");
727 If there is a beam but no stem, slope calculations depend on this
728 routine to return where the stem end /would/ be.
730 if (calc_beam && !beam && !unsmob<Stencil> (me->get_property ("stencil")))
733 Real y1 = robust_scm2double ((calc_beam
734 ? me->get_property ("stem-begin-position")
735 : me->get_pure_property ("stem-begin-position", 0, INT_MAX)),
738 Real y2 = dir * robust_scm2double ((calc_beam
739 ? me->get_property ("length")
740 : me->get_pure_property ("length", 0, INT_MAX)),
744 Real half_space = Staff_symbol_referencer::staff_space (me) * 0.5;
746 Interval stem_y = Interval (std::min (y1, y2), std::max (y2, y1)) * half_space;
751 MAKE_SCHEME_CALLBACK (Stem, width, 1);
755 Grob *me = unsmob<Grob> (e);
759 if (is_invisible (me))
763 r = Interval (-1, 1);
764 r *= thickness (me) / 2;
767 return ly_interval2scm (r);
771 Stem::thickness (Grob *me)
773 return scm_to_double (me->get_property ("thickness"))
774 * Staff_symbol_referencer::line_thickness (me);
777 MAKE_SCHEME_CALLBACK (Stem, calc_stem_begin_position, 1);
779 Stem::calc_stem_begin_position (SCM smob)
781 Grob *me = unsmob<Grob> (smob);
782 return scm_from_double (internal_calc_stem_begin_position (me, true));
785 MAKE_SCHEME_CALLBACK (Stem, pure_calc_stem_begin_position, 3);
787 Stem::pure_calc_stem_begin_position (SCM smob,
791 Grob *me = unsmob<Grob> (smob);
792 return scm_from_double (internal_calc_stem_begin_position (me, false));
796 Stem::internal_calc_stem_begin_position (Grob *me, bool calc_beam)
798 Grob *beam = get_beam (me);
799 Real ss = Staff_symbol_referencer::staff_space (me);
800 if (beam && calc_beam)
802 (void) beam->get_property ("quantized-positions");
803 return robust_scm2double (me->get_property ("stem-begin-position"), 0.0);
806 Direction d = get_grob_direction (me);
807 Grob *lh = get_reference_head (me);
812 Real pos = Staff_symbol_referencer::get_position (lh);
814 if (Grob *head = support_head (me))
816 Interval head_height = head->extent (head, Y_AXIS);
817 Real y_attach = Note_head::stem_attachment_coordinate (head, Y_AXIS);
819 y_attach = head_height.linear_combination (y_attach);
820 if (!isinf (y_attach) && !isnan (y_attach)) // empty heads
821 pos += d * y_attach * 2 / ss;
828 MAKE_SCHEME_CALLBACK (Stem, pure_calc_length, 3);
830 Stem::pure_calc_length (SCM smob, SCM /*start*/, SCM /*end*/)
832 Grob *me = unsmob<Grob> (smob);
833 Real beg = robust_scm2double (me->get_pure_property ("stem-begin-position", 0, INT_MAX), 0.0);
834 Real res = fabs (internal_calc_stem_end_position (me, false) - beg);
835 return scm_from_double (res);
838 MAKE_SCHEME_CALLBACK (Stem, calc_length, 1);
840 Stem::calc_length (SCM smob)
842 Grob *me = unsmob<Grob> (smob);
843 if (unsmob<Grob> (me->get_object ("beam")))
845 me->programming_error ("ly:stem::calc-length called but will not be used for beamed stem.");
846 return scm_from_double (0.0);
849 Real beg = robust_scm2double (me->get_property ("stem-begin-position"), 0.0);
850 Real res = fabs (internal_calc_stem_end_position (me, true) - beg);
851 return scm_from_double (res);
855 Stem::is_valid_stem (Grob *me)
857 /* TODO: make the stem start a direction ?
858 This is required to avoid stems passing in tablature chords. */
861 Grob *lh = get_reference_head (me);
862 Grob *beam = unsmob<Grob> (me->get_object ("beam"));
867 if (is_invisible (me))
873 MAKE_SCHEME_CALLBACK (Stem, print, 1);
875 Stem::print (SCM smob)
877 Grob *me = unsmob<Grob> (smob);
878 if (!is_valid_stem (me))
881 Direction dir = get_grob_direction (me);
882 Real y1 = robust_scm2double (me->get_property ("stem-begin-position"), 0.0);
883 Real y2 = dir * robust_scm2double (me->get_property ("length"), 0.0) + y1;
885 Real half_space = Staff_symbol_referencer::staff_space (me) * 0.5;
887 Interval stem_y = Interval (std::min (y1, y2), std::max (y2, y1)) * half_space;
889 stem_y[dir] -= beam_end_corrective (me);
892 Real stem_width = thickness (me);
894 = me->layout ()->get_dimension (ly_symbol2scm ("blot-diameter"));
896 Box b = Box (Interval (-stem_width / 2, stem_width / 2),
900 Stencil ss = Lookup::round_filled_box (b, blot);
901 mol.add_stencil (ss);
903 return mol.smobbed_copy ();
907 move the stem to right of the notehead if it is up.
909 MAKE_SCHEME_CALLBACK (Stem, offset_callback, 1);
911 Stem::offset_callback (SCM smob)
913 Grob *me = unsmob<Grob> (smob);
915 extract_grob_set (me, "rests", rests);
918 Grob *rest = rests.back ();
919 Real r = robust_relative_extent (rest, rest, X_AXIS).center ();
920 return scm_from_double (r);
923 if (Grob *f = first_head (me))
925 Interval head_wid = f->extent (f, X_AXIS);
928 if (is_invisible (me))
931 attach = Note_head::stem_attachment_coordinate (f, X_AXIS);
933 Direction d = get_grob_direction (me);
934 Real real_attach = head_wid.linear_combination (d * attach);
935 Real r = isnan(real_attach)? 0.0: real_attach;
937 /* If not centered: correct for stem thickness. */
938 string style = robust_symbol2string (f->get_property ("style"), "default");
939 if (attach && style != "mensural"
940 && style != "neomensural"
941 && style != "petrucci")
943 Real rule_thick = thickness (me);
944 r += -d * rule_thick * 0.5;
946 return scm_from_double (r);
949 programming_error ("Weird stem.");
950 return scm_from_double (0.0);
954 Stem::get_beam (Grob *me)
956 SCM b = me->get_object ("beam");
957 return unsmob<Spanner> (b);
961 Stem::get_stem_info (Grob *me)
964 si.dir_ = get_grob_direction (me);
966 SCM scm_info = me->get_property ("stem-info");
967 si.ideal_y_ = scm_to_double (scm_car (scm_info));
968 si.shortest_y_ = scm_to_double (scm_cadr (scm_info));
972 MAKE_SCHEME_CALLBACK (Stem, calc_stem_info, 1);
974 Stem::calc_stem_info (SCM smob)
976 Grob *me = unsmob<Grob> (smob);
977 Direction my_dir = get_grob_direction (me);
981 programming_error ("no stem dir set");
985 Real staff_space = Staff_symbol_referencer::staff_space (me);
986 Grob *beam = get_beam (me);
990 (void) beam->get_property ("beaming");
993 Real beam_translation = Beam::get_beam_translation (beam);
994 Real beam_thickness = Beam::get_beam_thickness (beam);
995 int beam_count = Beam::get_direction_beam_count (beam, my_dir);
997 = robust_scm2double (me->get_property ("length-fraction"), 1.0);
999 /* Simple standard stem length */
1000 SCM details = me->get_property ("details");
1001 SCM lengths = ly_assoc_get (ly_symbol2scm ("beamed-lengths"), details, SCM_EOL);
1004 = (scm_is_pair (lengths)
1005 ? (scm_to_double (robust_list_ref (beam_count - 1, lengths))
1009 stem only extends to center of beam
1011 - 0.5 * beam_thickness)
1014 /* Condition: sane minimum free stem length (chord to beams) */
1015 lengths = ly_assoc_get (ly_symbol2scm ("beamed-minimum-free-lengths"),
1018 Real ideal_minimum_free
1019 = (scm_is_pair (lengths)
1020 ? (scm_to_double (robust_list_ref (beam_count - 1, lengths))
1025 Real height_of_my_trem = 0.0;
1026 Grob *trem = unsmob<Grob> (me->get_object ("tremolo-flag"));
1030 = Stem_tremolo::vertical_length (trem)
1031 /* hack a bit of space around the trem. */
1036 It seems that also for ideal minimum length, we must use
1037 the maximum beam count (for this direction):
1039 \score { \relative c'' { a8[ a32] } }
1041 must be horizontal. */
1042 Real height_of_my_beams = beam_thickness
1043 + (beam_count - 1) * beam_translation;
1045 Real ideal_minimum_length = ideal_minimum_free
1046 + height_of_my_beams
1048 /* stem only extends to center of beam */
1049 - 0.5 * beam_thickness;
1051 ideal_length = std::max (ideal_length, ideal_minimum_length);
1053 /* Convert to Y position, calculate for dir == UP */
1055 = /* staff positions */
1056 head_positions (me)[my_dir] * 0.5
1057 * my_dir * staff_space;
1058 Real ideal_y = note_start + ideal_length;
1060 /* Conditions for Y position */
1062 /* Lowest beam of (UP) beam must never be lower than second staffline
1066 Although this (additional) rule is probably correct,
1067 I expect that highest beam (UP) should also never be lower
1068 than middle staffline, just as normal stems.
1072 Obviously not for grace beams.
1074 Also, not for knees. Seems to be a good thing. */
1075 bool no_extend = to_boolean (me->get_property ("no-stem-extend"));
1076 bool is_knee = Beam::is_knee (beam);
1077 if (!no_extend && !is_knee)
1079 /* Highest beam of (UP) beam must never be lower than middle
1081 ideal_y = std::max (ideal_y, 0.0);
1082 /* Lowest beam of (UP) beam must never be lower than second staffline */
1083 ideal_y = std::max (ideal_y, (-staff_space
1084 - beam_thickness + height_of_my_beams));
1087 ideal_y -= robust_scm2double (beam->get_property ("shorten"), 0);
1089 SCM bemfl = ly_assoc_get (ly_symbol2scm ("beamed-extreme-minimum-free-lengths"),
1093 = (scm_is_pair (bemfl)
1094 ? (scm_to_double (robust_list_ref (beam_count - 1, bemfl))
1099 Real minimum_length = std::max (minimum_free, height_of_my_trem)
1100 + height_of_my_beams
1101 /* stem only extends to center of beam */
1102 - 0.5 * beam_thickness;
1105 Real minimum_y = note_start + minimum_length;
1106 Real shortest_y = minimum_y * my_dir;
1108 return scm_list_2 (scm_from_double (ideal_y),
1109 scm_from_double (shortest_y));
1113 Stem::beam_multiplicity (Grob *stem)
1115 SCM beaming = stem->get_property ("beaming");
1116 Slice le = int_list_to_slice (scm_car (beaming));
1117 Slice ri = int_list_to_slice (scm_cdr (beaming));
1123 Stem::is_cross_staff (Grob *stem)
1125 Grob *beam = unsmob<Grob> (stem->get_object ("beam"));
1126 return beam && Beam::is_cross_staff (beam);
1129 MAKE_SCHEME_CALLBACK (Stem, calc_cross_staff, 1)
1131 Stem::calc_cross_staff (SCM smob)
1133 return scm_from_bool (is_cross_staff (unsmob<Grob> (smob)));
1137 Stem::flag (Grob *me)
1139 return unsmob<Grob> (me->get_object ("flag"));
1142 /* FIXME: Too many properties */
1143 ADD_INTERFACE (Stem,
1144 "The stem represents the graphical stem. In addition, it"
1145 " internally connects note heads, beams, and tremolos. Rests"
1146 " and whole notes have invisible stems.\n"
1148 "The following properties may be set in the @code{details}"
1152 "@item beamed-lengths\n"
1153 "List of stem lengths given beam multiplicity.\n"
1154 "@item beamed-minimum-free-lengths\n"
1155 "List of normal minimum free stem lengths (chord to beams)"
1156 " given beam multiplicity.\n"
1157 "@item beamed-extreme-minimum-free-lengths\n"
1158 "List of extreme minimum free stem lengths (chord to beams)"
1159 " given beam multiplicity.\n"
1161 "Default stem lengths. The list gives a length for each"
1163 "@item stem-shorten\n"
1164 "How much a stem in a forced direction should be shortened."
1165 " The list gives an amount depending on the number of flags"
1173 "beamlet-default-length "
1174 "beamlet-max-length-proportion "
1175 "default-direction "
1178 "double-stem-separation "
1186 "neutral-direction "
1191 "stem-begin-position "
1199 /****************************************************************/
1201 Stem_info::Stem_info ()
1203 ideal_y_ = shortest_y_ = 0;
1208 Stem_info::scale (Real x)