2 stem.cc -- implement Stem
4 source file of the GNU LilyPond music typesetter
6 (c) 1996--2009 Han-Wen Nienhuys <hanwen@xs4all.nl>
7 Jan Nieuwenhuizen <janneke@gnu.org>
9 TODO: This is way too hairy
13 Stem-end, chord-start, etc. is all confusing naming.
19 #include <cmath> // rint
23 #include "directional-element-interface.hh"
24 #include "dot-column.hh"
25 #include "font-interface.hh"
26 #include "international.hh"
29 #include "note-head.hh"
30 #include "output-def.hh"
31 #include "paper-column.hh"
32 #include "pointer-group-interface.hh"
34 #include "rhythmic-head.hh"
35 #include "side-position-interface.hh"
36 #include "staff-symbol-referencer.hh"
37 #include "stem-tremolo.hh"
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
);
53 for (int i
= 0; i
< beam_count
; i
++)
54 lst
= scm_cons (scm_from_int (i
), lst
);
58 index_set_cell (pair
, d
, lst
);
62 Stem::get_beaming (Grob
*me
, Direction d
)
64 SCM pair
= me
->get_property ("beaming");
65 if (!scm_is_pair (pair
))
68 SCM lst
= index_get_cell (pair
, d
);
70 int len
= scm_ilength (lst
);
75 Stem::head_positions (Grob
*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
]));
87 Stem::chord_start_y (Grob
*me
)
89 Interval hp
= head_positions (me
);
91 return hp
[get_grob_direction (me
)] * Staff_symbol_referencer::staff_space (me
)
99 Stem::set_stemend (Grob
*me
, Real se
)
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 */
113 Stem::support_head (Grob
*me
)
115 extract_grob_set (me
, "note-heads", heads
);
116 if (heads
.size () == 1)
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 */
131 Stem::first_head (Grob
*me
)
133 Direction d
= get_grob_direction (me
);
135 return extremal_heads (me
)[-d
];
139 /* The note head opposite to the first head. */
141 Stem::last_head (Grob
*me
)
143 Direction d
= get_grob_direction (me
);
145 return extremal_heads (me
)[d
];
150 START is part where stem reaches `last' head.
152 This function returns a drul with (bottom-head, top-head).
155 Stem::extremal_heads (Grob
*me
)
157 const int inf
= INT_MAX
;
158 Drul_array
<int> extpos
;
162 Drul_array
<Grob
*> exthead (0, 0);
163 extract_grob_set (me
, "note-heads", heads
);
165 for (vsize i
= heads
.size (); i
--;)
168 int p
= Staff_symbol_referencer::get_rounded_position (n
);
173 if (d
* p
> d
* extpos
[d
])
179 while (flip (&d
) != DOWN
);
184 /* The positions, in ascending order. */
186 Stem::note_head_positions (Grob
*me
)
189 extract_grob_set (me
, "note-heads", heads
);
191 for (vsize i
= heads
.size (); i
--;)
194 int p
= Staff_symbol_referencer::get_rounded_position (n
);
199 vector_sort (ps
, less
<int> ());
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
);
215 Stem::is_invisible (Grob
*me
)
217 return !is_normal_stem (me
)
218 && (robust_scm2double (me
->get_property ("stemlet-length"),
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
,
236 Grob
*me
= unsmob_grob (smob
);
239 if (!is_normal_stem (me
))
240 return ly_interval2scm (iv
);
242 Real ss
= Staff_symbol_referencer::staff_space (me
);
243 Real rad
= Staff_symbol_referencer::staff_radius (me
);
245 if (!to_boolean (me
->get_property ("cross-staff")))
247 Real len
= scm_to_double (calc_length (smob
)) * ss
/ 2;
248 Direction dir
= get_grob_direction (me
);
250 Interval hp
= head_positions (me
);
252 iv
= Interval (0, len
);
254 iv
= Interval (-len
, 0);
257 iv
.translate (hp
[dir
] * ss
/ 2);
259 /* extend the stem (away from the head) to cover the staff */
261 iv
[UP
] = max (iv
[UP
], rad
* ss
);
263 iv
[DOWN
] = min (iv
[DOWN
], -rad
* ss
);
266 iv
= Interval (-rad
* ss
, rad
* ss
);
268 return ly_interval2scm (iv
);
271 MAKE_SCHEME_CALLBACK (Stem
, calc_stem_end_position
, 1)
273 Stem::calc_stem_end_position (SCM smob
)
275 Grob
*me
= unsmob_grob (smob
);
277 if (!head_count (me
))
278 return scm_from_double (0.0);
280 if (Grob
*beam
= get_beam (me
))
282 (void) beam
->get_property ("quantized-positions");
283 return me
->get_property ("stem-end-position");
288 /* WARNING: IN HALF SPACES */
289 Real length
= robust_scm2double (me
->get_property ("length"), 7);
291 Direction dir
= get_grob_direction (me
);
292 Interval hp
= head_positions (me
);
293 Real stem_end
= dir
? hp
[dir
] + dir
* length
: 0;
295 /* TODO: change name to extend-stems to staff/center/'() */
296 bool no_extend
= to_boolean (me
->get_property ("no-stem-extend"));
297 if (!no_extend
&& dir
* stem_end
< 0)
300 return scm_from_double (stem_end
);
303 /* Length is in half-spaces (or: positions) here. */
304 MAKE_SCHEME_CALLBACK (Stem
, calc_length
, 1)
306 Stem::calc_length (SCM smob
)
308 Grob
*me
= unsmob_grob (smob
);
310 SCM details
= me
->get_property ("details");
311 int durlog
= duration_log (me
);
313 Real ss
= Staff_symbol_referencer::staff_space (me
);
315 SCM s
= ly_assoc_get (ly_symbol2scm ("lengths"), details
, SCM_EOL
);
317 length
= 2 * scm_to_double (robust_list_ref (durlog
- 2, s
));
319 Direction dir
= get_grob_direction (me
);
321 /* Stems in unnatural (forced) direction should be shortened,
322 according to [Roush & Gourlay] */
323 Interval hp
= head_positions (me
);
324 if (dir
&& dir
* hp
[dir
] >= 0)
326 SCM sshorten
= ly_assoc_get (ly_symbol2scm ("stem-shorten"), details
, SCM_EOL
);
327 SCM scm_shorten
= scm_is_pair (sshorten
)
328 ? robust_list_ref (max (duration_log (me
) - 2, 0), sshorten
) : SCM_EOL
;
329 Real shorten
= 2* robust_scm2double (scm_shorten
, 0);
331 /* On boundary: shorten only half */
332 if (abs (head_positions (me
)[dir
]) <= 1)
338 length
*= robust_scm2double (me
->get_property ("length-fraction"), 1.0);
341 Grob
*t_flag
= unsmob_grob (me
->get_object ("tremolo-flag"));
342 if (t_flag
&& !unsmob_grob (me
->get_object ("beam")))
344 /* Crude hack: add extra space if tremolo flag is there.
346 We can't do this for the beam, since we get into a loop
347 (Stem_tremolo::raw_stencil () looks at the beam.) --hwn */
350 + 2 * Stem_tremolo::vertical_length (t_flag
) / ss
;
352 /* We don't want to add the whole extent of the flag because the trem
353 and the flag can overlap partly. beam_translation gives a good
357 Real beam_trans
= Stem_tremolo::get_beam_translation (t_flag
);
358 /* the obvious choice is (durlog - 2) here, but we need a bit more space. */
359 minlen
+= 2 * (durlog
- 1.5) * beam_trans
;
361 /* up-stems need even a little more space to avoid collisions. This
362 needs to be in sync with the tremolo positioning code in
363 Stem_tremolo::print */
365 minlen
+= beam_trans
;
367 length
= max (length
, minlen
+ 1.0);
370 return scm_from_double (length
);
372 /* The log of the duration (Number of hooks on the flag minus two) */
374 Stem::duration_log (Grob
*me
)
376 SCM s
= me
->get_property ("duration-log");
377 return (scm_is_number (s
)) ? scm_to_int (s
) : 2;
380 MAKE_SCHEME_CALLBACK (Stem
, calc_positioning_done
, 1);
382 Stem::calc_positioning_done (SCM smob
)
384 Grob
*me
= unsmob_grob (smob
);
385 if (!head_count (me
))
388 me
->set_property ("positioning-done", SCM_BOOL_T
);
390 extract_grob_set (me
, "note-heads", ro_heads
);
391 vector
<Grob
*> heads (ro_heads
);
392 vector_sort (heads
, position_less
);
393 Direction dir
= get_grob_direction (me
);
398 Real thick
= thickness (me
);
400 Grob
*hed
= support_head (me
);
403 programming_error ("Stem dir must be up or down.");
405 set_grob_direction (me
, dir
);
408 bool is_harmonic_centered
= false;
409 for (vsize i
= 0; i
< heads
.size (); i
++)
410 is_harmonic_centered
= is_harmonic_centered
411 || heads
[i
]->get_property ("style") == ly_symbol2scm ("harmonic");
412 is_harmonic_centered
= is_harmonic_centered
&& is_invisible (me
);
414 Real w
= hed
->extent (hed
, X_AXIS
)[dir
];
415 for (vsize i
= 0; i
< heads
.size (); i
++)
417 Real amount
= w
- heads
[i
]->extent (heads
[i
], X_AXIS
)[dir
];
419 if (is_harmonic_centered
)
421 hed
->extent (hed
, X_AXIS
).linear_combination (CENTER
)
422 - heads
[i
]->extent (heads
[i
], X_AXIS
).linear_combination (CENTER
);
424 heads
[i
]->translate_axis (amount
, X_AXIS
);
427 Real lastpos
= Real (Staff_symbol_referencer::get_position (heads
[0]));
428 for (vsize i
= 1; i
< heads
.size (); i
++)
430 Real p
= Staff_symbol_referencer::get_position (heads
[i
]);
431 Real dy
= fabs (lastpos
- p
);
434 dy should always be 0.5, 0.0, 1.0, but provide safety margin
441 Real ell
= heads
[i
]->extent (heads
[i
], X_AXIS
).length ();
443 Direction d
= get_grob_direction (me
);
445 Reversed head should be shifted ell-thickness, but this
446 looks too crowded, so we only shift ell-0.5*thickness.
448 This leads to assymetry: Normal heads overlap the
449 stem 100% whereas reversed heads only overlaps the
453 Real reverse_overlap
= 0.5;
454 heads
[i
]->translate_axis ((ell
- thick
* reverse_overlap
) * d
,
457 if (is_invisible (me
))
458 heads
[i
]->translate_axis (-thick
* (2 - reverse_overlap
) * d
,
463 For some cases we should kern some more: when the
464 distance between the next or prev note is too large, we'd
465 get large white gaps, eg.
486 MAKE_SCHEME_CALLBACK (Stem
, calc_direction
, 1);
488 Stem::calc_direction (SCM smob
)
490 Grob
*me
= unsmob_grob (smob
);
491 Direction dir
= CENTER
;
492 if (Grob
*beam
= unsmob_grob (me
->get_object ("beam")))
494 SCM ignore_me
= beam
->get_property ("direction");
496 dir
= get_grob_direction (me
);
500 SCM dd
= me
->get_property ("default-direction");
503 return me
->get_property ("neutral-direction");
506 return scm_from_int (dir
);
509 MAKE_SCHEME_CALLBACK (Stem
, calc_default_direction
, 1);
511 Stem::calc_default_direction (SCM smob
)
513 Grob
*me
= unsmob_grob (smob
);
515 Direction dir
= CENTER
;
516 int staff_center
= 0;
517 Interval hp
= head_positions (me
);
520 int udistance
= (int) (UP
* hp
[UP
] - staff_center
);
521 int ddistance
= (int) (DOWN
* hp
[DOWN
] - staff_center
);
523 dir
= Direction (sign (ddistance
- udistance
));
526 return scm_from_int (dir
);
530 MAKE_SCHEME_CALLBACK (Stem
, height
, 1);
532 Stem::height (SCM smob
)
534 Grob
*me
= unsmob_grob (smob
);
535 if (!is_normal_stem (me
))
536 return ly_interval2scm (Interval ());
538 Direction dir
= get_grob_direction (me
);
540 Grob
*beam
= get_beam (me
);
543 /* trigger set-stem-lengths. */
544 beam
->get_property ("quantized-positions");
548 Can't get_stencil (), since that would cache stencils too early.
549 This causes problems with beams.
551 Stencil
*stencil
= unsmob_stencil (print (smob
));
552 Interval iv
= stencil
? stencil
->extent (Y_AXIS
) : Interval ();
557 programming_error ("no stem direction");
560 iv
[dir
] += dir
* Beam::get_thickness (beam
) * 0.5;
563 return ly_interval2scm (iv
);
567 Stem::stem_end_position (Grob
*me
)
569 return robust_scm2double (me
->get_property ("stem-end-position"), 0);
572 MAKE_SCHEME_CALLBACK (Stem
, calc_flag
, 1);
574 Stem::calc_flag (SCM smob
)
576 Grob
*me
= unsmob_grob (smob
);
578 int log
= duration_log (me
);
580 TODO: maybe property stroke-style should take different values,
581 e.g. "" (i.e. no stroke), "single" and "double" (currently, it's
585 SCM flag_style_scm
= me
->get_property ("flag-style");
586 if (scm_is_symbol (flag_style_scm
))
587 flag_style
= ly_symbol2string (flag_style_scm
);
589 if (flag_style
== "no-flag")
590 return Stencil ().smobbed_copy ();
594 string staffline_offs
;
595 if (flag_style
== "mensural")
596 /* Mensural notation: For notes on staff lines, use different
597 flags than for notes between staff lines. The idea is that
598 flags are always vertically aligned with the staff lines,
599 regardless if the note head is on a staff line or between two
600 staff lines. In other words, the inner end of a flag always
601 touches a staff line.
606 int p
= (int) (rint (stem_end_position (me
)));
608 = Staff_symbol_referencer::on_line (me
, p
) ? "0" : "1";
611 staffline_offs
= "2";
616 char dir
= (get_grob_direction (me
) == UP
) ? 'u' : 'd';
617 string font_char
= flag_style
618 + to_string (dir
) + staffline_offs
+ to_string (log
);
619 Font_metric
*fm
= Font_interface::get_default_font (me
);
620 Stencil flag
= fm
->find_by_name ("flags." + font_char
);
621 if (flag
.is_empty ())
622 me
->warning (_f ("flag `%s' not found", font_char
));
624 SCM stroke_style_scm
= me
->get_property ("stroke-style");
625 if (scm_is_string (stroke_style_scm
))
627 string stroke_style
= ly_scm2string (stroke_style_scm
);
628 if (!stroke_style
.empty ())
630 string font_char
= flag_style
+ to_string (dir
) + stroke_style
;
631 Stencil stroke
= fm
->find_by_name ("flags." + font_char
);
632 if (stroke
.is_empty ())
634 font_char
= to_string (dir
) + stroke_style
;
635 stroke
= fm
->find_by_name ("flags." + font_char
);
637 if (stroke
.is_empty ())
638 me
->warning (_f ("flag stroke `%s' not found", font_char
));
640 flag
.add_stencil (stroke
);
644 return flag
.smobbed_copy ();
649 Stem::flag (Grob
*me
)
651 int log
= duration_log (me
);
653 || unsmob_grob (me
->get_object ("beam")))
656 if (!is_normal_stem (me
))
659 // This get_property call already evaluates the scheme function with
660 // the grob passed as argument! Thus, we only have to check if a valid
661 // stencil is returned.
662 SCM flag_style_scm
= me
->get_property ("flag");
663 if (Stencil
*flag
= unsmob_stencil (flag_style_scm
)) {
670 MAKE_SCHEME_CALLBACK (Stem
, width
, 1);
674 Grob
*me
= unsmob_grob (e
);
678 if (is_invisible (me
))
680 else if (unsmob_grob (me
->get_object ("beam"))
681 || abs (duration_log (me
)) <= 2)
683 r
= Interval (-1, 1);
684 r
*= thickness (me
) / 2;
688 r
= Interval (-1, 1) * thickness (me
) * 0.5;
689 r
.unite (flag (me
).extent (X_AXIS
));
691 return ly_interval2scm (r
);
695 Stem::thickness (Grob
*me
)
697 return scm_to_double (me
->get_property ("thickness"))
698 * Staff_symbol_referencer::line_thickness (me
);
701 MAKE_SCHEME_CALLBACK (Stem
, print
, 1);
703 Stem::print (SCM smob
)
705 Grob
*me
= unsmob_grob (smob
);
706 Grob
*beam
= get_beam (me
);
709 Direction d
= get_grob_direction (me
);
711 Real stemlet_length
= robust_scm2double (me
->get_property ("stemlet-length"),
713 bool stemlet
= stemlet_length
> 0.0;
715 /* TODO: make the stem start a direction ?
716 This is required to avoid stems passing in tablature chords. */
718 = to_boolean (me
->get_property ("avoid-note-head"))
725 if (!lh
&& stemlet
&& !beam
)
728 if (lh
&& robust_scm2int (lh
->get_property ("duration-log"), 0) < 1)
731 if (is_invisible (me
))
734 Real y2
= robust_scm2double (me
->get_property ("stem-end-position"), 0.0);
736 Real half_space
= Staff_symbol_referencer::staff_space (me
) * 0.5;
739 y2
= Staff_symbol_referencer::get_position (lh
);
742 Real beam_translation
= Beam::get_beam_translation (beam
);
743 Real beam_thickness
= Beam::get_thickness (beam
);
744 int beam_count
= beam_multiplicity (me
).length () + 1;
747 * (0.5 * beam_thickness
748 + beam_translation
* max (0, (beam_count
- 1))
749 + stemlet_length
) / half_space
;
752 Interval
stem_y (min (y1
, y2
), max (y2
, y1
));
754 if (Grob
*head
= support_head (me
))
757 must not take ledgers into account.
759 Interval head_height
= head
->extent (head
, Y_AXIS
);
760 Real y_attach
= Note_head::stem_attachment_coordinate (head
, Y_AXIS
);
762 y_attach
= head_height
.linear_combination (y_attach
);
763 stem_y
[Direction (-d
)] += d
* y_attach
/ half_space
;
767 Real stem_width
= thickness (me
);
769 = me
->layout ()->get_dimension (ly_symbol2scm ("blot-diameter"));
771 Box b
= Box (Interval (-stem_width
/ 2, stem_width
/ 2),
772 Interval (stem_y
[DOWN
] * half_space
, stem_y
[UP
] * half_space
));
774 Stencil ss
= Lookup::round_filled_box (b
, blot
);
775 mol
.add_stencil (ss
);
777 mol
.add_stencil (get_translated_flag (me
));
779 return mol
.smobbed_copy ();
783 Stem::get_translated_flag (Grob
*me
)
785 Stencil fl
= flag (me
);
788 Direction d
= get_grob_direction (me
);
790 = me
->layout ()->get_dimension (ly_symbol2scm ("blot-diameter"));
791 Real stem_width
= thickness (me
);
792 Real half_space
= Staff_symbol_referencer::staff_space (me
) * 0.5;
793 Real y2
= robust_scm2double (me
->get_property ("stem-end-position"), 0.0);
794 fl
.translate_axis (y2
* half_space
- d
* blot
/ 2, Y_AXIS
);
795 fl
.translate_axis (stem_width
/ 2, X_AXIS
);
802 move the stem to right of the notehead if it is up.
804 MAKE_SCHEME_CALLBACK (Stem
, offset_callback
, 1);
806 Stem::offset_callback (SCM smob
)
808 Grob
*me
= unsmob_grob (smob
);
810 extract_grob_set (me
, "rests", rests
);
813 Grob
*rest
= rests
.back ();
814 Real r
= rest
->extent (rest
, X_AXIS
).center ();
815 return scm_from_double (r
);
819 if (Grob
*f
= first_head (me
))
821 Interval head_wid
= f
->extent (f
, X_AXIS
);
824 if (is_invisible (me
))
827 attach
= Note_head::stem_attachment_coordinate (f
, X_AXIS
);
829 Direction d
= get_grob_direction (me
);
830 Real real_attach
= head_wid
.linear_combination (d
* attach
);
831 Real r
= real_attach
;
833 /* If not centered: correct for stem thickness. */
836 Real rule_thick
= thickness (me
);
837 r
+= -d
* rule_thick
* 0.5;
839 return scm_from_double (r
);
842 programming_error ("Weird stem.");
843 return scm_from_double (0.0);
847 Stem::get_beam (Grob
*me
)
849 SCM b
= me
->get_object ("beam");
850 return dynamic_cast<Spanner
*> (unsmob_grob (b
));
854 Stem::get_stem_info (Grob
*me
)
857 si
.dir_
= get_grob_direction (me
);
859 SCM scm_info
= me
->get_property ("stem-info");
860 si
.ideal_y_
= scm_to_double (scm_car (scm_info
));
861 si
.shortest_y_
= scm_to_double (scm_cadr (scm_info
));
865 MAKE_SCHEME_CALLBACK (Stem
, calc_stem_info
, 1);
867 Stem::calc_stem_info (SCM smob
)
869 Grob
*me
= unsmob_grob (smob
);
870 Direction my_dir
= get_grob_direction (me
);
874 programming_error ("no stem dir set");
878 Real staff_space
= Staff_symbol_referencer::staff_space (me
);
879 Grob
*beam
= get_beam (me
);
883 (void) beam
->get_property ("beaming");
886 Real beam_translation
= Beam::get_beam_translation (beam
);
887 Real beam_thickness
= Beam::get_thickness (beam
);
888 int beam_count
= Beam::get_direction_beam_count (beam
, my_dir
);
890 = robust_scm2double (me
->get_property ("length-fraction"), 1.0);
892 /* Simple standard stem length */
893 SCM details
= me
->get_property ("details");
894 SCM lengths
= ly_assoc_get (ly_symbol2scm ("beamed-lengths"), details
, SCM_EOL
);
897 = scm_to_double (robust_list_ref (beam_count
- 1, lengths
))
901 /* stem only extends to center of beam
903 - 0.5 * beam_thickness
;
905 /* Condition: sane minimum free stem length (chord to beams) */
906 lengths
= ly_assoc_get (ly_symbol2scm ("beamed-minimum-free-lengths"), details
, SCM_EOL
);
908 Real ideal_minimum_free
909 = scm_to_double (robust_list_ref (beam_count
- 1, lengths
))
913 Real height_of_my_trem
= 0.0;
914 Grob
*trem
= unsmob_grob (me
->get_object ("tremolo-flag"));
918 = Stem_tremolo::vertical_length (trem
)
919 /* hack a bit of space around the trem. */
925 It seems that also for ideal minimum length, we must use
926 the maximum beam count (for this direction):
928 \score{ \notes\relative c''{ [a8 a32] }}
930 must be horizontal. */
931 Real height_of_my_beams
= beam_thickness
932 + (beam_count
- 1) * beam_translation
;
934 Real ideal_minimum_length
= ideal_minimum_free
937 /* stem only extends to center of beam */
938 - 0.5 * beam_thickness
;
940 ideal_length
= max (ideal_length
, ideal_minimum_length
);
942 /* Convert to Y position, calculate for dir == UP */
944 = /* staff positions */
945 head_positions (me
)[my_dir
] * 0.5
946 * my_dir
* staff_space
;
947 Real ideal_y
= note_start
+ ideal_length
;
949 /* Conditions for Y position */
951 /* Lowest beam of (UP) beam must never be lower than second staffline
955 Although this (additional) rule is probably correct,
956 I expect that highest beam (UP) should also never be lower
957 than middle staffline, just as normal stems.
961 Obviously not for grace beams.
963 Also, not for knees. Seems to be a good thing. */
964 bool no_extend
= to_boolean (me
->get_property ("no-stem-extend"));
965 bool is_knee
= to_boolean (beam
->get_property ("knee"));
966 if (!no_extend
&& !is_knee
)
968 /* Highest beam of (UP) beam must never be lower than middle
970 ideal_y
= max (ideal_y
, 0.0);
971 /* Lowest beam of (UP) beam must never be lower than second staffline */
972 ideal_y
= max (ideal_y
, (-staff_space
973 - beam_thickness
+ height_of_my_beams
));
976 ideal_y
-= robust_scm2double (beam
->get_property ("shorten"), 0);
978 SCM bemfl
= ly_assoc_get (ly_symbol2scm ("beamed-extreme-minimum-free-lengths"),
982 = scm_to_double (robust_list_ref (beam_count
- 1, bemfl
))
986 Real minimum_length
= max (minimum_free
, height_of_my_trem
)
988 /* stem only extends to center of beam */
989 - 0.5 * beam_thickness
;
992 Real minimum_y
= note_start
+ minimum_length
;
993 Real shortest_y
= minimum_y
* my_dir
;
995 return scm_list_2 (scm_from_double (ideal_y
),
996 scm_from_double (shortest_y
));
1000 Stem::beam_multiplicity (Grob
*stem
)
1002 SCM beaming
= stem
->get_property ("beaming");
1003 Slice le
= int_list_to_slice (scm_car (beaming
));
1004 Slice ri
= int_list_to_slice (scm_cdr (beaming
));
1010 Stem::is_cross_staff (Grob
*stem
)
1012 Grob
*beam
= unsmob_grob (stem
->get_object ("beam"));
1013 return beam
&& Beam::is_cross_staff (beam
);
1016 MAKE_SCHEME_CALLBACK (Stem
, calc_cross_staff
, 1)
1018 Stem::calc_cross_staff (SCM smob
)
1020 return scm_from_bool (is_cross_staff (unsmob_grob (smob
)));
1023 /* FIXME: Too many properties */
1024 ADD_INTERFACE (Stem
,
1025 "The stem represents the graphical stem. In addition, it"
1026 " internally connects note heads, beams, and tremolos. Rests"
1027 " and whole notes have invisible stems.\n"
1029 "The following properties may be set in the @code{details}"
1033 "@item beamed-lengths\n"
1034 "List of stem lengths given beam multiplicity.\n"
1035 "@item beamed-minimum-free-lengths\n"
1036 "List of normal minimum free stem lengths (chord to beams)"
1037 " given beam multiplicity.\n"
1038 "@item beamed-extreme-minimum-free-lengths\n"
1039 "List of extreme minimum free stem lengths (chord to beams)"
1040 " given beam multiplicity.\n"
1042 "Default stem lengths. The list gives a length for each"
1044 "@item stem-shorten\n"
1045 "How much a stem in a forced direction should be shortened."
1046 " The list gives an amount depending on the number of flags"
1054 "beamlet-default-length "
1055 "beamlet-max-length-proportion "
1056 "default-direction "
1066 "neutral-direction "
1071 "stem-end-position "
1079 /****************************************************************/
1081 Stem_info::Stem_info ()
1083 ideal_y_
= shortest_y_
= 0;
1088 Stem_info::scale (Real x
)