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
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"
68 Stem::set_beaming (Grob *me, int beam_count, Direction d)
70 SCM pair = me->get_property ("beaming");
72 if (!scm_is_pair (pair))
74 pair = scm_cons (SCM_EOL, SCM_EOL);
75 me->set_property ("beaming", pair);
78 SCM lst = index_get_cell (pair, d);
80 for (int i = 0; i < beam_count; i++)
81 lst = scm_cons (scm_from_int (i), lst);
85 index_set_cell (pair, d, lst);
89 Stem::get_beaming (Grob *me, Direction d)
91 SCM pair = me->get_property ("beaming");
92 if (!scm_is_pair (pair))
95 SCM lst = index_get_cell (pair, d);
97 int len = scm_ilength (lst);
102 Stem::head_positions (Grob *me)
106 Drul_array<Grob *> e (extremal_heads (me));
107 return Interval (Staff_symbol_referencer::get_position (e[DOWN]),
108 Staff_symbol_referencer::get_position (e[UP]));
114 Stem::chord_start_y (Grob *me)
117 return Staff_symbol_referencer::get_position (last_head (me))
118 * Staff_symbol_referencer::staff_space (me) * 0.5;
124 Stem::set_stem_positions (Grob *me, Real se)
127 Direction d = get_grob_direction (me);
129 Grob *beam = unsmob<Grob> (me->get_object ("beam"));
130 if (d && d * head_positions (me)[get_grob_direction (me)] >= se * d)
131 me->warning (_ ("weird stem size, check for narrow beams"));
133 // trigger note collision mechanisms
134 Real stem_beg = internal_calc_stem_begin_position (me, false);
135 Real staff_space = Staff_symbol_referencer::staff_space (me);
136 Real half_space = staff_space * 0.5;
139 height[-d] = stem_beg * half_space;
140 height[d] = se * half_space + beam_end_corrective (me);
142 Real stemlet_length = robust_scm2double (me->get_property ("stemlet-length"),
144 bool stemlet = stemlet_length > 0.0;
146 Grob *lh = get_reference_head (me);
152 Real beam_translation = Beam::get_beam_translation (beam);
153 Real beam_thickness = Beam::get_beam_thickness (beam);
154 int beam_count = beam_multiplicity (me).length () + 1;
156 height[-d] = (height[d] - d
157 * (0.5 * beam_thickness
158 + beam_translation * max (0, (beam_count - 1))
161 else if (!stemlet && beam)
162 height[-d] = height[d];
163 else if (stemlet && !beam)
164 me->programming_error ("Can't have a stemlet without a beam.");
167 me->set_property ("stem-begin-position", scm_from_double (height[-d] * 2 / staff_space));
168 me->set_property ("length", scm_from_double (height.length () * 2 / staff_space));
171 /* Note head that determines hshift for upstems
172 WARNING: triggers direction */
174 Stem::support_head (Grob *me)
176 extract_grob_set (me, "note-heads", heads);
177 if (heads.size () == 1)
180 return first_head (me);
184 Stem::head_count (Grob *me)
186 return Pointer_group_interface::count (me, ly_symbol2scm ("note-heads"));
189 /* The note head which forms one end of the stem.
190 WARNING: triggers direction */
192 Stem::first_head (Grob *me)
194 Direction d = get_grob_direction (me);
196 return extremal_heads (me)[-d];
200 /* The note head opposite to the first head. */
202 Stem::last_head (Grob *me)
204 Direction d = get_grob_direction (me);
206 return extremal_heads (me)[d];
211 START is part where stem reaches `last' head.
213 This function returns a drul with (bottom-head, top-head).
216 Stem::extremal_heads (Grob *me)
218 const int inf = INT_MAX;
219 Drul_array<int> extpos;
223 Drul_array<Grob *> exthead (0, 0);
224 extract_grob_set (me, "note-heads", heads);
226 for (vsize i = heads.size (); i--;)
229 int p = Staff_symbol_referencer::get_rounded_position (n);
231 for (LEFT_and_RIGHT (d))
233 if (d * p > d * extpos[d])
243 /* The staff positions, in ascending order.
244 * If FILTER, include the main column of noteheads only */
246 Stem::note_head_positions (Grob *me, bool filter)
249 extract_grob_set (me, "note-heads", heads);
250 Grob *xref = common_refpoint_of_array (heads, me, X_AXIS);
252 for (vsize i = heads.size (); i--;)
256 && n->relative_coordinate (xref, X_AXIS) != 0.0)
259 int p = Staff_symbol_referencer::get_rounded_position (n);
263 vector_sort (ps, less<int> ());
268 Stem::add_head (Grob *me, Grob *n)
270 n->set_object ("stem", me->self_scm ());
272 if (has_interface<Note_head> (n))
273 Pointer_group_interface::add_grob (me, ly_symbol2scm ("note-heads"), n);
274 else if (has_interface<Rest> (n))
275 Pointer_group_interface::add_grob (me, ly_symbol2scm ("rests"), n);
279 Stem::is_invisible (Grob *me)
281 if (is_normal_stem (me))
283 else if (head_count (me))
285 else // if there are no note-heads, we might want stemlets
286 return 0.0 == robust_scm2double (me->get_property ("stemlet-length"), 0.0);
290 Stem::is_normal_stem (Grob *me)
292 if (!head_count (me))
295 return scm_to_int (me->get_property ("duration-log")) >= 1;
298 MAKE_SCHEME_CALLBACK (Stem, pure_height, 3)
300 Stem::pure_height (SCM smob,
304 Grob *me = unsmob<Grob> (smob);
305 return ly_interval2scm (internal_pure_height (me, true));
309 Stem::internal_pure_height (Grob *me, bool calc_beam)
311 if (!is_normal_stem (me))
312 return Interval (0.0, 0.0);
314 Grob *beam = unsmob<Grob> (me->get_object ("beam"));
316 Interval iv = internal_height (me, false);
323 Direction dir = get_grob_direction (me);
324 for (DOWN_and_UP (d))
325 overshoot[d] = d == dir ? dir * infinity_f : iv[d];
327 vector<Interval> heights;
328 vector<Grob *> my_stems;
329 extract_grob_set (beam, "normal-stems", normal_stems);
330 for (vsize i = 0; i < normal_stems.size (); i++)
331 if (get_grob_direction (normal_stems[i]) == dir)
333 if (normal_stems[i] != me)
334 heights.push_back (Stem::internal_pure_height (normal_stems[i], false));
336 heights.push_back (iv);
337 my_stems.push_back (normal_stems[i]);
339 //iv.unite (heights.back ());
340 // look for cross staff effects
342 Grob *common = common_refpoint_of_array (my_stems, me, Y_AXIS);
343 Real min_pos = infinity_f;
344 Real max_pos = -infinity_f;
345 for (vsize i = 0; i < my_stems.size (); i++)
347 coords.push_back (my_stems[i]->pure_relative_y_coordinate (common, 0, INT_MAX));
348 min_pos = min (min_pos, coords[i]);
349 max_pos = max (max_pos, coords[i]);
351 for (vsize i = 0; i < heights.size (); i++)
353 heights[i][dir] += dir == DOWN
354 ? coords[i] - max_pos
355 : coords[i] - min_pos;
358 for (vsize i = 0; i < heights.size (); i++) iv.unite (heights[i]);
360 for (vsize i = 0; i < my_stems.size (); i++)
361 cache_pure_height (my_stems[i], iv, heights[i]);
362 iv.intersect (overshoot);
369 Stem::cache_pure_height (Grob *me, Interval iv, Interval my_iv)
372 Direction dir = get_grob_direction (me);
373 for (DOWN_and_UP (d))
374 overshoot[d] = d == dir ? dir * infinity_f : my_iv[d];
376 iv.intersect (overshoot);
377 dynamic_cast<Item *> (me)->cache_pure_height (iv);
380 MAKE_SCHEME_CALLBACK (Stem, calc_stem_end_position, 1)
382 Stem::calc_stem_end_position (SCM smob)
384 Grob *me = unsmob<Grob> (smob);
385 return scm_from_double (internal_calc_stem_end_position (me, true));
388 MAKE_SCHEME_CALLBACK (Stem, pure_calc_stem_end_position, 3)
390 Stem::pure_calc_stem_end_position (SCM smob,
394 Grob *me = unsmob<Grob> (smob);
395 return scm_from_double (internal_calc_stem_end_position (me, false));
399 Stem::internal_calc_stem_end_position (Grob *me, bool calc_beam)
401 if (!head_count (me))
404 Grob *beam = get_beam (me);
405 Real ss = Staff_symbol_referencer::staff_space (me);
406 Direction dir = get_grob_direction (me);
408 if (beam && calc_beam)
410 (void) beam->get_property ("quantized-positions");
411 return robust_scm2double (me->get_property ("length"), 0.0)
412 + dir * robust_scm2double (me->get_property ("stem-begin-position"), 0.0);
417 /* WARNING: IN HALF SPACES */
418 SCM details = me->get_property ("details");
419 int durlog = duration_log (me);
421 Real staff_rad = Staff_symbol_referencer::staff_radius (me);
423 SCM s = ly_assoc_get (ly_symbol2scm ("lengths"), details, SCM_EOL);
425 length = 2 * scm_to_double (robust_list_ref (durlog - 2, s));
427 /* Stems in unnatural (forced) direction should be shortened,
428 according to [Roush & Gourlay] */
429 Interval hp = head_positions (me);
430 if (dir && dir * hp[dir] >= 0)
432 SCM sshorten = ly_assoc_get (ly_symbol2scm ("stem-shorten"), details, SCM_EOL);
433 SCM scm_shorten = scm_is_pair (sshorten)
434 ? robust_list_ref (max (duration_log (me) - 2, 0), sshorten) : SCM_EOL;
435 Real shorten_property = 2 * robust_scm2double (scm_shorten, 0);
436 /* change in length between full-size and shortened stems is executed gradually.
437 "transition area" = stems between full-sized and fully-shortened.
439 Real quarter_stem_length = 2 * scm_to_double (robust_list_ref (0, s));
440 /* shortening_step = difference in length between consecutive stem lengths
441 in transition area. The bigger the difference between full-sized
442 and shortened stems, the bigger shortening_step is.
443 (but not greater than 1/2 and not smaller than 1/4).
444 value 6 is heuristic; it determines the suggested transition slope steepnesas.
446 Real shortening_step = min (max (0.25, (shorten_property / 6)), 0.5);
447 /* Shortening of unflagged stems should begin on the first stem that sticks
448 more than 1 staffspace (2 units) out of the staff.
449 Shortening of flagged stems begins in the same moment as unflagged ones,
450 but not earlier than on the middle line note.
452 Real which_step = (min (1.0, quarter_stem_length - (2 * staff_rad) - 2.0)) + abs (hp[dir]);
453 Real shorten = min (max (0.0, (shortening_step * which_step)), shorten_property);
458 length *= robust_scm2double (me->get_property ("length-fraction"), 1.0);
461 Grob *t_flag = unsmob<Grob> (me->get_object ("tremolo-flag"));
462 if (t_flag && (!unsmob<Grob> (me->get_object ("beam")) || !calc_beam))
464 /* Crude hack: add extra space if tremolo flag is there.
466 We can't do this for the beam, since we get into a loop
467 (Stem_tremolo::raw_stencil () looks at the beam.) --hwn */
470 + 2 * Stem_tremolo::vertical_length (t_flag) / ss;
472 /* We don't want to add the whole extent of the flag because the trem
473 and the flag can overlap partly. beam_translation gives a good
477 Real beam_trans = Stem_tremolo::get_beam_translation (t_flag);
478 /* the obvious choice is (durlog - 2) here, but we need a bit more space. */
479 minlen += 2 * (durlog - 1.5) * beam_trans;
481 /* up-stems need even a little more space to avoid collisions. This
482 needs to be in sync with the tremolo positioning code in
483 Stem_tremolo::print */
485 minlen += beam_trans;
487 length = max (length, minlen + 1.0);
490 Real stem_end = dir ? hp[dir] + dir * length : 0;
492 /* TODO: change name to extend-stems to staff/center/'() */
493 bool no_extend = to_boolean (me->get_property ("no-stem-extend"));
494 if (!no_extend && dir * stem_end < 0)
500 /* The log of the duration (Number of hooks on the flag minus two) */
502 Stem::duration_log (Grob *me)
504 SCM s = me->get_property ("duration-log");
505 return (scm_is_number (s)) ? scm_to_int (s) : 2;
508 MAKE_SCHEME_CALLBACK (Stem, calc_positioning_done, 1);
510 Stem::calc_positioning_done (SCM smob)
512 Grob *me = unsmob<Grob> (smob);
513 if (!head_count (me))
516 me->set_property ("positioning-done", SCM_BOOL_T);
518 extract_grob_set (me, "note-heads", ro_heads);
519 vector<Grob *> heads (ro_heads);
520 vector_sort (heads, position_less);
521 Direction dir = get_grob_direction (me);
526 Real thick = thickness (me);
528 Grob *hed = support_head (me);
531 programming_error ("Stem dir must be up or down.");
533 set_grob_direction (me, dir);
536 bool is_harmonic_centered = false;
537 for (vsize i = 0; i < heads.size (); i++)
538 is_harmonic_centered = is_harmonic_centered
539 || scm_is_eq (heads[i]->get_property ("style"),
540 ly_symbol2scm ("harmonic"));
541 is_harmonic_centered = is_harmonic_centered && is_invisible (me);
543 Real w = hed->extent (hed, X_AXIS)[dir];
544 for (vsize i = 0; i < heads.size (); i++)
546 Real amount = w - heads[i]->extent (heads[i], X_AXIS)[dir];
548 if (is_harmonic_centered)
550 = hed->extent (hed, X_AXIS).linear_combination (CENTER)
551 - heads[i]->extent (heads[i], X_AXIS).linear_combination (CENTER);
553 if (!isnan (amount)) // empty heads can produce NaN
554 heads[i]->translate_axis (amount, X_AXIS);
557 Real lastpos = Real (Staff_symbol_referencer::get_position (heads[0]));
558 for (vsize i = 1; i < heads.size (); i++)
560 Real p = Staff_symbol_referencer::get_position (heads[i]);
561 Real dy = fabs (lastpos - p);
564 dy should always be 0.5, 0.0, 1.0, but provide safety margin
571 Real ell = heads[i]->extent (heads[i], X_AXIS).length ();
573 Direction d = get_grob_direction (me);
575 Reversed head should be shifted ell-thickness, but this
576 looks too crowded, so we only shift ell-0.5*thickness.
578 This leads to assymetry: Normal heads overlap the
579 stem 100% whereas reversed heads only overlaps the
582 Real reverse_overlap = 0.5;
585 However, the first reverse head has to be shifted even
586 more than the full reverse overlap if it is the same
587 height as the first head or there will be a gap
588 because of the head slant (issue 346).
591 if (i == 1 && dy < 0.1)
592 reverse_overlap = 1.1;
594 if (is_invisible (me))
596 // Semibreves and longer are tucked in considerably
597 // to be recognizable as chorded rather than
598 // parallel voices. During the course of issue 346
599 // there was a discussion to change this for unisons
600 // (dy < 0.1) to reduce overlap but without reaching
601 // agreement and with Gould being rather on the
602 // overlapping front.
606 heads[i]->translate_axis ((ell - thick * reverse_overlap) * d,
611 For some cases we should kern some more: when the
612 distance between the next or prev note is too large, we'd
613 get large white gaps, eg.
634 MAKE_SCHEME_CALLBACK (Stem, calc_direction, 1);
636 Stem::calc_direction (SCM smob)
638 Grob *me = unsmob<Grob> (smob);
639 Direction dir = CENTER;
640 if (Grob *beam = unsmob<Grob> (me->get_object ("beam")))
642 SCM ignore_me = beam->get_property ("direction");
644 dir = get_grob_direction (me);
648 SCM dd = me->get_property ("default-direction");
651 return me->get_property ("neutral-direction");
654 return scm_from_int (dir);
657 MAKE_SCHEME_CALLBACK (Stem, calc_default_direction, 1);
659 Stem::calc_default_direction (SCM smob)
661 Grob *me = unsmob<Grob> (smob);
663 Direction dir = CENTER;
664 int staff_center = 0;
667 Interval hp = head_positions (me);
668 int udistance = (int) (UP * hp[UP] - staff_center);
669 int ddistance = (int) (DOWN * hp[DOWN] - staff_center);
671 dir = Direction (sign (ddistance - udistance));
674 return scm_from_int (dir);
677 // note - height property necessary to trigger quantized beam positions
678 // otherwise, we could just use Grob::stencil_height_proc
679 MAKE_SCHEME_CALLBACK (Stem, height, 1);
681 Stem::height (SCM smob)
683 Grob *me = unsmob<Grob> (smob);
684 return ly_interval2scm (internal_height (me, true));
688 Stem::get_reference_head (Grob *me)
690 return to_boolean (me->get_property ("avoid-note-head"))
696 Stem::beam_end_corrective (Grob *me)
698 Grob *beam = unsmob<Grob> (me->get_object ("beam"));
699 Direction dir = get_grob_direction (me);
704 programming_error ("no stem direction");
707 return dir * Beam::get_beam_thickness (beam) * 0.5;
713 Stem::internal_height (Grob *me, bool calc_beam)
715 Grob *beam = get_beam (me);
716 if (!is_valid_stem (me) && !beam)
719 Direction dir = get_grob_direction (me);
721 if (beam && calc_beam)
723 /* trigger set-stem-lengths. */
724 (void) beam->get_property ("quantized-positions");
728 If there is a beam but no stem, slope calculations depend on this
729 routine to return where the stem end /would/ be.
731 if (calc_beam && !beam && !unsmob<Stencil> (me->get_property ("stencil")))
734 Real y1 = robust_scm2double ((calc_beam
735 ? me->get_property ("stem-begin-position")
736 : me->get_pure_property ("stem-begin-position", 0, INT_MAX)),
739 Real y2 = dir * robust_scm2double ((calc_beam
740 ? me->get_property ("length")
741 : me->get_pure_property ("length", 0, INT_MAX)),
745 Real half_space = Staff_symbol_referencer::staff_space (me) * 0.5;
747 Interval stem_y = Interval (min (y1, y2), max (y2, y1)) * half_space;
752 MAKE_SCHEME_CALLBACK (Stem, width, 1);
756 Grob *me = unsmob<Grob> (e);
760 if (is_invisible (me))
764 r = Interval (-1, 1);
765 r *= thickness (me) / 2;
768 return ly_interval2scm (r);
772 Stem::thickness (Grob *me)
774 return scm_to_double (me->get_property ("thickness"))
775 * Staff_symbol_referencer::line_thickness (me);
778 MAKE_SCHEME_CALLBACK (Stem, calc_stem_begin_position, 1);
780 Stem::calc_stem_begin_position (SCM smob)
782 Grob *me = unsmob<Grob> (smob);
783 return scm_from_double (internal_calc_stem_begin_position (me, true));
786 MAKE_SCHEME_CALLBACK (Stem, pure_calc_stem_begin_position, 3);
788 Stem::pure_calc_stem_begin_position (SCM smob,
792 Grob *me = unsmob<Grob> (smob);
793 return scm_from_double (internal_calc_stem_begin_position (me, false));
797 Stem::internal_calc_stem_begin_position (Grob *me, bool calc_beam)
799 Grob *beam = get_beam (me);
800 Real ss = Staff_symbol_referencer::staff_space (me);
801 if (beam && calc_beam)
803 (void) beam->get_property ("quantized-positions");
804 return robust_scm2double (me->get_property ("stem-begin-position"), 0.0);
807 Direction d = get_grob_direction (me);
808 Grob *lh = get_reference_head (me);
813 Real pos = Staff_symbol_referencer::get_position (lh);
815 if (Grob *head = support_head (me))
817 Interval head_height = head->extent (head, Y_AXIS);
818 Real y_attach = Note_head::stem_attachment_coordinate (head, Y_AXIS);
820 y_attach = head_height.linear_combination (y_attach);
821 if (!isinf (y_attach) && !isnan (y_attach)) // empty heads
822 pos += d * y_attach * 2 / ss;
829 MAKE_SCHEME_CALLBACK (Stem, pure_calc_length, 3);
831 Stem::pure_calc_length (SCM smob, SCM /*start*/, SCM /*end*/)
833 Grob *me = unsmob<Grob> (smob);
834 Real beg = robust_scm2double (me->get_pure_property ("stem-begin-position", 0, INT_MAX), 0.0);
835 Real res = fabs (internal_calc_stem_end_position (me, false) - beg);
836 return scm_from_double (res);
839 MAKE_SCHEME_CALLBACK (Stem, calc_length, 1);
841 Stem::calc_length (SCM smob)
843 Grob *me = unsmob<Grob> (smob);
844 if (unsmob<Grob> (me->get_object ("beam")))
846 me->programming_error ("ly:stem::calc-length called but will not be used for beamed stem.");
847 return scm_from_double (0.0);
850 Real beg = robust_scm2double (me->get_property ("stem-begin-position"), 0.0);
851 Real res = fabs (internal_calc_stem_end_position (me, true) - beg);
852 return scm_from_double (res);
856 Stem::is_valid_stem (Grob *me)
858 /* TODO: make the stem start a direction ?
859 This is required to avoid stems passing in tablature chords. */
862 Grob *lh = get_reference_head (me);
863 Grob *beam = unsmob<Grob> (me->get_object ("beam"));
868 if (is_invisible (me))
874 MAKE_SCHEME_CALLBACK (Stem, print, 1);
876 Stem::print (SCM smob)
878 Grob *me = unsmob<Grob> (smob);
879 if (!is_valid_stem (me))
882 Direction dir = get_grob_direction (me);
883 Real y1 = robust_scm2double (me->get_property ("stem-begin-position"), 0.0);
884 Real y2 = dir * robust_scm2double (me->get_property ("length"), 0.0) + y1;
886 Real half_space = Staff_symbol_referencer::staff_space (me) * 0.5;
888 Interval stem_y = Interval (min (y1, y2), max (y2, y1)) * half_space;
890 stem_y[dir] -= beam_end_corrective (me);
893 Real stem_width = thickness (me);
895 = me->layout ()->get_dimension (ly_symbol2scm ("blot-diameter"));
897 Box b = Box (Interval (-stem_width / 2, stem_width / 2),
901 Stencil ss = Lookup::round_filled_box (b, blot);
902 mol.add_stencil (ss);
904 return mol.smobbed_copy ();
908 move the stem to right of the notehead if it is up.
910 MAKE_SCHEME_CALLBACK (Stem, offset_callback, 1);
912 Stem::offset_callback (SCM smob)
914 Grob *me = unsmob<Grob> (smob);
916 extract_grob_set (me, "rests", rests);
919 Grob *rest = rests.back ();
920 Real r = robust_relative_extent (rest, rest, X_AXIS).center ();
921 return scm_from_double (r);
924 if (Grob *f = first_head (me))
926 Interval head_wid = f->extent (f, X_AXIS);
929 if (is_invisible (me))
932 attach = Note_head::stem_attachment_coordinate (f, X_AXIS);
934 Direction d = get_grob_direction (me);
935 Real real_attach = head_wid.linear_combination (d * attach);
936 Real r = isnan(real_attach)? 0.0: real_attach;
938 /* If not centered: correct for stem thickness. */
939 string style = robust_symbol2string (f->get_property ("style"), "default");
940 if (attach && style != "mensural"
941 && style != "neomensural"
942 && style != "petrucci")
944 Real rule_thick = thickness (me);
945 r += -d * rule_thick * 0.5;
947 return scm_from_double (r);
950 programming_error ("Weird stem.");
951 return scm_from_double (0.0);
955 Stem::get_beam (Grob *me)
957 SCM b = me->get_object ("beam");
958 return unsmob<Spanner> (b);
962 Stem::get_stem_info (Grob *me)
965 si.dir_ = get_grob_direction (me);
967 SCM scm_info = me->get_property ("stem-info");
968 si.ideal_y_ = scm_to_double (scm_car (scm_info));
969 si.shortest_y_ = scm_to_double (scm_cadr (scm_info));
973 MAKE_SCHEME_CALLBACK (Stem, calc_stem_info, 1);
975 Stem::calc_stem_info (SCM smob)
977 Grob *me = unsmob<Grob> (smob);
978 Direction my_dir = get_grob_direction (me);
982 programming_error ("no stem dir set");
986 Real staff_space = Staff_symbol_referencer::staff_space (me);
987 Grob *beam = get_beam (me);
991 (void) beam->get_property ("beaming");
994 Real beam_translation = Beam::get_beam_translation (beam);
995 Real beam_thickness = Beam::get_beam_thickness (beam);
996 int beam_count = Beam::get_direction_beam_count (beam, my_dir);
998 = robust_scm2double (me->get_property ("length-fraction"), 1.0);
1000 /* Simple standard stem length */
1001 SCM details = me->get_property ("details");
1002 SCM lengths = ly_assoc_get (ly_symbol2scm ("beamed-lengths"), details, SCM_EOL);
1005 = (scm_is_pair (lengths)
1006 ? (scm_to_double (robust_list_ref (beam_count - 1, lengths))
1010 stem only extends to center of beam
1012 - 0.5 * beam_thickness)
1015 /* Condition: sane minimum free stem length (chord to beams) */
1016 lengths = ly_assoc_get (ly_symbol2scm ("beamed-minimum-free-lengths"),
1019 Real ideal_minimum_free
1020 = (scm_is_pair (lengths)
1021 ? (scm_to_double (robust_list_ref (beam_count - 1, lengths))
1026 Real height_of_my_trem = 0.0;
1027 Grob *trem = unsmob<Grob> (me->get_object ("tremolo-flag"));
1031 = Stem_tremolo::vertical_length (trem)
1032 /* hack a bit of space around the trem. */
1037 It seems that also for ideal minimum length, we must use
1038 the maximum beam count (for this direction):
1040 \score { \relative c'' { a8[ a32] } }
1042 must be horizontal. */
1043 Real height_of_my_beams = beam_thickness
1044 + (beam_count - 1) * beam_translation;
1046 Real ideal_minimum_length = ideal_minimum_free
1047 + height_of_my_beams
1049 /* stem only extends to center of beam */
1050 - 0.5 * beam_thickness;
1052 ideal_length = max (ideal_length, ideal_minimum_length);
1054 /* Convert to Y position, calculate for dir == UP */
1056 = /* staff positions */
1057 head_positions (me)[my_dir] * 0.5
1058 * my_dir * staff_space;
1059 Real ideal_y = note_start + ideal_length;
1061 /* Conditions for Y position */
1063 /* Lowest beam of (UP) beam must never be lower than second staffline
1067 Although this (additional) rule is probably correct,
1068 I expect that highest beam (UP) should also never be lower
1069 than middle staffline, just as normal stems.
1073 Obviously not for grace beams.
1075 Also, not for knees. Seems to be a good thing. */
1076 bool no_extend = to_boolean (me->get_property ("no-stem-extend"));
1077 bool is_knee = Beam::is_knee (beam);
1078 if (!no_extend && !is_knee)
1080 /* Highest beam of (UP) beam must never be lower than middle
1082 ideal_y = max (ideal_y, 0.0);
1083 /* Lowest beam of (UP) beam must never be lower than second staffline */
1084 ideal_y = max (ideal_y, (-staff_space
1085 - beam_thickness + height_of_my_beams));
1088 ideal_y -= robust_scm2double (beam->get_property ("shorten"), 0);
1090 SCM bemfl = ly_assoc_get (ly_symbol2scm ("beamed-extreme-minimum-free-lengths"),
1094 = (scm_is_pair (bemfl)
1095 ? (scm_to_double (robust_list_ref (beam_count - 1, bemfl))
1100 Real minimum_length = max (minimum_free, height_of_my_trem)
1101 + height_of_my_beams
1102 /* stem only extends to center of beam */
1103 - 0.5 * beam_thickness;
1106 Real minimum_y = note_start + minimum_length;
1107 Real shortest_y = minimum_y * my_dir;
1109 return scm_list_2 (scm_from_double (ideal_y),
1110 scm_from_double (shortest_y));
1114 Stem::beam_multiplicity (Grob *stem)
1116 SCM beaming = stem->get_property ("beaming");
1117 Slice le = int_list_to_slice (scm_car (beaming));
1118 Slice ri = int_list_to_slice (scm_cdr (beaming));
1124 Stem::is_cross_staff (Grob *stem)
1126 Grob *beam = unsmob<Grob> (stem->get_object ("beam"));
1127 return beam && Beam::is_cross_staff (beam);
1130 MAKE_SCHEME_CALLBACK (Stem, calc_cross_staff, 1)
1132 Stem::calc_cross_staff (SCM smob)
1134 return scm_from_bool (is_cross_staff (unsmob<Grob> (smob)));
1138 Stem::flag (Grob *me)
1140 return unsmob<Grob> (me->get_object ("flag"));
1143 /* FIXME: Too many properties */
1144 ADD_INTERFACE (Stem,
1145 "The stem represents the graphical stem. In addition, it"
1146 " internally connects note heads, beams, and tremolos. Rests"
1147 " and whole notes have invisible stems.\n"
1149 "The following properties may be set in the @code{details}"
1153 "@item beamed-lengths\n"
1154 "List of stem lengths given beam multiplicity.\n"
1155 "@item beamed-minimum-free-lengths\n"
1156 "List of normal minimum free stem lengths (chord to beams)"
1157 " given beam multiplicity.\n"
1158 "@item beamed-extreme-minimum-free-lengths\n"
1159 "List of extreme minimum free stem lengths (chord to beams)"
1160 " given beam multiplicity.\n"
1162 "Default stem lengths. The list gives a length for each"
1164 "@item stem-shorten\n"
1165 "How much a stem in a forced direction should be shortened."
1166 " The list gives an amount depending on the number of flags"
1174 "beamlet-default-length "
1175 "beamlet-max-length-proportion "
1176 "default-direction "
1179 "double-stem-separation "
1187 "neutral-direction "
1192 "stem-begin-position "
1200 /****************************************************************/
1202 Stem_info::Stem_info ()
1204 ideal_y_ = shortest_y_ = 0;
1209 Stem_info::scale (Real x)