.ly version update.
[lilypond.git] / lily / stem.cc
blob32576c2170c7988311010a39c8fc282498564bb1
1 /*
2 stem.cc -- implement Stem
4 source file of the GNU LilyPond music typesetter
6 (c) 1996--2003 Han-Wen Nienhuys <hanwen@cs.uu.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 <math.h> // rint
18 #include "lookup.hh"
19 #include "directional-element-interface.hh"
20 #include "note-head.hh"
21 #include "stem.hh"
22 #include "warn.hh"
23 #include "paper-def.hh"
24 #include "rhythmic-head.hh"
25 #include "font-interface.hh"
26 #include "molecule.hh"
27 #include "paper-column.hh"
28 #include "misc.hh"
29 #include "beam.hh"
30 #include "rest.hh"
31 #include "group-interface.hh"
32 #include "staff-symbol-referencer.hh"
33 #include "spanner.hh"
34 #include "side-position-interface.hh"
35 #include "dot-column.hh"
36 #include "stem-tremolo.hh"
38 void
39 Stem::set_beaming (Grob*me, int beam_count, Direction d)
41 SCM pair = me->get_grob_property ("beaming");
43 if (!gh_pair_p (pair))
45 pair = gh_cons (SCM_EOL, SCM_EOL);
46 me->set_grob_property ("beaming", pair);
49 SCM l = index_get_cell (pair, d);
50 for( int i = 0; i< beam_count; i++)
52 l = gh_cons (gh_int2scm (i), l);
54 index_set_cell (pair, d, l);
58 Interval
59 Stem::head_positions (Grob*me)
61 if (!head_count (me))
63 Interval iv;
64 return iv;
67 Drul_array<Grob*> e (extremal_heads (me));
69 return Interval (Staff_symbol_referencer::get_position (e[DOWN]),
70 Staff_symbol_referencer::get_position (e[UP]));
74 Real
75 Stem::chord_start_y (Grob*me)
77 return head_positions (me)[get_direction (me)]
78 * Staff_symbol_referencer::staff_space (me)/2.0;
81 Real
82 Stem::stem_end_position (Grob*me)
84 SCM p =me->get_grob_property ("stem-end-position");
85 Real pos;
86 if (!gh_number_p (p))
88 pos = get_default_stem_end_position (me);
89 me->set_grob_property ("stem-end-position", gh_double2scm (pos));
91 else
92 pos = gh_scm2double (p);
94 return pos;
97 Direction
98 Stem::get_direction (Grob*me)
100 Direction d = Directional_element_interface::get (me);
102 if (!d)
104 d = get_default_dir (me);
105 // urg, AAARGH!
106 Directional_element_interface::set (me, d);
108 return d ;
112 void
113 Stem::set_stemend (Grob*me, Real se)
115 // todo: margins
116 Direction d= get_direction (me);
118 if (d && d * head_positions (me)[get_direction (me)] >= se*d)
119 me->warning (_ ("Weird stem size; check for narrow beams"));
121 me->set_grob_property ("stem-end-position", gh_double2scm (se));
126 Note head that determines hshift for upstems
128 WARNING: triggers direction
130 Grob*
131 Stem::support_head (Grob*me)
133 SCM h = me->get_grob_property ("support-head");
134 Grob * nh = unsmob_grob (h);
135 if (nh)
136 return nh;
137 else if (head_count (me) == 1)
140 UGH.
143 return unsmob_grob (ly_car (me->get_grob_property ("note-heads")));
145 else
146 return first_head (me);
151 Stem::head_count (Grob*me)
153 return Pointer_group_interface::count (me, "note-heads");
157 The note head which forms one end of the stem.
159 WARNING: triggers direction
161 Grob*
162 Stem::first_head (Grob*me)
164 Direction d = get_direction (me);
165 if (!d)
166 return 0;
167 return extremal_heads (me)[-d];
171 The note head opposite to the first head.
173 Grob*
174 Stem::last_head (Grob*me)
176 Direction d = get_direction (me);
177 if (!d)
178 return 0;
179 return extremal_heads (me)[d];
183 START is part where stem reaches `last' head.
185 Drul_array<Grob*>
186 Stem::extremal_heads (Grob*me)
188 const int inf = 1000000;
189 Drul_array<int> extpos;
190 extpos[DOWN] = inf;
191 extpos[UP] = -inf;
193 Drul_array<Grob *> exthead;
194 exthead[LEFT] = exthead[RIGHT] =0;
196 for (SCM s = me->get_grob_property ("note-heads"); gh_pair_p (s); s = ly_cdr (s))
198 Grob * n = unsmob_grob (ly_car (s));
201 int p = int (Staff_symbol_referencer::get_position (n));
203 Direction d = LEFT;
204 do {
205 if (d* p > d* extpos[d])
207 exthead[d] = n;
208 extpos[d] = p;
210 } while (flip (&d) != DOWN);
213 return exthead;
216 static int
217 icmp (int const &a, int const &b)
219 return a-b;
222 Array<int>
223 Stem::note_head_positions (Grob *me)
225 Array<int> ps ;
226 for (SCM s = me->get_grob_property ("note-heads"); gh_pair_p (s); s = ly_cdr (s))
228 Grob * n = unsmob_grob (ly_car (s));
229 int p = int (Staff_symbol_referencer::get_position (n));
231 ps.push (p);
234 ps.sort (icmp);
235 return ps;
239 void
240 Stem::add_head (Grob*me, Grob *n)
242 n->set_grob_property ("stem", me->self_scm ());
243 n->add_dependency (me);
245 if (Note_head::has_interface (n))
247 Pointer_group_interface::add_grob (me, ly_symbol2scm ("note-heads"), n);
251 bool
252 Stem::invisible_b (Grob*me)
254 return ! (head_count (me)
255 && gh_scm2int (me->get_grob_property ("duration-log")) >= 1);
258 Direction
259 Stem::get_default_dir (Grob*me)
261 int staff_center = 0;
262 Interval hp = head_positions (me);
263 if (hp.empty_b())
265 return CENTER;
268 int udistance = (int) (UP * hp[UP] - staff_center);
269 int ddistance = (int) (DOWN* hp[DOWN] - staff_center);
271 if (sign (ddistance - udistance))
272 return Direction (sign (ddistance -udistance));
274 return to_dir (me->get_grob_property ("neutral-direction"));
277 Real
278 Stem::get_default_stem_end_position (Grob*me)
280 /* Tab notation feature: make stem end extend out of staff. */
281 SCM up_to_staff = me->get_grob_property ("up-to-staff");
282 if (to_boolean (up_to_staff))
284 int line_count = Staff_symbol_referencer::line_count (me);
285 Direction dir = get_direction (me);
286 return dir * (line_count + 3.5);
288 Real ss = Staff_symbol_referencer::staff_space (me);
290 int durlog = duration_log (me);
292 bool grace_b = to_boolean (me->get_grob_property ("grace"));
293 SCM s;
294 Array<Real> a;
297 Real length = 7; // WARNING: IN HALF SPACES
298 SCM scm_len = me->get_grob_property ("length");
299 if (gh_number_p (scm_len))
301 length = gh_scm2double (scm_len);
303 else
305 s = me->get_grob_property ("lengths");
306 if (gh_pair_p (s))
308 length = 2* gh_scm2double (robust_list_ref (durlog -2, s));
314 /* URGURGURG
315 'set-default-stemlen' sets direction too
317 Direction dir = get_direction (me);
318 if (!dir)
320 dir = get_default_dir (me);
321 Directional_element_interface::set (me, dir);
325 /* stems in unnatural (forced) direction should be shortened,
326 according to [Roush & Gourlay] */
327 if (!chord_start_y (me)
328 || (get_direction (me) != get_default_dir (me)))
331 Real shorten = 0.0;
333 SCM sshorten = me->get_grob_property ("stem-shorten");
334 SCM scm_shorten = gh_pair_p (sshorten) ?
335 robust_list_ref ((duration_log (me) - 2) >? 0, sshorten): SCM_EOL;
336 if (gh_number_p (scm_shorten))
338 shorten = 2* gh_scm2double (scm_shorten);
341 /* On boundary: shorten only half */
342 if (abs (head_positions (me)[get_direction (me)]) <= 1)
343 shorten *= 0.5;
345 length -= shorten;
349 Tremolo stuff:
351 Grob * trem = unsmob_grob (me->get_grob_property ("tremolo-flag"));
352 if (trem && !unsmob_grob (me->get_grob_property ("beam")))
355 Crude hack: add extra space if tremolo flag is there.
357 We can't do this for the beam, since we get into a loop
358 (Stem_tremolo::raw_molecule() looks at the beam.)
360 --hwn
363 Real minlen =
364 1.0 + 2 * Stem_tremolo::raw_molecule (trem).extent (Y_AXIS).length () / ss;
366 if (durlog >= 3)
368 Interval flag_ext = flag (me).extent (Y_AXIS) ;
369 if (!flag_ext.empty_b())
370 minlen += 2 * flag_ext.length () / ss ;
373 The clash is smaller for down stems (since the tremolo is
374 angled up.)
376 if (dir == DOWN)
377 minlen -= 1.0;
380 length = length >? (minlen + 1.0);
383 Interval hp = head_positions (me);
384 Real st = hp[dir] + dir * length;
387 TODO: change name to extend-stems to staff/center/'()
389 bool no_extend_b = to_boolean (me->get_grob_property ("no-stem-extend"));
390 if (!grace_b && !no_extend_b && dir * st < 0) // junkme?
391 st = 0.0;
394 Make a little room if we have a upflag and there is a dot.
395 previous approach was to lengthen the stem. This is not
396 good typesetting practice.
399 if (!get_beam (me) && dir == UP
400 && durlog > 2)
402 Grob * closest_to_flag = extremal_heads (me)[dir];
403 Grob * dots = closest_to_flag
404 ? Rhythmic_head::get_dots (closest_to_flag ) : 0;
406 if (dots)
408 Real dp = Staff_symbol_referencer::get_position (dots);
409 Real flagy = flag (me).extent (Y_AXIS)[-dir] * 2
410 / ss;
413 Very gory: add myself to the X-support of the parent,
414 which should be a dot-column.
416 if (dir * (st + flagy - dp) < 0.5)
418 Grob *par = dots->get_parent (X_AXIS);
420 if (Dot_column::has_interface (par))
422 Side_position_interface::add_support (par, me);
425 TODO: apply some better logic here. The flag is
426 curved inwards, so this will typically be too
427 much.
435 return st;
442 the log of the duration (Number of hooks on the flag minus two)
445 Stem::duration_log (Grob*me)
447 SCM s = me->get_grob_property ("duration-log");
448 return (gh_number_p (s)) ? gh_scm2int (s) : 2;
451 void
452 Stem::position_noteheads (Grob*me)
454 if (!head_count (me))
455 return;
457 Link_array<Grob> heads =
458 Pointer_group_interface__extract_grobs (me, (Grob*)0, "note-heads");
460 heads.sort (compare_position);
461 Direction dir =get_direction (me);
463 if (dir < 0)
464 heads.reverse ();
467 Real thick = gh_scm2double (me->get_grob_property ("thickness"))
468 * me->get_paper ()->get_realvar (ly_symbol2scm ("linethickness"));
470 Grob *hed = support_head (me);
471 Real w = Note_head::head_extent (hed,X_AXIS)[dir];
472 for (int i=0; i < heads.size (); i++)
474 heads[i]->translate_axis (w - Note_head::head_extent (heads[i],X_AXIS)[dir],
475 X_AXIS);
478 bool parity= true;
479 int lastpos = int (Staff_symbol_referencer::get_position (heads[0]));
480 for (int i=1; i < heads.size (); i ++)
482 Real p = Staff_symbol_referencer::get_position (heads[i]);
483 int dy =abs (lastpos- (int)p);
485 if (dy <= 1)
487 if (parity)
489 Real l = Note_head::head_extent (heads[i], X_AXIS).length ();
491 Direction d = get_direction (me);
492 /* reversed head should be shifted l-thickness, but this looks
493 too crowded, so we only shift l-0.5*thickness.
494 Notice that this leads to assymetry: Normal heads overlap
495 the stem 100% whereas reversed heads only overlaps the stem
496 50% */
497 #define magic 0.5
498 heads[i]->translate_axis ((l-thick*magic) * d, X_AXIS);
500 if (invisible_b(me))
501 heads[i]->translate_axis (-thick*(2-magic) * d , X_AXIS);
504 /* TODO:
506 For some cases we should kern some more: when the
507 distance between the next or prev note is too large, we'd
508 get large white gaps, eg.
512 |X <- kern this.
518 parity = !parity;
520 else
521 parity = true;
523 lastpos = int (p);
527 MAKE_SCHEME_CALLBACK (Stem,before_line_breaking,1);
529 Stem::before_line_breaking (SCM smob)
531 Grob*me = unsmob_grob (smob);
535 Do the calculations for visible stems, but also for invisible stems
536 with note heads (i.e. half notes.)
538 if (head_count (me))
540 stem_end_position (me); // ugh. Trigger direction calc.
541 position_noteheads (me);
543 else
545 me->set_grob_property ("molecule-callback", SCM_EOL);
548 return SCM_UNSPECIFIED;
552 ugh.
553 When in a beam with tuplet brackets, brew_mol is called early,
554 caching a wrong value.
556 MAKE_SCHEME_CALLBACK (Stem, height, 2);
558 Stem::height (SCM smob, SCM ax)
560 Axis a = (Axis)gh_scm2int (ax);
561 Grob * me = unsmob_grob (smob);
562 assert (a == Y_AXIS);
564 SCM mol = me->get_uncached_molecule ();
565 Interval iv;
566 if (mol != SCM_EOL)
567 iv = unsmob_molecule (mol)->extent (a);
568 if (Grob *b =get_beam (me))
570 Direction d = get_direction (me);
571 iv[d] += d * Beam::get_thickness (b) /2.0 ;
574 return ly_interval2scm (iv);
578 Molecule
579 Stem::flag (Grob*me)
581 /* TODO: maybe property stroke-style should take different values,
582 e.g. "" (i.e. no stroke), "single" and "double" (currently, it's
583 '() or "grace"). */
584 String flag_style;
586 SCM flag_style_scm = me->get_grob_property ("flag-style");
587 if (gh_symbol_p (flag_style_scm))
589 flag_style = ly_symbol2string (flag_style_scm);
592 if (flag_style == "no-flag")
594 return Molecule ();
597 bool adjust = to_boolean (me->get_grob_property ("adjust-if-on-staffline"));
599 String staffline_offs;
600 if (String::compare (flag_style, "mensural") == 0)
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 /* Urrgh! We have to detect wether this stem ends on a staff
612 line or between two staff lines. But we can not call
613 stem_end_position(me) or get_default_stem_end_position(me),
614 since this encounters the flag and hence results in an
615 infinite recursion. However, in pure mensural notation,
616 there are no multiple note heads attached to a single stem,
617 neither is there usually need for using the stem_shorten
618 property (except for 32th and 64th notes, but that is not a
619 problem since the stem length in this case is augmented by
620 an integral multiple of staff_space). Hence, it should be
621 sufficient to just take the first note head, assume it's
622 the only one, look if it's on a staff line, and select the
623 flag's shape accordingly. In the worst case, the shape
624 looks slightly misplaced, but that will usually be the
625 programmer's fault (e.g. when trying to attach multiple
626 note heads to a single stem in mensural notation).
630 perhaps the detection whether this correction is needed should
631 happen in a different place to avoid the recursion.
633 --hwn.
635 int p = (int)rint (Staff_symbol_referencer::get_position (first_head (me)));
636 staffline_offs = Staff_symbol_referencer::on_staffline (me, p) ?
637 "1" : "0";
639 else
641 staffline_offs = "2";
644 else
646 staffline_offs = "";
649 char dir = (get_direction (me) == UP) ? 'u' : 'd';
650 String font_char =
651 flag_style + to_string (dir) + staffline_offs + to_string (duration_log (me));
652 Font_metric *fm = Font_interface::get_default_font (me);
653 Molecule flag = fm->find_by_name ("flags-" + font_char);
654 if (flag.empty_b ())
656 me->warning (_f ("flag `%s' not found", font_char));
659 SCM stroke_style_scm = me->get_grob_property ("stroke-style");
660 if (gh_string_p (stroke_style_scm))
662 String stroke_style = ly_scm2string (stroke_style_scm);
663 if (!stroke_style.empty_b ())
665 String font_char = to_string (dir) + stroke_style;
666 Molecule stroke = fm->find_by_name ("flags-" + font_char);
667 if (stroke.empty_b ())
669 me->warning (_f ("flag stroke `%s' not found", font_char));
671 else
673 flag.add_molecule (stroke);
678 return flag;
681 MAKE_SCHEME_CALLBACK (Stem,dim_callback,2);
683 Stem::dim_callback (SCM e, SCM ax)
685 Axis a = (Axis) gh_scm2int (ax);
686 assert (a == X_AXIS);
687 Grob *se = unsmob_grob (e);
688 Interval r (0, 0);
689 if (unsmob_grob (se->get_grob_property ("beam")) || abs (duration_log (se)) <= 2)
690 ; // TODO!
691 else
693 r = flag (se).extent (X_AXIS);
695 return ly_interval2scm (r);
700 MAKE_SCHEME_CALLBACK (Stem,brew_molecule,1);
703 Stem::brew_molecule (SCM smob)
705 Grob*me = unsmob_grob (smob);
706 Molecule mol;
707 Direction d = get_direction (me);
711 Real y1;
714 This is required to avoid stems passing in tablature chords...
719 TODO: make the stem start a direction ?
721 if (to_boolean (me->get_grob_property ("avoid-note-head")))
723 Grob * lh = last_head (me);
724 if (!lh)
725 return SCM_EOL;
726 y1 = Staff_symbol_referencer::get_position (lh);
728 else
730 Grob * lh = first_head (me);
731 if (!lh)
732 return SCM_EOL;
733 y1 = Staff_symbol_referencer::get_position (lh);
736 Real y2 = stem_end_position (me);
738 Interval stem_y (y1 <? y2,y2 >? y1);
741 // dy?
742 Real dy = Staff_symbol_referencer::staff_space (me) * 0.5;
744 if (Grob *hed = support_head (me))
747 must not take ledgers into account.
749 Interval head_height = Note_head::head_extent (hed,Y_AXIS);
750 Real y_attach = Note_head::stem_attachment_coordinate ( hed, Y_AXIS);
752 y_attach = head_height.linear_combination (y_attach);
753 stem_y[Direction (-d)] += d * y_attach/dy;
756 if (!invisible_b (me))
758 Real stem_width = gh_scm2double (me->get_grob_property ("thickness"))
759 // URG
760 * me->get_paper ()->get_realvar (ly_symbol2scm ("linethickness"));
762 Molecule ss =Lookup::filledbox (Box (Interval (-stem_width/2, stem_width/2),
763 Interval (stem_y[DOWN]*dy, stem_y[UP]*dy)));
764 mol.add_molecule (ss);
767 if (!get_beam (me) && abs (duration_log (me)) > 2)
769 Molecule fl = flag (me);
770 fl.translate_axis (stem_y[d]*dy, Y_AXIS);
771 mol.add_molecule (fl);
774 return mol.smobbed_copy ();
778 move the stem to right of the notehead if it is up.
780 MAKE_SCHEME_CALLBACK (Stem,off_callback,2);
782 Stem::off_callback (SCM element_smob, SCM)
784 Grob *me = unsmob_grob (element_smob);
786 Real r=0;
788 if (head_count (me) == 0)
790 return gh_double2scm (0.0);
793 if (Grob * f = first_head (me))
795 Interval head_wid = Note_head::head_extent(f, X_AXIS);
798 Real attach =0.0;
800 if (invisible_b (me))
802 attach = 0.0;
804 else
805 attach = Note_head::stem_attachment_coordinate(f, X_AXIS);
807 Direction d = get_direction (me);
809 Real real_attach = head_wid.linear_combination (d * attach);
811 r = real_attach;
814 If not centered: correct for stem thickness.
816 if (attach)
818 Real rule_thick
819 = gh_scm2double (me->get_grob_property ("thickness"))
820 * me->get_paper ()->get_realvar (ly_symbol2scm ("linethickness"));
823 r += - d * rule_thick * 0.5;
826 return gh_double2scm (r);
829 Grob*
830 Stem::get_beam (Grob*me)
832 SCM b= me->get_grob_property ("beam");
833 return unsmob_grob (b);
836 Stem_info
837 Stem::get_stem_info (Grob *me)
839 /* Return cached info if available */
840 SCM scm_info = me->get_grob_property ("stem-info");
841 if (!gh_pair_p (scm_info))
843 calc_stem_info (me);
844 scm_info = me->get_grob_property ("stem-info");
847 Stem_info si;
848 si.dir_ = Directional_element_interface::get (me);
849 si.ideal_y_ = gh_scm2double (gh_car (scm_info));
850 si.shortest_y_ = gh_scm2double (gh_cadr (scm_info));
851 return si;
856 TODO: add extra space for tremolos!
858 void
859 Stem::calc_stem_info (Grob *me)
861 /* Tab notation feature: make stem end extend out of staff. */
862 SCM up_to_staff = me->get_grob_property ("up-to-staff");
863 if (to_boolean (up_to_staff))
865 int line_count = Staff_symbol_referencer::line_count (me);
866 Direction dir = get_direction (me);
867 Real ideal_y = dir * (line_count + 1.5);
868 Real shortest_y = ideal_y;
870 me->set_grob_property ("stem-info",
871 scm_list_n (gh_double2scm (ideal_y),
872 gh_double2scm (shortest_y),
873 SCM_UNDEFINED));
874 return;
877 Direction my_dir = Directional_element_interface::get (me);
878 Real staff_space = Staff_symbol_referencer::staff_space (me);
879 Grob *beam = get_beam (me);
880 Real beam_translation = Beam::get_beam_translation (beam);
881 Real beam_thickness = gh_scm2double (beam->get_grob_property ("thickness"));
882 int beam_count = Beam::get_direction_beam_count (beam, my_dir);
885 /* Simple standard stem length */
886 SCM lengths = me->get_grob_property ("beamed-lengths");
887 Real ideal_length =
888 gh_scm2double (robust_list_ref (beam_count - 1,lengths))
890 * staff_space
891 /* stem only extends to center of beam */
892 - 0.5 * beam_thickness;
894 /* Condition: sane minimum free stem length (chord to beams) */
895 lengths = me->get_grob_property ("beamed-minimum-free-lengths");
896 Real ideal_minimum_free =
897 gh_scm2double (robust_list_ref (beam_count - 1, lengths))
898 * staff_space;
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 /* stem only extends to center of beam */
914 - 0.5 * beam_thickness;
916 ideal_length = 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;
924 Real ideal_y = note_start + ideal_length;
927 /* Conditions for Y position */
929 /* Lowest beam of (UP) beam must never be lower than second staffline
931 Reference?
933 Although this (additional) rule is probably correct,
934 I expect that highest beam (UP) should also never be lower
935 than middle staffline, just as normal stems.
937 Reference?
939 Obviously not for grace beams.
941 Also, not for knees. Seems to be a good thing. */
942 SCM grace = me->get_grob_property ("grace");
943 bool grace_b = to_boolean (grace);
944 bool no_extend_b = to_boolean (me->get_grob_property ("no-stem-extend"));
945 bool knee_b = to_boolean (beam->get_grob_property ("knee"));
946 if (!grace_b && !no_extend_b && !knee_b)
948 /* Highest beam of (UP) beam must never be lower than middle
949 staffline */
950 ideal_y = ideal_y >? 0;
951 /* Lowest beam of (UP) beam must never be lower than second staffline */
952 ideal_y = ideal_y >? (-staff_space
953 - beam_thickness + height_of_my_beams);
957 SCM shorten = beam->get_grob_property ("shorten");
958 if (gh_number_p (shorten))
959 ideal_y -= gh_scm2double (shorten);
961 Real minimum_free =
962 gh_scm2double (robust_list_ref
963 (beam_count - 1,
964 me->get_grob_property
965 ("beamed-extreme-minimum-free-lengths")))
966 * staff_space;
968 Real minimum_length = minimum_free
969 + height_of_my_beams
970 /* stem only extends to center of beam */
971 - 0.5 * beam_thickness;
973 Real minimum_y = note_start + minimum_length;
976 ideal_y *= my_dir;
977 Real shortest_y = minimum_y * my_dir;
979 me->set_grob_property ("stem-info",
980 scm_list_n (gh_double2scm (ideal_y),
981 gh_double2scm (shortest_y),
982 SCM_UNDEFINED));
985 Slice
986 Stem::beam_multiplicity (Grob *stem)
988 SCM beaming= stem->get_grob_property ("beaming");
989 Slice l = int_list_to_slice (gh_car (beaming));
990 Slice r = int_list_to_slice (gh_cdr (beaming));
991 l.unite (r);
993 return l;
998 these are too many props.
1000 ADD_INTERFACE (Stem,"stem-interface",
1001 "A stem",
1002 "tremolo-flag french-beaming up-to-staff avoid-note-head adjust-if-on-staffline thickness stem-info beamed-lengths beamed-minimum-free-lengths beamed-extreme-minimum-free-lengths lengths beam stem-shorten duration-log beaming neutral-direction stem-end-position support-head note-heads direction length flag-style no-stem-extend stroke-style");