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"
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); // -1 for dotted lists!
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)
114 return Staff_symbol_referencer::get_position (last_head (me))
115 * Staff_symbol_referencer::staff_space (me) * 0.5;
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 // trigger note collision mechanisms
131 Real stem_beg = internal_calc_stem_begin_position (me, false);
132 Real staff_space = Staff_symbol_referencer::staff_space (me);
133 Real half_space = staff_space * 0.5;
136 height[-d] = stem_beg * half_space;
137 height[d] = se * half_space + beam_end_corrective (me);
139 Real stemlet_length = robust_scm2double (me->get_property ("stemlet-length"),
141 bool stemlet = stemlet_length > 0.0;
143 Grob *lh = get_reference_head (me);
149 Real beam_translation = Beam::get_beam_translation (beam);
150 Real beam_thickness = Beam::get_beam_thickness (beam);
151 int beam_count = beam_multiplicity (me).length () + 1;
153 height[-d] = (height[d] - d
154 * (0.5 * beam_thickness
155 + beam_translation * max (0, (beam_count - 1))
158 else if (!stemlet && beam)
159 height[-d] = height[d];
160 else if (stemlet && !beam)
161 me->programming_error ("Can't have a stemlet without a beam.");
164 me->set_property ("stem-begin-position", scm_from_double (height[-d] * 2 / staff_space));
165 me->set_property ("length", scm_from_double (height.length () * 2 / staff_space));
168 /* Note head that determines hshift for upstems
169 WARNING: triggers direction */
171 Stem::support_head (Grob *me)
173 extract_grob_set (me, "note-heads", heads);
174 if (heads.size () == 1)
177 return first_head (me);
181 Stem::head_count (Grob *me)
183 return Pointer_group_interface::count (me, ly_symbol2scm ("note-heads"));
186 /* The note head which forms one end of the stem.
187 WARNING: triggers direction */
189 Stem::first_head (Grob *me)
191 Direction d = get_grob_direction (me);
193 return extremal_heads (me)[-d];
197 /* The note head opposite to the first head. */
199 Stem::last_head (Grob *me)
201 Direction d = get_grob_direction (me);
203 return extremal_heads (me)[d];
208 START is part where stem reaches `last' head.
210 This function returns a drul with (bottom-head, top-head).
213 Stem::extremal_heads (Grob *me)
215 const int inf = INT_MAX;
216 Drul_array<int> extpos;
220 Drul_array<Grob *> exthead (0, 0);
221 extract_grob_set (me, "note-heads", heads);
223 for (vsize i = heads.size (); i--;)
226 int p = Staff_symbol_referencer::get_rounded_position (n);
228 for (LEFT_and_RIGHT (d))
230 if (d * p > d * extpos[d])
240 /* The staff positions, in ascending order.
241 * If FILTER, include the main column of noteheads only */
243 Stem::note_head_positions (Grob *me, bool filter)
246 extract_grob_set (me, "note-heads", heads);
247 Grob *xref = common_refpoint_of_array (heads, me, X_AXIS);
249 for (vsize i = heads.size (); i--;)
253 && n->relative_coordinate (xref, X_AXIS) != 0.0)
256 int p = Staff_symbol_referencer::get_rounded_position (n);
260 vector_sort (ps, less<int> ());
265 Stem::add_head (Grob *me, Grob *n)
267 n->set_object ("stem", me->self_scm ());
269 if (has_interface<Note_head> (n))
270 Pointer_group_interface::add_grob (me, ly_symbol2scm ("note-heads"), n);
271 else if (has_interface<Rest> (n))
272 Pointer_group_interface::add_grob (me, ly_symbol2scm ("rests"), n);
276 Stem::is_invisible (Grob *me)
278 if (is_normal_stem (me))
280 else if (head_count (me))
282 else // if there are no note-heads, we might want stemlets
283 return 0.0 == robust_scm2double (me->get_property ("stemlet-length"), 0.0);
287 Stem::is_normal_stem (Grob *me)
289 if (!head_count (me))
292 return scm_to_int (me->get_property ("duration-log")) >= 1;
295 MAKE_SCHEME_CALLBACK (Stem, pure_height, 3)
297 Stem::pure_height (SCM smob,
301 Grob *me = unsmob<Grob> (smob);
302 return ly_interval2scm (internal_pure_height (me, true));
306 Stem::internal_pure_height (Grob *me, bool calc_beam)
308 if (!is_normal_stem (me))
309 return Interval (0.0, 0.0);
311 Grob *beam = unsmob<Grob> (me->get_object ("beam"));
313 Interval iv = internal_height (me, false);
320 Direction dir = get_grob_direction (me);
321 for (DOWN_and_UP (d))
322 overshoot[d] = d == dir ? dir * infinity_f : iv[d];
324 vector<Interval> heights;
325 vector<Grob *> my_stems;
326 extract_grob_set (beam, "normal-stems", normal_stems);
327 for (vsize i = 0; i < normal_stems.size (); i++)
328 if (get_grob_direction (normal_stems[i]) == dir)
330 if (normal_stems[i] != me)
331 heights.push_back (Stem::internal_pure_height (normal_stems[i], false));
333 heights.push_back (iv);
334 my_stems.push_back (normal_stems[i]);
336 //iv.unite (heights.back ());
337 // look for cross staff effects
339 Grob *common = common_refpoint_of_array (my_stems, me, Y_AXIS);
340 Real min_pos = infinity_f;
341 Real max_pos = -infinity_f;
342 for (vsize i = 0; i < my_stems.size (); i++)
344 coords.push_back (my_stems[i]->pure_relative_y_coordinate (common, 0, INT_MAX));
345 min_pos = min (min_pos, coords[i]);
346 max_pos = max (max_pos, coords[i]);
348 for (vsize i = 0; i < heights.size (); i++)
350 heights[i][dir] += dir == DOWN
351 ? coords[i] - max_pos
352 : coords[i] - min_pos;
355 for (vsize i = 0; i < heights.size (); i++) iv.unite (heights[i]);
357 for (vsize i = 0; i < my_stems.size (); i++)
358 cache_pure_height (my_stems[i], iv, heights[i]);
359 iv.intersect (overshoot);
366 Stem::cache_pure_height (Grob *me, Interval iv, Interval my_iv)
369 Direction dir = get_grob_direction (me);
370 for (DOWN_and_UP (d))
371 overshoot[d] = d == dir ? dir * infinity_f : my_iv[d];
373 iv.intersect (overshoot);
374 dynamic_cast<Item *> (me)->cache_pure_height (iv);
377 MAKE_SCHEME_CALLBACK (Stem, calc_stem_end_position, 1)
379 Stem::calc_stem_end_position (SCM smob)
381 Grob *me = unsmob<Grob> (smob);
382 return scm_from_double (internal_calc_stem_end_position (me, true));
385 MAKE_SCHEME_CALLBACK (Stem, pure_calc_stem_end_position, 3)
387 Stem::pure_calc_stem_end_position (SCM smob,
391 Grob *me = unsmob<Grob> (smob);
392 return scm_from_double (internal_calc_stem_end_position (me, false));
396 Stem::internal_calc_stem_end_position (Grob *me, bool calc_beam)
398 if (!head_count (me))
401 Grob *beam = get_beam (me);
402 Real ss = Staff_symbol_referencer::staff_space (me);
403 Direction dir = get_grob_direction (me);
405 if (beam && calc_beam)
407 (void) beam->get_property ("quantized-positions");
408 return robust_scm2double (me->get_property ("length"), 0.0)
409 + dir * robust_scm2double (me->get_property ("stem-begin-position"), 0.0);
414 /* WARNING: IN HALF SPACES */
415 SCM details = me->get_property ("details");
416 int durlog = duration_log (me);
418 Real staff_rad = Staff_symbol_referencer::staff_radius (me);
420 SCM s = ly_assoc_get (ly_symbol2scm ("lengths"), details, SCM_EOL);
422 length = 2 * scm_to_double (robust_list_ref (durlog - 2, s));
424 /* Stems in unnatural (forced) direction should be shortened,
425 according to [Roush & Gourlay] */
426 Interval hp = head_positions (me);
427 if (dir && dir * hp[dir] >= 0)
429 SCM sshorten = ly_assoc_get (ly_symbol2scm ("stem-shorten"), details, SCM_EOL);
430 SCM scm_shorten = scm_is_pair (sshorten)
431 ? robust_list_ref (max (duration_log (me) - 2, 0), sshorten) : SCM_EOL;
432 Real shorten_property = 2 * robust_scm2double (scm_shorten, 0);
433 /* change in length between full-size and shortened stems is executed gradually.
434 "transition area" = stems between full-sized and fully-shortened.
436 Real quarter_stem_length = 2 * scm_to_double (robust_list_ref (0, s));
437 /* shortening_step = difference in length between consecutive stem lengths
438 in transition area. The bigger the difference between full-sized
439 and shortened stems, the bigger shortening_step is.
440 (but not greater than 1/2 and not smaller than 1/4).
441 value 6 is heuristic; it determines the suggested transition slope steepnesas.
443 Real shortening_step = min (max (0.25, (shorten_property / 6)), 0.5);
444 /* Shortening of unflagged stems should begin on the first stem that sticks
445 more than 1 staffspace (2 units) out of the staff.
446 Shortening of flagged stems begins in the same moment as unflagged ones,
447 but not earlier than on the middle line note.
449 Real which_step = (min (1.0, quarter_stem_length - (2 * staff_rad) - 2.0)) + abs (hp[dir]);
450 Real shorten = min (max (0.0, (shortening_step * which_step)), shorten_property);
455 length *= robust_scm2double (me->get_property ("length-fraction"), 1.0);
458 Grob *t_flag = unsmob<Grob> (me->get_object ("tremolo-flag"));
459 if (t_flag && (!unsmob<Grob> (me->get_object ("beam")) || !calc_beam))
461 /* Crude hack: add extra space if tremolo flag is there.
463 We can't do this for the beam, since we get into a loop
464 (Stem_tremolo::raw_stencil () looks at the beam.) --hwn */
467 + 2 * Stem_tremolo::vertical_length (t_flag) / ss;
469 /* We don't want to add the whole extent of the flag because the trem
470 and the flag can overlap partly. beam_translation gives a good
474 Real beam_trans = Stem_tremolo::get_beam_translation (t_flag);
475 /* the obvious choice is (durlog - 2) here, but we need a bit more space. */
476 minlen += 2 * (durlog - 1.5) * beam_trans;
478 /* up-stems need even a little more space to avoid collisions. This
479 needs to be in sync with the tremolo positioning code in
480 Stem_tremolo::print */
482 minlen += beam_trans;
484 length = max (length, minlen + 1.0);
487 Real stem_end = dir ? hp[dir] + dir * length : 0;
489 /* TODO: change name to extend-stems to staff/center/'() */
490 bool no_extend = to_boolean (me->get_property ("no-stem-extend"));
491 if (!no_extend && dir * stem_end < 0)
497 /* The log of the duration (Number of hooks on the flag minus two) */
499 Stem::duration_log (Grob *me)
501 SCM s = me->get_property ("duration-log");
502 return (scm_is_number (s)) ? scm_to_int (s) : 2;
505 MAKE_SCHEME_CALLBACK (Stem, calc_positioning_done, 1);
507 Stem::calc_positioning_done (SCM smob)
509 Grob *me = unsmob<Grob> (smob);
510 if (!head_count (me))
513 me->set_property ("positioning-done", SCM_BOOL_T);
515 extract_grob_set (me, "note-heads", ro_heads);
516 vector<Grob *> heads (ro_heads);
517 vector_sort (heads, position_less);
518 Direction dir = get_grob_direction (me);
523 Real thick = thickness (me);
525 Grob *hed = support_head (me);
528 programming_error ("Stem dir must be up or down.");
530 set_grob_direction (me, dir);
533 bool is_harmonic_centered = false;
534 for (vsize i = 0; i < heads.size (); i++)
535 is_harmonic_centered = is_harmonic_centered
536 || scm_is_eq (heads[i]->get_property ("style"),
537 ly_symbol2scm ("harmonic"));
538 is_harmonic_centered = is_harmonic_centered && is_invisible (me);
540 Real w = hed->extent (hed, X_AXIS)[dir];
541 for (vsize i = 0; i < heads.size (); i++)
543 Real amount = w - heads[i]->extent (heads[i], X_AXIS)[dir];
545 if (is_harmonic_centered)
547 = hed->extent (hed, X_AXIS).linear_combination (CENTER)
548 - heads[i]->extent (heads[i], X_AXIS).linear_combination (CENTER);
550 if (!isnan (amount)) // empty heads can produce NaN
551 heads[i]->translate_axis (amount, X_AXIS);
554 Real lastpos = Real (Staff_symbol_referencer::get_position (heads[0]));
555 int threshold = robust_scm2int (me->get_property ("note-collision-threshold"), 1);
556 for (vsize i = 1; i < heads.size (); i++)
558 Real p = Staff_symbol_referencer::get_position (heads[i]);
559 Real dy = fabs (lastpos - p);
562 dy should always be 0.5, 0.0, 1.0, but provide safety margin
565 if (dy < 0.1 + threshold)
569 Real ell = heads[i]->extent (heads[i], X_AXIS).length ();
571 Direction d = get_grob_direction (me);
573 Reversed head should be shifted ell-thickness, but this
574 looks too crowded, so we only shift ell-0.5*thickness.
576 This leads to assymetry: Normal heads overlap the
577 stem 100% whereas reversed heads only overlaps the
580 Real reverse_overlap = 0.5;
583 However, the first reverse head has to be shifted even
584 more than the full reverse overlap if it is the same
585 height as the first head or there will be a gap
586 because of the head slant (issue 346).
589 if (i == 1 && dy < 0.1)
590 reverse_overlap = 1.1;
592 if (is_invisible (me))
594 // Semibreves and longer are tucked in considerably
595 // to be recognizable as chorded rather than
596 // parallel voices. During the course of issue 346
597 // there was a discussion to change this for unisons
598 // (dy < 0.1) to reduce overlap but without reaching
599 // agreement and with Gould being rather on the
600 // overlapping front.
604 heads[i]->translate_axis ((ell - thick * reverse_overlap) * d,
609 For some cases we should kern some more: when the
610 distance between the next or prev note is too large, we'd
611 get large white gaps, eg.
632 MAKE_SCHEME_CALLBACK (Stem, calc_direction, 1);
634 Stem::calc_direction (SCM smob)
636 Grob *me = unsmob<Grob> (smob);
637 Direction dir = CENTER;
638 if (Grob *beam = unsmob<Grob> (me->get_object ("beam")))
640 SCM ignore_me = beam->get_property ("direction");
642 dir = get_grob_direction (me);
646 SCM dd = me->get_property ("default-direction");
649 return me->get_property ("neutral-direction");
652 return scm_from_int (dir);
655 MAKE_SCHEME_CALLBACK (Stem, calc_default_direction, 1);
657 Stem::calc_default_direction (SCM smob)
659 Grob *me = unsmob<Grob> (smob);
661 Direction dir = CENTER;
662 int staff_center = 0;
665 Interval hp = head_positions (me);
666 int udistance = (int) (UP * hp[UP] - staff_center);
667 int ddistance = (int) (DOWN * hp[DOWN] - staff_center);
669 dir = Direction (sign (ddistance - udistance));
672 return scm_from_int (dir);
675 // note - height property necessary to trigger quantized beam positions
676 // otherwise, we could just use Grob::stencil_height_proc
677 MAKE_SCHEME_CALLBACK (Stem, height, 1);
679 Stem::height (SCM smob)
681 Grob *me = unsmob<Grob> (smob);
682 return ly_interval2scm (internal_height (me, true));
686 Stem::get_reference_head (Grob *me)
688 return to_boolean (me->get_property ("avoid-note-head"))
694 Stem::beam_end_corrective (Grob *me)
696 Grob *beam = unsmob<Grob> (me->get_object ("beam"));
697 Direction dir = get_grob_direction (me);
702 programming_error ("no stem direction");
705 return dir * Beam::get_beam_thickness (beam) * 0.5;
711 Stem::internal_height (Grob *me, bool calc_beam)
713 Grob *beam = get_beam (me);
714 if (!is_valid_stem (me) && !beam)
717 Direction dir = get_grob_direction (me);
719 if (beam && calc_beam)
721 /* trigger set-stem-lengths. */
722 (void) beam->get_property ("quantized-positions");
726 If there is a beam but no stem, slope calculations depend on this
727 routine to return where the stem end /would/ be.
729 if (calc_beam && !beam && !unsmob<Stencil> (me->get_property ("stencil")))
732 Real y1 = robust_scm2double ((calc_beam
733 ? me->get_property ("stem-begin-position")
734 : me->get_pure_property ("stem-begin-position", 0, INT_MAX)),
737 Real y2 = dir * robust_scm2double ((calc_beam
738 ? me->get_property ("length")
739 : me->get_pure_property ("length", 0, INT_MAX)),
743 Real half_space = Staff_symbol_referencer::staff_space (me) * 0.5;
745 Interval stem_y = Interval (min (y1, y2), max (y2, y1)) * half_space;
750 MAKE_SCHEME_CALLBACK (Stem, width, 1);
754 Grob *me = unsmob<Grob> (e);
758 if (is_invisible (me))
762 r = Interval (-1, 1);
763 r *= thickness (me) / 2;
766 return ly_interval2scm (r);
770 Stem::thickness (Grob *me)
772 return scm_to_double (me->get_property ("thickness"))
773 * Staff_symbol_referencer::line_thickness (me);
776 MAKE_SCHEME_CALLBACK (Stem, calc_stem_begin_position, 1);
778 Stem::calc_stem_begin_position (SCM smob)
780 Grob *me = unsmob<Grob> (smob);
781 return scm_from_double (internal_calc_stem_begin_position (me, true));
784 MAKE_SCHEME_CALLBACK (Stem, pure_calc_stem_begin_position, 3);
786 Stem::pure_calc_stem_begin_position (SCM smob,
790 Grob *me = unsmob<Grob> (smob);
791 return scm_from_double (internal_calc_stem_begin_position (me, false));
795 Stem::internal_calc_stem_begin_position (Grob *me, bool calc_beam)
797 Grob *beam = get_beam (me);
798 Real ss = Staff_symbol_referencer::staff_space (me);
799 if (beam && calc_beam)
801 (void) beam->get_property ("quantized-positions");
802 return robust_scm2double (me->get_property ("stem-begin-position"), 0.0);
805 Direction d = get_grob_direction (me);
806 Grob *lh = get_reference_head (me);
811 Real pos = Staff_symbol_referencer::get_position (lh);
813 if (Grob *head = support_head (me))
815 Interval head_height = head->extent (head, Y_AXIS);
816 Real y_attach = Note_head::stem_attachment_coordinate (head, Y_AXIS);
818 y_attach = head_height.linear_combination (y_attach);
819 if (!isinf (y_attach) && !isnan (y_attach)) // empty heads
820 pos += d * y_attach * 2 / ss;
827 MAKE_SCHEME_CALLBACK (Stem, pure_calc_length, 3);
829 Stem::pure_calc_length (SCM smob, SCM /*start*/, SCM /*end*/)
831 Grob *me = unsmob<Grob> (smob);
832 Real beg = robust_scm2double (me->get_pure_property ("stem-begin-position", 0, INT_MAX), 0.0);
833 Real res = fabs (internal_calc_stem_end_position (me, false) - beg);
834 return scm_from_double (res);
837 MAKE_SCHEME_CALLBACK (Stem, calc_length, 1);
839 Stem::calc_length (SCM smob)
841 Grob *me = unsmob<Grob> (smob);
842 if (unsmob<Grob> (me->get_object ("beam")))
844 me->programming_error ("ly:stem::calc-length called but will not be used for beamed stem.");
845 return scm_from_double (0.0);
848 Real beg = robust_scm2double (me->get_property ("stem-begin-position"), 0.0);
849 Real res = fabs (internal_calc_stem_end_position (me, true) - beg);
850 return scm_from_double (res);
854 Stem::is_valid_stem (Grob *me)
856 /* TODO: make the stem start a direction ?
857 This is required to avoid stems passing in tablature chords. */
860 Grob *lh = get_reference_head (me);
861 Grob *beam = unsmob<Grob> (me->get_object ("beam"));
866 if (is_invisible (me))
872 MAKE_SCHEME_CALLBACK (Stem, print, 1);
874 Stem::print (SCM smob)
876 Grob *me = unsmob<Grob> (smob);
877 if (!is_valid_stem (me))
880 Direction dir = get_grob_direction (me);
881 Real y1 = robust_scm2double (me->get_property ("stem-begin-position"), 0.0);
882 Real y2 = dir * robust_scm2double (me->get_property ("length"), 0.0) + y1;
884 Real half_space = Staff_symbol_referencer::staff_space (me) * 0.5;
886 Interval stem_y = Interval (min (y1, y2), max (y2, y1)) * half_space;
888 stem_y[dir] -= beam_end_corrective (me);
891 Real stem_width = thickness (me);
893 = me->layout ()->get_dimension (ly_symbol2scm ("blot-diameter"));
895 Box b = Box (Interval (-stem_width / 2, stem_width / 2),
899 Stencil ss = Lookup::round_filled_box (b, blot);
900 mol.add_stencil (ss);
902 return mol.smobbed_copy ();
906 move the stem to right of the notehead if it is up.
908 MAKE_SCHEME_CALLBACK (Stem, offset_callback, 1);
910 Stem::offset_callback (SCM smob)
912 Grob *me = unsmob<Grob> (smob);
914 extract_grob_set (me, "rests", rests);
917 Grob *rest = rests.back ();
918 Real r = robust_relative_extent (rest, rest, X_AXIS).center ();
919 return scm_from_double (r);
922 if (Grob *f = first_head (me))
924 Interval head_wid = f->extent (f, X_AXIS);
927 if (is_invisible (me))
930 attach = Note_head::stem_attachment_coordinate (f, X_AXIS);
932 Direction d = get_grob_direction (me);
933 Real real_attach = head_wid.linear_combination (d * attach);
934 Real r = isnan(real_attach)? 0.0: real_attach;
936 /* If not centered: correct for stem thickness. */
937 string style = robust_symbol2string (f->get_property ("style"), "default");
938 if (attach && style != "mensural"
939 && style != "neomensural"
940 && style != "petrucci")
942 Real rule_thick = thickness (me);
943 r += -d * rule_thick * 0.5;
945 return scm_from_double (r);
948 programming_error ("Weird stem.");
949 return scm_from_double (0.0);
953 Stem::get_beam (Grob *me)
955 SCM b = me->get_object ("beam");
956 return unsmob<Spanner> (b);
960 Stem::get_stem_info (Grob *me)
963 si.dir_ = get_grob_direction (me);
965 SCM scm_info = me->get_property ("stem-info");
966 si.ideal_y_ = scm_to_double (scm_car (scm_info));
967 si.shortest_y_ = scm_to_double (scm_cadr (scm_info));
971 MAKE_SCHEME_CALLBACK (Stem, calc_stem_info, 1);
973 Stem::calc_stem_info (SCM smob)
975 Grob *me = unsmob<Grob> (smob);
976 Direction my_dir = get_grob_direction (me);
980 programming_error ("no stem dir set");
984 Real staff_space = Staff_symbol_referencer::staff_space (me);
985 Grob *beam = get_beam (me);
989 (void) beam->get_property ("beaming");
992 Real beam_translation = Beam::get_beam_translation (beam);
993 Real beam_thickness = Beam::get_beam_thickness (beam);
994 int beam_count = Beam::get_direction_beam_count (beam, my_dir);
996 = robust_scm2double (me->get_property ("length-fraction"), 1.0);
998 /* Simple standard stem length */
999 SCM details = me->get_property ("details");
1000 SCM lengths = ly_assoc_get (ly_symbol2scm ("beamed-lengths"), details, SCM_EOL);
1003 = (scm_is_pair (lengths)
1004 ? (scm_to_double (robust_list_ref (beam_count - 1, lengths))
1008 stem only extends to center of beam
1010 - 0.5 * beam_thickness)
1013 /* Condition: sane minimum free stem length (chord to beams) */
1014 lengths = ly_assoc_get (ly_symbol2scm ("beamed-minimum-free-lengths"),
1017 Real ideal_minimum_free
1018 = (scm_is_pair (lengths)
1019 ? (scm_to_double (robust_list_ref (beam_count - 1, lengths))
1024 Real height_of_my_trem = 0.0;
1025 Grob *trem = unsmob<Grob> (me->get_object ("tremolo-flag"));
1029 = Stem_tremolo::vertical_length (trem)
1030 /* hack a bit of space around the trem. */
1035 It seems that also for ideal minimum length, we must use
1036 the maximum beam count (for this direction):
1038 \score { \relative c'' { a8[ a32] } }
1040 must be horizontal. */
1041 Real height_of_my_beams = beam_thickness
1042 + (beam_count - 1) * beam_translation;
1044 Real ideal_minimum_length = ideal_minimum_free
1045 + height_of_my_beams
1047 /* stem only extends to center of beam */
1048 - 0.5 * beam_thickness;
1050 ideal_length = max (ideal_length, ideal_minimum_length);
1052 /* Convert to Y position, calculate for dir == UP */
1054 = /* staff positions */
1055 head_positions (me)[my_dir] * 0.5
1056 * my_dir * staff_space;
1057 Real ideal_y = note_start + ideal_length;
1059 /* Conditions for Y position */
1061 /* Lowest beam of (UP) beam must never be lower than second staffline
1065 Although this (additional) rule is probably correct,
1066 I expect that highest beam (UP) should also never be lower
1067 than middle staffline, just as normal stems.
1071 Obviously not for grace beams.
1073 Also, not for knees. Seems to be a good thing. */
1074 bool no_extend = to_boolean (me->get_property ("no-stem-extend"));
1075 bool is_knee = Beam::is_knee (beam);
1076 if (!no_extend && !is_knee)
1078 /* Highest beam of (UP) beam must never be lower than middle
1080 ideal_y = max (ideal_y, 0.0);
1081 /* Lowest beam of (UP) beam must never be lower than second staffline */
1082 ideal_y = max (ideal_y, (-staff_space
1083 - beam_thickness + height_of_my_beams));
1086 ideal_y -= robust_scm2double (beam->get_property ("shorten"), 0);
1088 SCM bemfl = ly_assoc_get (ly_symbol2scm ("beamed-extreme-minimum-free-lengths"),
1092 = (scm_is_pair (bemfl)
1093 ? (scm_to_double (robust_list_ref (beam_count - 1, bemfl))
1098 Real minimum_length = max (minimum_free, height_of_my_trem)
1099 + height_of_my_beams
1100 /* stem only extends to center of beam */
1101 - 0.5 * beam_thickness;
1104 Real minimum_y = note_start + minimum_length;
1105 Real shortest_y = minimum_y * my_dir;
1107 return scm_list_2 (scm_from_double (ideal_y),
1108 scm_from_double (shortest_y));
1112 Stem::beam_multiplicity (Grob *stem)
1114 SCM beaming = stem->get_property ("beaming");
1115 Slice le = int_list_to_slice (scm_car (beaming));
1116 Slice ri = int_list_to_slice (scm_cdr (beaming));
1122 Stem::is_cross_staff (Grob *stem)
1124 Grob *beam = unsmob<Grob> (stem->get_object ("beam"));
1125 return beam && Beam::is_cross_staff (beam);
1128 MAKE_SCHEME_CALLBACK (Stem, calc_cross_staff, 1)
1130 Stem::calc_cross_staff (SCM smob)
1132 return scm_from_bool (is_cross_staff (unsmob<Grob> (smob)));
1136 Stem::flag (Grob *me)
1138 return unsmob<Grob> (me->get_object ("flag"));
1141 /* FIXME: Too many properties */
1142 ADD_INTERFACE (Stem,
1143 "The stem represents the graphical stem. In addition, it"
1144 " internally connects note heads, beams, and tremolos. Rests"
1145 " and whole notes have invisible stems.\n"
1147 "The following properties may be set in the @code{details}"
1151 "@item beamed-lengths\n"
1152 "List of stem lengths given beam multiplicity.\n"
1153 "@item beamed-minimum-free-lengths\n"
1154 "List of normal minimum free stem lengths (chord to beams)"
1155 " given beam multiplicity.\n"
1156 "@item beamed-extreme-minimum-free-lengths\n"
1157 "List of extreme minimum free stem lengths (chord to beams)"
1158 " given beam multiplicity.\n"
1160 "Default stem lengths. The list gives a length for each"
1162 "@item stem-shorten\n"
1163 "How much a stem in a forced direction should be shortened."
1164 " The list gives an amount depending on the number of flags"
1172 "beamlet-default-length "
1173 "beamlet-max-length-proportion "
1174 "default-direction "
1177 "double-stem-separation "
1185 "neutral-direction "
1188 "note-collision-threshold "
1191 "stem-begin-position "
1199 /****************************************************************/
1201 Stem_info::Stem_info ()
1203 ideal_y_ = shortest_y_ = 0;
1208 Stem_info::scale (Real x)