2 stem.cc -- implement Stem
4 source file of the GNU LilyPond music typesetter
6 (c) 1996--2008 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
, SCM start
, SCM end
)
237 Grob
*me
= unsmob_grob (smob
);
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
);
253 iv
= Interval (0, len
);
255 iv
= Interval (-len
, 0);
258 iv
.translate (hp
[dir
] * ss
/ 2);
260 /* extend the stem (away from the head) to cover the staff */
262 iv
[UP
] = max (iv
[UP
], rad
* ss
);
264 iv
[DOWN
] = min (iv
[DOWN
], -rad
* ss
);
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");
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)
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
);
316 SCM s
= ly_assoc_get (ly_symbol2scm ("lengths"), details
, SCM_EOL
);
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)
339 length
*= robust_scm2double (me
->get_property ("length-fraction"), 1.0);
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 */
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
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 */
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
))
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
);
399 Real thick
= thickness (me
);
401 Grob
*hed
= support_head (me
);
404 programming_error ("Stem dir must be up or down.");
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
)
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
);
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
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
454 Real reverse_overlap
= 0.5;
455 heads
[i
]->translate_axis ((ell
- thick
* reverse_overlap
) * d
,
458 if (is_invisible (me
))
459 heads
[i
]->translate_axis (-thick
* (2 - reverse_overlap
) * d
,
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.
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");
497 dir
= get_grob_direction (me
);
501 SCM dd
= me
->get_property ("default-direction");
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
);
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
);
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 ();
558 programming_error ("no stem direction");
561 iv
[dir
] += dir
* Beam::get_thickness (beam
) * 0.5;
564 return ly_interval2scm (iv
);
568 Stem::stem_end_position (Grob
*me
)
570 return robust_scm2double (me
->get_property ("stem-end-position"), 0);
573 MAKE_SCHEME_CALLBACK (Stem
, calc_flag
, 1);
575 Stem::calc_flag (SCM smob
)
577 Grob
*me
= unsmob_grob (smob
);
579 int log
= duration_log (me
);
581 TODO: maybe property stroke-style should take different values,
582 e.g. "" (i.e. no stroke), "single" and "double" (currently, it's
586 SCM flag_style_scm
= me
->get_property ("flag-style");
587 if (scm_is_symbol (flag_style_scm
))
588 flag_style
= ly_symbol2string (flag_style_scm
);
590 if (flag_style
== "no-flag")
591 return Stencil ().smobbed_copy ();
595 string staffline_offs
;
596 if (flag_style
== "mensural")
597 /* Mensural notation: For notes on staff lines, use different
598 flags than for notes between staff lines. The idea is that
599 flags are always vertically aligned with the staff lines,
600 regardless if the note head is on a staff line or between two
601 staff lines. In other words, the inner end of a flag always
602 touches a staff line.
607 int p
= (int) (rint (stem_end_position (me
)));
609 = Staff_symbol_referencer::on_line (me
, p
) ? "0" : "1";
612 staffline_offs
= "2";
617 char dir
= (get_grob_direction (me
) == UP
) ? 'u' : 'd';
618 string font_char
= flag_style
619 + to_string (dir
) + staffline_offs
+ to_string (log
);
620 Font_metric
*fm
= Font_interface::get_default_font (me
);
621 Stencil flag
= fm
->find_by_name ("flags." + font_char
);
622 if (flag
.is_empty ())
623 me
->warning (_f ("flag `%s' not found", font_char
));
625 SCM stroke_style_scm
= me
->get_property ("stroke-style");
626 if (scm_is_string (stroke_style_scm
))
628 string stroke_style
= ly_scm2string (stroke_style_scm
);
629 if (!stroke_style
.empty ())
631 string font_char
= flag_style
+ to_string (dir
) + stroke_style
;
632 Stencil stroke
= fm
->find_by_name ("flags." + font_char
);
633 if (stroke
.is_empty ())
635 font_char
= to_string (dir
) + stroke_style
;
636 stroke
= fm
->find_by_name ("flags." + font_char
);
638 if (stroke
.is_empty ())
639 me
->warning (_f ("flag stroke `%s' not found", font_char
));
641 flag
.add_stencil (stroke
);
645 return flag
.smobbed_copy ();
650 Stem::flag (Grob
*me
)
652 int log
= duration_log (me
);
654 || unsmob_grob (me
->get_object ("beam")))
657 if (!is_normal_stem (me
))
660 // This get_property call already evaluates the scheme function with
661 // the grob passed as argument! Thus, we only have to check if a valid
662 // stencil is returned.
663 SCM flag_style_scm
= me
->get_property ("flag");
664 if (Stencil
*flag
= unsmob_stencil (flag_style_scm
)) {
671 MAKE_SCHEME_CALLBACK (Stem
, width
, 1);
675 Grob
*me
= unsmob_grob (e
);
679 if (is_invisible (me
))
681 else if (unsmob_grob (me
->get_object ("beam"))
682 || abs (duration_log (me
)) <= 2)
684 r
= Interval (-1, 1);
685 r
*= thickness (me
) / 2;
689 r
= Interval (-1, 1) * thickness (me
) * 0.5;
690 r
.unite (flag (me
).extent (X_AXIS
));
692 return ly_interval2scm (r
);
696 Stem::thickness (Grob
*me
)
698 return scm_to_double (me
->get_property ("thickness"))
699 * Staff_symbol_referencer::line_thickness (me
);
702 MAKE_SCHEME_CALLBACK (Stem
, print
, 1);
704 Stem::print (SCM smob
)
706 Grob
*me
= unsmob_grob (smob
);
707 Grob
*beam
= get_beam (me
);
710 Direction d
= get_grob_direction (me
);
712 Real stemlet_length
= robust_scm2double (me
->get_property ("stemlet-length"),
714 bool stemlet
= stemlet_length
> 0.0;
716 /* TODO: make the stem start a direction ?
717 This is required to avoid stems passing in tablature chords. */
719 = to_boolean (me
->get_property ("avoid-note-head"))
726 if (!lh
&& stemlet
&& !beam
)
729 if (lh
&& robust_scm2int (lh
->get_property ("duration-log"), 0) < 1)
732 if (is_invisible (me
))
735 Real y2
= robust_scm2double (me
->get_property ("stem-end-position"), 0.0);
737 Real half_space
= Staff_symbol_referencer::staff_space (me
) * 0.5;
740 y2
= Staff_symbol_referencer::get_position (lh
);
743 Real beam_translation
= Beam::get_beam_translation (beam
);
744 Real beam_thickness
= Beam::get_thickness (beam
);
745 int beam_count
= beam_multiplicity (me
).length () + 1;
748 * (0.5 * beam_thickness
749 + beam_translation
* max (0, (beam_count
- 1))
750 + stemlet_length
) / half_space
;
753 Interval
stem_y (min (y1
, y2
), max (y2
, y1
));
755 if (Grob
*head
= support_head (me
))
758 must not take ledgers into account.
760 Interval head_height
= head
->extent (head
, Y_AXIS
);
761 Real y_attach
= Note_head::stem_attachment_coordinate (head
, Y_AXIS
);
763 y_attach
= head_height
.linear_combination (y_attach
);
764 stem_y
[Direction (-d
)] += d
* y_attach
/ half_space
;
768 Real stem_width
= thickness (me
);
770 = me
->layout ()->get_dimension (ly_symbol2scm ("blot-diameter"));
772 Box b
= Box (Interval (-stem_width
/ 2, stem_width
/ 2),
773 Interval (stem_y
[DOWN
] * half_space
, stem_y
[UP
] * half_space
));
775 Stencil ss
= Lookup::round_filled_box (b
, blot
);
776 mol
.add_stencil (ss
);
778 mol
.add_stencil (get_translated_flag (me
));
780 return mol
.smobbed_copy ();
784 Stem::get_translated_flag (Grob
*me
)
786 Stencil fl
= flag (me
);
789 Direction d
= get_grob_direction (me
);
791 = me
->layout ()->get_dimension (ly_symbol2scm ("blot-diameter"));
792 Real stem_width
= thickness (me
);
793 Real half_space
= Staff_symbol_referencer::staff_space (me
) * 0.5;
794 Real y2
= robust_scm2double (me
->get_property ("stem-end-position"), 0.0);
795 fl
.translate_axis (y2
* half_space
- d
* blot
/ 2, Y_AXIS
);
796 fl
.translate_axis (stem_width
/ 2, X_AXIS
);
803 move the stem to right of the notehead if it is up.
805 MAKE_SCHEME_CALLBACK (Stem
, offset_callback
, 1);
807 Stem::offset_callback (SCM smob
)
809 Grob
*me
= unsmob_grob (smob
);
811 extract_grob_set (me
, "rests", rests
);
814 Grob
*rest
= rests
.back ();
815 Real r
= rest
->extent (rest
, X_AXIS
).center ();
816 return scm_from_double (r
);
820 if (Grob
*f
= first_head (me
))
822 Interval head_wid
= f
->extent (f
, X_AXIS
);
825 if (is_invisible (me
))
828 attach
= Note_head::stem_attachment_coordinate (f
, X_AXIS
);
830 Direction d
= get_grob_direction (me
);
831 Real real_attach
= head_wid
.linear_combination (d
* attach
);
832 Real r
= real_attach
;
834 /* If not centered: correct for stem thickness. */
837 Real rule_thick
= thickness (me
);
838 r
+= -d
* rule_thick
* 0.5;
840 return scm_from_double (r
);
843 programming_error ("Weird stem.");
844 return scm_from_double (0.0);
848 Stem::get_beam (Grob
*me
)
850 SCM b
= me
->get_object ("beam");
851 return dynamic_cast<Spanner
*> (unsmob_grob (b
));
855 Stem::get_stem_info (Grob
*me
)
858 si
.dir_
= get_grob_direction (me
);
860 SCM scm_info
= me
->get_property ("stem-info");
861 si
.ideal_y_
= scm_to_double (scm_car (scm_info
));
862 si
.shortest_y_
= scm_to_double (scm_cadr (scm_info
));
866 MAKE_SCHEME_CALLBACK (Stem
, calc_stem_info
, 1);
868 Stem::calc_stem_info (SCM smob
)
870 Grob
*me
= unsmob_grob (smob
);
871 Direction my_dir
= get_grob_direction (me
);
875 programming_error ("no stem dir set");
879 Real staff_space
= Staff_symbol_referencer::staff_space (me
);
880 Grob
*beam
= get_beam (me
);
884 (void) beam
->get_property ("beaming");
887 Real beam_translation
= Beam::get_beam_translation (beam
);
888 Real beam_thickness
= Beam::get_thickness (beam
);
889 int beam_count
= Beam::get_direction_beam_count (beam
, my_dir
);
891 = robust_scm2double (me
->get_property ("length-fraction"), 1.0);
893 /* Simple standard stem length */
894 SCM details
= me
->get_property ("details");
895 SCM lengths
= ly_assoc_get (ly_symbol2scm ("beamed-lengths"), details
, SCM_EOL
);
898 = scm_to_double (robust_list_ref (beam_count
- 1, lengths
))
902 /* stem only extends to center of beam
904 - 0.5 * beam_thickness
;
906 /* Condition: sane minimum free stem length (chord to beams) */
907 lengths
= ly_assoc_get (ly_symbol2scm ("beamed-minimum-free-lengths"), details
, SCM_EOL
);
909 Real ideal_minimum_free
910 = scm_to_double (robust_list_ref (beam_count
- 1, lengths
))
914 Real height_of_my_trem
= 0.0;
915 Grob
*trem
= unsmob_grob (me
->get_object ("tremolo-flag"));
919 = Stem_tremolo::vertical_length (trem
)
920 /* hack a bit of space around the trem. */
926 It seems that also for ideal minimum length, we must use
927 the maximum beam count (for this direction):
929 \score{ \notes\relative c''{ [a8 a32] }}
931 must be horizontal. */
932 Real height_of_my_beams
= beam_thickness
933 + (beam_count
- 1) * beam_translation
;
935 Real ideal_minimum_length
= ideal_minimum_free
938 /* stem only extends to center of beam */
939 - 0.5 * beam_thickness
;
941 ideal_length
= max (ideal_length
, ideal_minimum_length
);
943 /* Convert to Y position, calculate for dir == UP */
945 = /* staff positions */
946 head_positions (me
)[my_dir
] * 0.5
947 * my_dir
* staff_space
;
948 Real ideal_y
= note_start
+ ideal_length
;
950 /* Conditions for Y position */
952 /* Lowest beam of (UP) beam must never be lower than second staffline
956 Although this (additional) rule is probably correct,
957 I expect that highest beam (UP) should also never be lower
958 than middle staffline, just as normal stems.
962 Obviously not for grace beams.
964 Also, not for knees. Seems to be a good thing. */
965 bool no_extend
= to_boolean (me
->get_property ("no-stem-extend"));
966 bool is_knee
= to_boolean (beam
->get_property ("knee"));
967 if (!no_extend
&& !is_knee
)
969 /* Highest beam of (UP) beam must never be lower than middle
971 ideal_y
= max (ideal_y
, 0.0);
972 /* Lowest beam of (UP) beam must never be lower than second staffline */
973 ideal_y
= max (ideal_y
, (-staff_space
974 - beam_thickness
+ height_of_my_beams
));
977 ideal_y
-= robust_scm2double (beam
->get_property ("shorten"), 0);
979 SCM bemfl
= ly_assoc_get (ly_symbol2scm ("beamed-extreme-minimum-free-lengths"),
983 = scm_to_double (robust_list_ref (beam_count
- 1, bemfl
))
987 Real minimum_length
= max (minimum_free
, height_of_my_trem
)
989 /* stem only extends to center of beam */
990 - 0.5 * beam_thickness
;
993 Real minimum_y
= note_start
+ minimum_length
;
994 Real shortest_y
= minimum_y
* my_dir
;
996 return scm_list_2 (scm_from_double (ideal_y
),
997 scm_from_double (shortest_y
));
1001 Stem::beam_multiplicity (Grob
*stem
)
1003 SCM beaming
= stem
->get_property ("beaming");
1004 Slice le
= int_list_to_slice (scm_car (beaming
));
1005 Slice ri
= int_list_to_slice (scm_cdr (beaming
));
1011 Stem::is_cross_staff (Grob
*stem
)
1013 Grob
*beam
= unsmob_grob (stem
->get_object ("beam"));
1014 return beam
&& Beam::is_cross_staff (beam
);
1017 MAKE_SCHEME_CALLBACK (Stem
, calc_cross_staff
, 1)
1019 Stem::calc_cross_staff (SCM smob
)
1021 return scm_from_bool (is_cross_staff (unsmob_grob (smob
)));
1024 /* FIXME: Too many properties */
1025 ADD_INTERFACE (Stem
,
1026 "The stem represents the graphical stem. In addition, it"
1027 " internally connects note heads, beams, and tremolos. Rests"
1028 " and whole notes have invisible stems.\n"
1030 "The following properties may be set in the @code{details}"
1034 "@item beamed-lengths\n"
1035 "List of stem lengths given beam multiplicity.\n"
1036 "@item beamed-minimum-free-lengths\n"
1037 "List of normal minimum free stem lengths (chord to beams)"
1038 " given beam multiplicity.\n"
1039 "@item beamed-extreme-minimum-free-lengths\n"
1040 "List of extreme minimum free stem lengths (chord to beams)"
1041 " given beam multiplicity.\n"
1043 "Default stem lengths. The list gives a length for each"
1045 "@item stem-shorten\n"
1046 "How much a stem in a forced direction should be shortened."
1047 " The list gives an amount depending on the number of flags"
1055 "default-direction "
1065 "neutral-direction "
1070 "stem-end-position "
1078 /****************************************************************/
1080 Stem_info::Stem_info ()
1082 ideal_y_
= shortest_y_
= 0;
1087 Stem_info::scale (Real x
)