staff-symbol-referencer.cc: Junk redundant functions.
[lilypond.git] / lily / stem.cc
blobb517b6ec9f69f323dfa97fdea5a52f1781187b92
1 /*
2 stem.cc -- implement Stem
4 source file of the GNU LilyPond music typesetter
6 (c) 1996--2007 Han-Wen Nienhuys <hanwen@xs4all.nl>
7 Jan Nieuwenhuizen <janneke@gnu.org>
9 TODO: This is way too hairy
11 TODO: fix naming.
13 Stem-end, chord-start, etc. is all confusing naming.
16 #include "stem.hh"
17 #include "spanner.hh"
19 #include <cmath> // rint
20 using namespace std;
22 #include "beam.hh"
23 #include "directional-element-interface.hh"
24 #include "dot-column.hh"
25 #include "font-interface.hh"
26 #include "international.hh"
27 #include "lookup.hh"
28 #include "misc.hh"
29 #include "note-head.hh"
30 #include "output-def.hh"
31 #include "paper-column.hh"
32 #include "pointer-group-interface.hh"
33 #include "rest.hh"
34 #include "rhythmic-head.hh"
35 #include "side-position-interface.hh"
36 #include "staff-symbol-referencer.hh"
37 #include "stem-tremolo.hh"
38 #include "warn.hh"
40 void
41 Stem::set_beaming (Grob *me, int beam_count, Direction d)
43 SCM pair = me->get_property ("beaming");
45 if (!scm_is_pair (pair))
47 pair = scm_cons (SCM_EOL, SCM_EOL);
48 me->set_property ("beaming", pair);
51 SCM lst = index_get_cell (pair, d);
52 if (beam_count)
53 for (int i = 0; i < beam_count; i++)
54 lst = scm_cons (scm_from_int (i), lst);
55 else
56 lst = SCM_BOOL_F;
58 index_set_cell (pair, d, lst);
61 int
62 Stem::get_beaming (Grob *me, Direction d)
64 SCM pair = me->get_property ("beaming");
65 if (!scm_is_pair (pair))
66 return 0;
68 SCM lst = index_get_cell (pair, d);
70 int len = scm_ilength (lst);
71 return max (len, 0);
74 Interval
75 Stem::head_positions (Grob *me)
77 if (head_count (me))
79 Drul_array<Grob *> e (extremal_heads (me));
80 return Interval (Staff_symbol_referencer::get_position (e[DOWN]),
81 Staff_symbol_referencer::get_position (e[UP]));
83 return Interval ();
86 Real
87 Stem::chord_start_y (Grob *me)
89 Interval hp = head_positions (me);
90 if (!hp.is_empty ())
91 return hp[get_grob_direction (me)] * Staff_symbol_referencer::staff_space (me)
92 * 0.5;
93 return 0;
98 void
99 Stem::set_stemend (Grob *me, Real se)
101 // todo: margins
102 Direction d = get_grob_direction (me);
104 if (d && d * head_positions (me)[get_grob_direction (me)] >= se * d)
105 me->warning (_ ("weird stem size, check for narrow beams"));
107 me->set_property ("stem-end-position", scm_from_double (se));
110 /* Note head that determines hshift for upstems
111 WARNING: triggers direction */
112 Grob *
113 Stem::support_head (Grob *me)
115 extract_grob_set (me, "note-heads", heads);
116 if (heads.size () == 1)
117 return heads[0];
119 return first_head (me);
123 Stem::head_count (Grob *me)
125 return Pointer_group_interface::count (me, ly_symbol2scm ("note-heads"));
128 /* The note head which forms one end of the stem.
129 WARNING: triggers direction */
130 Grob *
131 Stem::first_head (Grob *me)
133 Direction d = get_grob_direction (me);
134 if (d)
135 return extremal_heads (me)[-d];
136 return 0;
139 /* The note head opposite to the first head. */
140 Grob *
141 Stem::last_head (Grob *me)
143 Direction d = get_grob_direction (me);
144 if (d)
145 return extremal_heads (me)[d];
146 return 0;
150 START is part where stem reaches `last' head.
152 This function returns a drul with (bottom-head, top-head).
154 Drul_array<Grob *>
155 Stem::extremal_heads (Grob *me)
157 const int inf = INT_MAX;
158 Drul_array<int> extpos;
159 extpos[DOWN] = inf;
160 extpos[UP] = -inf;
162 Drul_array<Grob *> exthead (0, 0);
163 extract_grob_set (me, "note-heads", heads);
165 for (vsize i = heads.size (); i--;)
167 Grob *n = heads[i];
168 int p = Staff_symbol_referencer::get_rounded_position (n);
170 Direction d = LEFT;
173 if (d * p > d * extpos[d])
175 exthead[d] = n;
176 extpos[d] = p;
179 while (flip (&d) != DOWN);
181 return exthead;
184 /* The positions, in ascending order. */
185 vector<int>
186 Stem::note_head_positions (Grob *me)
188 vector<int> ps;
189 extract_grob_set (me, "note-heads", heads);
191 for (vsize i = heads.size (); i--;)
193 Grob *n = heads[i];
194 int p = Staff_symbol_referencer::get_rounded_position (n);
196 ps.push_back (p);
199 vector_sort (ps, less<int> ());
200 return ps;
203 void
204 Stem::add_head (Grob *me, Grob *n)
206 n->set_object ("stem", me->self_scm ());
208 if (Note_head::has_interface (n))
209 Pointer_group_interface::add_grob (me, ly_symbol2scm ("note-heads"), n);
210 else if (Rest::has_interface (n))
211 Pointer_group_interface::add_grob (me, ly_symbol2scm ("rests"), n);
214 bool
215 Stem::is_invisible (Grob *me)
217 return !is_normal_stem (me)
218 && (robust_scm2double (me->get_property ("stemlet-length"),
219 0.0) == 0.0);
223 bool
224 Stem::is_normal_stem (Grob *me)
226 return head_count (me) && scm_to_int (me->get_property ("duration-log")) >= 1;
230 MAKE_SCHEME_CALLBACK (Stem, pure_height, 3)
232 Stem::pure_height (SCM smob, SCM start, SCM end)
234 (void) start;
235 (void) end;
237 Grob *me = unsmob_grob (smob);
238 Interval iv;
240 if (!is_normal_stem (me))
241 return ly_interval2scm (iv);
243 Real ss = Staff_symbol_referencer::staff_space (me);
244 Real rad = Staff_symbol_referencer::staff_radius (me);
246 if (!to_boolean (me->get_property ("cross-staff")))
248 Real len = scm_to_double (calc_length (smob)) * ss / 2;
249 Direction dir = get_grob_direction (me);
251 Interval hp = head_positions (me);
252 if (dir == UP)
253 iv = Interval (0, len);
254 else
255 iv = Interval (-len, 0);
257 if (!hp.is_empty ())
258 iv.translate (hp[dir] * ss / 2);
260 /* extend the stem (away from the head) to cover the staff */
261 if (dir == UP)
262 iv[UP] = max (iv[UP], rad * ss);
263 else
264 iv[DOWN] = min (iv[DOWN], -rad * ss);
266 else
267 iv = Interval (-rad * ss, rad * ss);
269 return ly_interval2scm (iv);
272 MAKE_SCHEME_CALLBACK (Stem, calc_stem_end_position, 1)
274 Stem::calc_stem_end_position (SCM smob)
276 Grob *me = unsmob_grob (smob);
278 if (!head_count (me))
279 return scm_from_double (0.0);
281 if (Grob *beam = get_beam (me))
283 (void) beam->get_property ("quantized-positions");
284 return me->get_property ("stem-end-position");
287 vector<Real> a;
289 /* WARNING: IN HALF SPACES */
290 Real length = robust_scm2double (me->get_property ("length"), 7);
292 Direction dir = get_grob_direction (me);
293 Interval hp = head_positions (me);
294 Real stem_end = dir ? hp[dir] + dir * length : 0;
296 /* TODO: change name to extend-stems to staff/center/'() */
297 bool no_extend = to_boolean (me->get_property ("no-stem-extend"));
298 if (!no_extend && dir * stem_end < 0)
299 stem_end = 0.0;
301 return scm_from_double (stem_end);
304 /* Length is in half-spaces (or: positions) here. */
305 MAKE_SCHEME_CALLBACK (Stem, calc_length, 1)
307 Stem::calc_length (SCM smob)
309 Grob *me = unsmob_grob (smob);
311 SCM details = me->get_property ("details");
312 int durlog = duration_log (me);
314 Real ss = Staff_symbol_referencer::staff_space (me);
315 Real length = 7;
316 SCM s = ly_assoc_get (ly_symbol2scm ("lengths"), details, SCM_EOL);
317 if (scm_is_pair (s))
318 length = 2 * scm_to_double (robust_list_ref (durlog - 2, s));
320 Direction dir = get_grob_direction (me);
322 /* Stems in unnatural (forced) direction should be shortened,
323 according to [Roush & Gourlay] */
324 Interval hp = head_positions (me);
325 if (dir && dir * hp[dir] >= 0)
327 SCM sshorten = ly_assoc_get (ly_symbol2scm ("stem-shorten"), details, SCM_EOL);
328 SCM scm_shorten = scm_is_pair (sshorten)
329 ? robust_list_ref (max (duration_log (me) - 2, 0), sshorten) : SCM_EOL;
330 Real shorten = 2* robust_scm2double (scm_shorten, 0);
332 /* On boundary: shorten only half */
333 if (abs (head_positions (me)[dir]) <= 1)
334 shorten *= 0.5;
336 length -= shorten;
339 length *= robust_scm2double (me->get_property ("length-fraction"), 1.0);
341 /* Tremolo stuff. */
342 Grob *t_flag = unsmob_grob (me->get_object ("tremolo-flag"));
343 if (t_flag && !unsmob_grob (me->get_object ("beam")))
345 /* Crude hack: add extra space if tremolo flag is there.
347 We can't do this for the beam, since we get into a loop
348 (Stem_tremolo::raw_stencil () looks at the beam.) --hwn */
350 Real minlen = 1.0
351 + 2 * Stem_tremolo::vertical_length (t_flag) / ss;
353 /* We don't want to add the whole extent of the flag because the trem
354 and the flag can overlap partly. beam_translation gives a good
355 approximation */
356 if (durlog >= 3)
358 Real beam_trans = Stem_tremolo::get_beam_translation (t_flag);
359 /* the obvious choice is (durlog - 2) here, but we need a bit more space. */
360 minlen += 2 * (durlog - 1.5) * beam_trans;
362 /* up-stems need even a little more space to avoid collisions. This
363 needs to be in sync with the tremolo positioning code in
364 Stem_tremolo::print */
365 if (dir == UP)
366 minlen += beam_trans;
368 length = max (length, minlen + 1.0);
371 return scm_from_double (length);
373 /* The log of the duration (Number of hooks on the flag minus two) */
375 Stem::duration_log (Grob *me)
377 SCM s = me->get_property ("duration-log");
378 return (scm_is_number (s)) ? scm_to_int (s) : 2;
381 MAKE_SCHEME_CALLBACK (Stem, calc_positioning_done, 1);
383 Stem::calc_positioning_done (SCM smob)
385 Grob *me = unsmob_grob (smob);
386 if (!head_count (me))
387 return SCM_BOOL_T;
389 me->set_property ("positioning-done", SCM_BOOL_T);
391 extract_grob_set (me, "note-heads", ro_heads);
392 vector<Grob*> heads (ro_heads);
393 vector_sort (heads, position_less);
394 Direction dir = get_grob_direction (me);
396 if (dir < 0)
397 reverse (heads);
399 Real thick = thickness (me);
401 Grob *hed = support_head (me);
402 if (!dir)
404 programming_error ("Stem dir must be up or down.");
405 dir = UP;
406 set_grob_direction (me, dir);
409 bool is_harmonic_centered = false;
410 for (vsize i = 0; i < heads.size (); i++)
411 is_harmonic_centered = is_harmonic_centered
412 || heads[i]->get_property ("style") == ly_symbol2scm ("harmonic");
413 is_harmonic_centered = is_harmonic_centered && is_invisible (me);
415 Real w = hed->extent (hed, X_AXIS)[dir];
416 for (vsize i = 0; i < heads.size (); i++)
418 Real amount = w - heads[i]->extent (heads[i], X_AXIS)[dir];
420 if (is_harmonic_centered)
421 amount =
422 hed->extent (hed, X_AXIS).linear_combination (CENTER)
423 - heads[i]->extent (heads[i], X_AXIS).linear_combination (CENTER);
425 heads[i]->translate_axis (amount, X_AXIS);
427 bool parity = true;
428 Real lastpos = Real (Staff_symbol_referencer::get_position (heads[0]));
429 for (vsize i = 1; i < heads.size (); i++)
431 Real p = Staff_symbol_referencer::get_position (heads[i]);
432 Real dy = fabs (lastpos- p);
435 dy should always be 0.5, 0.0, 1.0, but provide safety margin
436 for rounding errors.
438 if (dy < 1.1)
440 if (parity)
442 Real ell = heads[i]->extent (heads[i], X_AXIS).length ();
444 Direction d = get_grob_direction (me);
446 Reversed head should be shifted ell-thickness, but this
447 looks too crowded, so we only shift ell-0.5*thickness.
449 This leads to assymetry: Normal heads overlap the
450 stem 100% whereas reversed heads only overlaps the
451 stem 50%
454 Real reverse_overlap = 0.5;
455 heads[i]->translate_axis ((ell - thick * reverse_overlap) * d,
456 X_AXIS);
458 if (is_invisible (me))
459 heads[i]->translate_axis (-thick * (2 - reverse_overlap) * d,
460 X_AXIS);
462 /* TODO:
464 For some cases we should kern some more: when the
465 distance between the next or prev note is too large, we'd
466 get large white gaps, eg.
470 |X <- kern this.
476 parity = !parity;
478 else
479 parity = true;
481 lastpos = int (p);
484 return SCM_BOOL_T;
487 MAKE_SCHEME_CALLBACK (Stem, calc_direction, 1);
489 Stem::calc_direction (SCM smob)
491 Grob *me = unsmob_grob (smob);
492 Direction dir = CENTER;
493 if (Grob *beam = unsmob_grob (me->get_object ("beam")))
495 SCM ignore_me = beam->get_property ("direction");
496 (void) ignore_me;
497 dir = get_grob_direction (me);
499 else
501 SCM dd = me->get_property ("default-direction");
502 dir = to_dir (dd);
503 if (!dir)
504 return me->get_property ("neutral-direction");
507 return scm_from_int (dir);
510 MAKE_SCHEME_CALLBACK (Stem, calc_default_direction, 1);
512 Stem::calc_default_direction (SCM smob)
514 Grob *me = unsmob_grob (smob);
516 Direction dir = CENTER;
517 int staff_center = 0;
518 Interval hp = head_positions (me);
519 if (!hp.is_empty ())
521 int udistance = (int) (UP * hp[UP] - staff_center);
522 int ddistance = (int) (DOWN * hp[DOWN] - staff_center);
524 dir = Direction (sign (ddistance - udistance));
527 return scm_from_int (dir);
531 MAKE_SCHEME_CALLBACK (Stem, height, 1);
533 Stem::height (SCM smob)
535 Grob *me = unsmob_grob (smob);
536 if (!is_normal_stem (me))
537 return ly_interval2scm (Interval ());
539 Direction dir = get_grob_direction (me);
541 Grob *beam = get_beam (me);
542 if (beam)
544 /* trigger set-stem-lengths. */
545 beam->get_property ("quantized-positions");
549 Can't get_stencil (), since that would cache stencils too early.
550 This causes problems with beams.
552 Stencil *stencil = unsmob_stencil (print (smob));
553 Interval iv = stencil ? stencil->extent (Y_AXIS) : Interval ();
554 if (beam)
556 if (dir == CENTER)
558 programming_error ("no stem direction");
559 dir = UP;
561 iv[dir] += dir * Beam::get_thickness (beam) * 0.5;
564 return ly_interval2scm (iv);
567 Real
568 Stem::stem_end_position (Grob *me)
570 return robust_scm2double (me->get_property ("stem-end-position"), 0);
573 Stencil
574 Stem::flag (Grob *me)
576 int log = duration_log (me);
577 if (log < 3
578 || unsmob_grob (me->get_object ("beam")))
579 return Stencil ();
581 if (!is_normal_stem (me))
582 return Stencil ();
585 TODO: maybe property stroke-style should take different values,
586 e.g. "" (i.e. no stroke), "single" and "double" (currently, it's
587 '() or "grace"). */
588 string flag_style;
590 SCM flag_style_scm = me->get_property ("flag-style");
591 if (scm_is_symbol (flag_style_scm))
592 flag_style = ly_symbol2string (flag_style_scm);
594 if (flag_style == "no-flag")
595 return Stencil ();
597 bool adjust = true;
599 string staffline_offs;
600 if (flag_style == "mensural")
601 /* Mensural notation: For notes on staff lines, use different
602 flags than for notes between staff lines. The idea is that
603 flags are always vertically aligned with the staff lines,
604 regardless if the note head is on a staff line or between two
605 staff lines. In other words, the inner end of a flag always
606 touches a staff line.
609 if (adjust)
611 int p = (int) (rint (stem_end_position (me)));
612 staffline_offs
613 = Staff_symbol_referencer::on_line (me, p) ? "0" : "1";
615 else
616 staffline_offs = "2";
618 else
619 staffline_offs = "";
621 char dir = (get_grob_direction (me) == UP) ? 'u' : 'd';
622 string font_char = flag_style
623 + to_string (dir) + staffline_offs + to_string (log);
624 Font_metric *fm = Font_interface::get_default_font (me);
625 Stencil flag = fm->find_by_name ("flags." + font_char);
626 if (flag.is_empty ())
627 me->warning (_f ("flag `%s' not found", font_char));
629 SCM stroke_style_scm = me->get_property ("stroke-style");
630 if (scm_is_string (stroke_style_scm))
632 string stroke_style = ly_scm2string (stroke_style_scm);
633 if (!stroke_style.empty ())
635 string font_char = to_string (dir) + stroke_style;
636 Stencil stroke = fm->find_by_name ("flags." + font_char);
637 if (stroke.is_empty ())
638 me->warning (_f ("flag stroke `%s' not found", font_char));
639 else
640 flag.add_stencil (stroke);
644 return flag;
647 MAKE_SCHEME_CALLBACK (Stem, width, 1);
649 Stem::width (SCM e)
651 Grob *me = unsmob_grob (e);
653 Interval r;
655 if (is_invisible (me))
656 r.set_empty ();
657 else if (unsmob_grob (me->get_object ("beam"))
658 || abs (duration_log (me)) <= 2)
660 r = Interval (-1, 1);
661 r *= thickness (me) / 2;
663 else
665 r = Interval (-1, 1) * thickness (me) * 0.5;
666 r.unite (flag (me).extent (X_AXIS));
668 return ly_interval2scm (r);
671 Real
672 Stem::thickness (Grob *me)
674 return scm_to_double (me->get_property ("thickness"))
675 * Staff_symbol_referencer::line_thickness (me);
678 MAKE_SCHEME_CALLBACK (Stem, print, 1);
680 Stem::print (SCM smob)
682 Grob *me = unsmob_grob (smob);
683 Grob *beam = get_beam (me);
685 Stencil mol;
686 Direction d = get_grob_direction (me);
688 Real stemlet_length = robust_scm2double (me->get_property ("stemlet-length"),
689 0.0);
690 bool stemlet = stemlet_length > 0.0;
692 /* TODO: make the stem start a direction ?
693 This is required to avoid stems passing in tablature chords. */
694 Grob *lh
695 = to_boolean (me->get_property ("avoid-note-head"))
696 ? last_head (me)
697 : first_head (me);
699 if (!lh && !stemlet)
700 return SCM_EOL;
702 if (!lh && stemlet && !beam)
703 return SCM_EOL;
705 if (lh && robust_scm2int (lh->get_property ("duration-log"), 0) < 1)
706 return SCM_EOL;
708 if (is_invisible (me))
709 return SCM_EOL;
711 Real y2 = robust_scm2double (me->get_property ("stem-end-position"), 0.0);
712 Real y1 = y2;
713 Real half_space = Staff_symbol_referencer::staff_space (me) * 0.5;
715 if (lh)
716 y2 = Staff_symbol_referencer::get_position (lh);
717 else if (stemlet)
719 Real beam_translation = Beam::get_beam_translation (beam);
720 Real beam_thickness = Beam::get_thickness (beam);
721 int beam_count = beam_multiplicity (me).length () + 1;
723 y2 -= d
724 * (0.5 * beam_thickness
725 + beam_translation * max (0, (beam_count - 1))
726 + stemlet_length) / half_space;
729 Interval stem_y (min (y1, y2), max (y2, y1));
731 if (Grob *head = support_head (me))
734 must not take ledgers into account.
736 Interval head_height = head->extent (head, Y_AXIS);
737 Real y_attach = Note_head::stem_attachment_coordinate (head, Y_AXIS);
739 y_attach = head_height.linear_combination (y_attach);
740 stem_y[Direction (-d)] += d * y_attach / half_space;
743 // URG
744 Real stem_width = thickness (me);
745 Real blot
746 = me->layout ()->get_dimension (ly_symbol2scm ("blot-diameter"));
748 Box b = Box (Interval (-stem_width / 2, stem_width / 2),
749 Interval (stem_y[DOWN] * half_space, stem_y[UP] * half_space));
751 Stencil ss = Lookup::round_filled_box (b, blot);
752 mol.add_stencil (ss);
754 mol.add_stencil (get_translated_flag (me));
756 return mol.smobbed_copy ();
759 Stencil
760 Stem::get_translated_flag (Grob *me)
762 Stencil fl = flag (me);
763 if (!fl.is_empty ())
765 Direction d = get_grob_direction (me);
766 Real blot
767 = me->layout ()->get_dimension (ly_symbol2scm ("blot-diameter"));
768 Real stem_width = thickness (me);
769 Real half_space = Staff_symbol_referencer::staff_space (me) * 0.5;
770 Real y2 = robust_scm2double (me->get_property ("stem-end-position"), 0.0);
771 fl.translate_axis (y2 * half_space - d * blot / 2, Y_AXIS);
772 fl.translate_axis (stem_width / 2, X_AXIS);
774 return fl;
779 move the stem to right of the notehead if it is up.
781 MAKE_SCHEME_CALLBACK (Stem, offset_callback, 1);
783 Stem::offset_callback (SCM smob)
785 Grob *me = unsmob_grob (smob);
787 extract_grob_set (me, "rests", rests);
788 if (rests.size ())
790 Grob *rest = rests.back ();
791 Real r = rest->extent (rest, X_AXIS).center ();
792 return scm_from_double (r);
796 if (Grob *f = first_head (me))
798 Interval head_wid = f->extent (f, X_AXIS);
799 Real attach = 0.0;
801 if (is_invisible (me))
802 attach = 0.0;
803 else
804 attach = Note_head::stem_attachment_coordinate (f, X_AXIS);
806 Direction d = get_grob_direction (me);
807 Real real_attach = head_wid.linear_combination (d * attach);
808 Real r = real_attach;
810 /* If not centered: correct for stem thickness. */
811 if (attach)
813 Real rule_thick = thickness (me);
814 r += -d * rule_thick * 0.5;
816 return scm_from_double (r);
819 programming_error ("Weird stem.");
820 return scm_from_double (0.0);
823 Spanner *
824 Stem::get_beam (Grob *me)
826 SCM b = me->get_object ("beam");
827 return dynamic_cast<Spanner *> (unsmob_grob (b));
830 Stem_info
831 Stem::get_stem_info (Grob *me)
833 Stem_info si;
834 si.dir_ = get_grob_direction (me);
836 SCM scm_info = me->get_property ("stem-info");
837 si.ideal_y_ = scm_to_double (scm_car (scm_info));
838 si.shortest_y_ = scm_to_double (scm_cadr (scm_info));
839 return si;
842 MAKE_SCHEME_CALLBACK (Stem, calc_stem_info, 1);
844 Stem::calc_stem_info (SCM smob)
846 Grob *me = unsmob_grob (smob);
847 Direction my_dir = get_grob_direction (me);
849 if (!my_dir)
851 programming_error ("no stem dir set");
852 my_dir = UP;
855 Real staff_space = Staff_symbol_referencer::staff_space (me);
856 Grob *beam = get_beam (me);
858 if (beam)
860 (void) beam->get_property ("beaming");
863 Real beam_translation = Beam::get_beam_translation (beam);
864 Real beam_thickness = Beam::get_thickness (beam);
865 int beam_count = Beam::get_direction_beam_count (beam, my_dir);
866 Real length_fraction
867 = robust_scm2double (me->get_property ("length-fraction"), 1.0);
869 /* Simple standard stem length */
870 SCM details = me->get_property ("details");
871 SCM lengths = ly_assoc_get (ly_symbol2scm ("beamed-lengths"), details, SCM_EOL);
873 Real ideal_length
874 = scm_to_double (robust_list_ref (beam_count - 1, lengths))
875 * staff_space
876 * length_fraction
878 /* stem only extends to center of beam
880 - 0.5 * beam_thickness;
882 /* Condition: sane minimum free stem length (chord to beams) */
883 lengths = ly_assoc_get (ly_symbol2scm ("beamed-minimum-free-lengths"), details, SCM_EOL);
885 Real ideal_minimum_free
886 = scm_to_double (robust_list_ref (beam_count - 1, lengths))
887 * staff_space
888 * length_fraction;
890 Real height_of_my_trem = 0.0;
891 Grob *trem = unsmob_grob (me->get_object ("tremolo-flag"));
892 if (trem)
894 height_of_my_trem
895 = Stem_tremolo::vertical_length (trem)
896 /* hack a bit of space around the trem. */
897 + beam_translation;
901 /* UGH
902 It seems that also for ideal minimum length, we must use
903 the maximum beam count (for this direction):
905 \score{ \notes\relative c''{ [a8 a32] }}
907 must be horizontal. */
908 Real height_of_my_beams = beam_thickness
909 + (beam_count - 1) * beam_translation;
911 Real ideal_minimum_length = ideal_minimum_free
912 + height_of_my_beams
913 + height_of_my_trem
914 /* stem only extends to center of beam */
915 - 0.5 * beam_thickness;
917 ideal_length = max (ideal_length, ideal_minimum_length);
919 /* Convert to Y position, calculate for dir == UP */
920 Real note_start
921 = /* staff positions */
922 head_positions (me)[my_dir] * 0.5
923 * my_dir * staff_space;
924 Real ideal_y = note_start + ideal_length;
926 /* Conditions for Y position */
928 /* Lowest beam of (UP) beam must never be lower than second staffline
930 Reference?
932 Although this (additional) rule is probably correct,
933 I expect that highest beam (UP) should also never be lower
934 than middle staffline, just as normal stems.
936 Reference?
938 Obviously not for grace beams.
940 Also, not for knees. Seems to be a good thing. */
941 bool no_extend = to_boolean (me->get_property ("no-stem-extend"));
942 bool is_knee = to_boolean (beam->get_property ("knee"));
943 if (!no_extend && !is_knee)
945 /* Highest beam of (UP) beam must never be lower than middle
946 staffline */
947 ideal_y = max (ideal_y, 0.0);
948 /* Lowest beam of (UP) beam must never be lower than second staffline */
949 ideal_y = max (ideal_y, (-staff_space
950 - beam_thickness + height_of_my_beams));
953 ideal_y -= robust_scm2double (beam->get_property ("shorten"), 0);
955 SCM bemfl = ly_assoc_get (ly_symbol2scm ("beamed-extreme-minimum-free-lengths"),
956 details, SCM_EOL);
958 Real minimum_free
959 = scm_to_double (robust_list_ref (beam_count - 1, bemfl))
960 * staff_space
961 * length_fraction;
963 Real minimum_length = max (minimum_free, height_of_my_trem)
964 + height_of_my_beams
965 /* stem only extends to center of beam */
966 - 0.5 * beam_thickness;
968 ideal_y *= my_dir;
969 Real minimum_y = note_start + minimum_length;
970 Real shortest_y = minimum_y * my_dir;
972 return scm_list_2 (scm_from_double (ideal_y),
973 scm_from_double (shortest_y));
976 Slice
977 Stem::beam_multiplicity (Grob *stem)
979 SCM beaming = stem->get_property ("beaming");
980 Slice le = int_list_to_slice (scm_car (beaming));
981 Slice ri = int_list_to_slice (scm_cdr (beaming));
982 le.unite (ri);
983 return le;
986 bool
987 Stem::is_cross_staff (Grob *stem)
989 Grob *beam = unsmob_grob (stem->get_object ("beam"));
990 return beam && Beam::is_cross_staff (beam);
993 MAKE_SCHEME_CALLBACK (Stem, calc_cross_staff, 1)
995 Stem::calc_cross_staff (SCM smob)
997 return scm_from_bool (is_cross_staff (unsmob_grob (smob)));
1000 /* FIXME: Too many properties */
1001 ADD_INTERFACE (Stem,
1002 "The stem represents the graphical stem. In addition, it"
1003 " internally connects note heads, beams, and tremolos. Rests"
1004 " and whole notes have invisible stems.\n"
1005 "\n"
1006 "The following properties may be set in the @code{details}"
1007 " list.\n"
1008 "\n"
1009 "@table @code\n"
1010 "@item beamed-lengths\n"
1011 "List of stem lengths given beam multiplicity.\n"
1012 "@item beamed-minimum-free-lengths\n"
1013 "List of normal minimum free stem lengths (chord to beams)"
1014 " given beam multiplicity.\n"
1015 "@item beamed-extreme-minimum-free-lengths\n"
1016 "List of extreme minimum free stem lengths (chord to beams)"
1017 " given beam multiplicity.\n"
1018 "@item lengths\n"
1019 "Default stem lengths. The list gives a length for each"
1020 " flag count.\n"
1021 "@item stem-shorten\n"
1022 "How much a stem in a forced direction should be shortened."
1023 " The list gives an amount depending on the number of flags"
1024 " and beams.\n"
1025 "@end table\n",
1027 /* properties */
1028 "avoid-note-head "
1029 "beam "
1030 "beaming "
1031 "default-direction "
1032 "details "
1033 "direction "
1034 "duration-log "
1035 "flag-style "
1036 "french-beaming "
1037 "length "
1038 "length-fraction "
1039 "max-beam-connect "
1040 "neutral-direction "
1041 "no-stem-extend "
1042 "note-heads "
1043 "positioning-done "
1044 "rests "
1045 "stem-end-position "
1046 "stem-info "
1047 "stemlet-length "
1048 "stroke-style "
1049 "thickness "
1050 "tremolo-flag "
1053 /****************************************************************/
1055 Stem_info::Stem_info ()
1057 ideal_y_ = shortest_y_ = 0;
1058 dir_ = CENTER;
1061 void
1062 Stem_info::scale (Real x)
1064 ideal_y_ *= x;
1065 shortest_y_ *= x;