/*
- stem.cc -- implement Stem
+ This file is part of LilyPond, the GNU music typesetter.
- source file of the GNU LilyPond music typesetter
-
- (c) 1996--2007 Han-Wen Nienhuys <hanwen@xs4all.nl>
+ Copyright (C) 1996--2010 Han-Wen Nienhuys <hanwen@xs4all.nl>
Jan Nieuwenhuizen <janneke@gnu.org>
TODO: This is way too hairy
TODO: fix naming.
Stem-end, chord-start, etc. is all confusing naming.
+
+ LilyPond is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ LilyPond is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with LilyPond. If not, see <http://www.gnu.org/licenses/>.
*/
#include "stem.hh"
MAKE_SCHEME_CALLBACK (Stem, pure_height, 3)
SCM
-Stem::pure_height (SCM smob, SCM start, SCM end)
+Stem::pure_height (SCM smob,
+ SCM /* start */,
+ SCM /* end */)
{
- (void) start;
- (void) end;
-
Grob *me = unsmob_grob (smob);
Interval iv;
if (!is_normal_stem (me))
return ly_interval2scm (iv);
- /* if we are part of a cross-staff beam, return empty */
- if (get_beam (me) && Beam::is_cross_staff (get_beam (me)))
- return ly_interval2scm (iv);
-
Real ss = Staff_symbol_referencer::staff_space (me);
- Real len = scm_to_double (calc_length (smob)) * ss / 2;
- Direction dir = get_grob_direction (me);
+ Real rad = Staff_symbol_referencer::staff_radius (me);
- Interval hp = head_positions (me);
- if (dir == UP)
- iv = Interval (0, len);
- else
- iv = Interval (-len, 0);
+ if (!to_boolean (me->get_property ("cross-staff")))
+ {
+ Real len = scm_to_double (calc_length (smob)) * ss / 2;
+ Direction dir = get_grob_direction (me);
- if (!hp.is_empty ())
- iv.translate (hp[dir] * ss / 2);
+ Interval hp = head_positions (me);
+ if (dir == UP)
+ iv = Interval (0, len);
+ else
+ iv = Interval (-len, 0);
+
+ if (!hp.is_empty ())
+ {
+ iv.translate (hp[dir] * ss / 2);
+ iv.add_point (hp[-dir] * ss / 2);
+ }
+
+ /* extend the stem (away from the head) to cover the staff */
+ if (dir == UP)
+ iv[UP] = max (iv[UP], rad * ss);
+ else
+ iv[DOWN] = min (iv[DOWN], -rad * ss);
+ }
+ else
+ iv = Interval (-rad * ss, rad * ss);
return ly_interval2scm (iv);
}
return me->get_property ("stem-end-position");
}
- Real ss = Staff_symbol_referencer::staff_space (me);
- int durlog = duration_log (me);
vector<Real> a;
/* WARNING: IN HALF SPACES */
Real stem_end = dir ? hp[dir] + dir * length : 0;
/* TODO: change name to extend-stems to staff/center/'() */
- bool no_extend_b = to_boolean (me->get_property ("no-stem-extend"));
- if (!no_extend_b && dir * stem_end < 0)
+ bool no_extend = to_boolean (me->get_property ("no-stem-extend"));
+ if (!no_extend && dir * stem_end < 0)
stem_end = 0.0;
-
- /* Make a little room if we have a upflag and there is a dot.
- previous approach was to lengthen the stem. This is not
- good typesetting practice. */
- if (!get_beam (me) && dir == UP
- && durlog > 2)
- {
- Grob *closest_to_flag = extremal_heads (me)[dir];
- Grob *dots = closest_to_flag
- ? Rhythmic_head::get_dots (closest_to_flag) : 0;
-
- if (dots)
- {
- Real dp = Staff_symbol_referencer::get_position (dots);
- Interval flag_yext = flag (me).extent (Y_AXIS) * (2 / ss) + stem_end;
-
- /* Very gory: add myself to the X-support of the parent,
- which should be a dot-column. */
-
- if (flag_yext.distance (dp) < 0.5)
- {
- Grob *par = dots->get_parent (X_AXIS);
-
- if (Dot_column::has_interface (par))
- {
- Side_position_interface::add_support (par, me);
-
- /* TODO: apply some better logic here. The flag is
- curved inwards, so this will typically be too
- much. */
- }
- }
- }
- }
-
return scm_from_double (stem_end);
}
Real ss = Staff_symbol_referencer::staff_space (me);
Real length = 7;
- SCM s = scm_cdr (scm_assq (ly_symbol2scm ("lengths"), details));
+ SCM s = ly_assoc_get (ly_symbol2scm ("lengths"), details, SCM_EOL);
if (scm_is_pair (s))
length = 2 * scm_to_double (robust_list_ref (durlog - 2, s));
Interval hp = head_positions (me);
if (dir && dir * hp[dir] >= 0)
{
- SCM sshorten = scm_cdr (scm_assq (ly_symbol2scm ("stem-shorten"), details));
+ SCM sshorten = ly_assoc_get (ly_symbol2scm ("stem-shorten"), details, SCM_EOL);
SCM scm_shorten = scm_is_pair (sshorten)
? robust_list_ref (max (duration_log (me) - 2, 0), sshorten) : SCM_EOL;
Real shorten = 2* robust_scm2double (scm_shorten, 0);
return (scm_is_number (s)) ? scm_to_int (s) : 2;
}
-MAKE_SCHEME_CALLBACK(Stem, calc_positioning_done, 1);
+MAKE_SCHEME_CALLBACK (Stem, calc_positioning_done, 1);
SCM
Stem::calc_positioning_done (SCM smob)
{
if (!head_count (me))
return SCM_BOOL_T;
+ me->set_property ("positioning-done", SCM_BOOL_T);
+
extract_grob_set (me, "note-heads", ro_heads);
vector<Grob*> heads (ro_heads);
vector_sort (heads, position_less);
return SCM_BOOL_T;
}
-MAKE_SCHEME_CALLBACK(Stem, calc_direction, 1);
+MAKE_SCHEME_CALLBACK (Stem, calc_direction, 1);
SCM
Stem::calc_direction (SCM smob)
{
return scm_from_int (dir);
}
-MAKE_SCHEME_CALLBACK(Stem, calc_default_direction, 1);
+MAKE_SCHEME_CALLBACK (Stem, calc_default_direction, 1);
SCM
Stem::calc_default_direction (SCM smob)
{
Stem::height (SCM smob)
{
Grob *me = unsmob_grob (smob);
-
+ if (!is_normal_stem (me))
+ return ly_interval2scm (Interval ());
+
Direction dir = get_grob_direction (me);
- /* Trigger callback.
-
- UGH. Should be automatic
- */
Grob *beam = get_beam (me);
if (beam)
{
}
/*
- Can't get_stencil(), since that would cache stencils too early.
+ Can't get_stencil (), since that would cache stencils too early.
This causes problems with beams.
*/
Stencil *stencil = unsmob_stencil (print (smob));
- Interval iv = stencil ? stencil->extent (Y_AXIS) : Interval();
+ Interval iv = stencil ? stencil->extent (Y_AXIS) : Interval ();
if (beam)
{
if (dir == CENTER)
programming_error ("no stem direction");
dir = UP;
}
- iv[dir] += dir * Beam::get_thickness (beam) * 0.5;
+ iv[dir] += dir * Beam::get_beam_thickness (beam) * 0.5;
}
return ly_interval2scm (iv);
return robust_scm2double (me->get_property ("stem-end-position"), 0);
}
-Stencil
-Stem::flag (Grob *me)
+MAKE_SCHEME_CALLBACK (Stem, calc_flag, 1);
+SCM
+Stem::calc_flag (SCM smob)
{
- int log = duration_log (me);
- if (log < 3
- || unsmob_grob (me->get_object ("beam")))
- return Stencil ();
+ Grob *me = unsmob_grob (smob);
+ int log = duration_log (me);
/*
TODO: maybe property stroke-style should take different values,
e.g. "" (i.e. no stroke), "single" and "double" (currently, it's
'() or "grace"). */
string flag_style;
- SCM flag_style_scm = me->get_property ("flag-style");
+ SCM flag_style_scm = me->get_property ("flag-style");
if (scm_is_symbol (flag_style_scm))
flag_style = ly_symbol2string (flag_style_scm);
if (flag_style == "no-flag")
- return Stencil ();
+ return Stencil ().smobbed_copy ();
bool adjust = true;
*/
{
if (adjust)
- {
- int p = (int) (rint (stem_end_position (me)));
- staffline_offs
- = Staff_symbol_referencer::on_line (me, p) ? "0" : "1";
- }
+ {
+ int p = (int) (rint (stem_end_position (me)));
+ staffline_offs
+ = Staff_symbol_referencer::on_line (me, p) ? "0" : "1";
+ }
else
- staffline_offs = "2";
- }
+ staffline_offs = "2";
+ }
else
staffline_offs = "";
{
string stroke_style = ly_scm2string (stroke_style_scm);
if (!stroke_style.empty ())
- {
- string font_char = to_string (dir) + stroke_style;
- Stencil stroke = fm->find_by_name ("flags." + font_char);
- if (stroke.is_empty ())
- me->warning (_f ("flag stroke `%s' not found", font_char));
- else
- flag.add_stencil (stroke);
- }
- }
+ {
+ string font_char = flag_style + to_string (dir) + stroke_style;
+ Stencil stroke = fm->find_by_name ("flags." + font_char);
+ if (stroke.is_empty ())
+ {
+ font_char = to_string (dir) + stroke_style;
+ stroke = fm->find_by_name ("flags." + font_char);
+ }
+ if (stroke.is_empty ())
+ me->warning (_f ("flag stroke `%s' not found", font_char));
+ else
+ flag.add_stencil (stroke);
+ }
+ }
+
+ return flag.smobbed_copy ();
+}
+
+
+Stencil
+Stem::flag (Grob *me)
+{
+ int log = duration_log (me);
+ if (log < 3
+ || unsmob_grob (me->get_object ("beam")))
+ return Stencil ();
- return flag;
+ if (!is_normal_stem (me))
+ return Stencil ();
+
+ // This get_property call already evaluates the scheme function with
+ // the grob passed as argument! Thus, we only have to check if a valid
+ // stencil is returned.
+ SCM flag_style_scm = me->get_property ("flag");
+ if (Stencil *flag = unsmob_stencil (flag_style_scm)) {
+ return *flag;
+ } else {
+ return Stencil ();
+ }
}
MAKE_SCHEME_CALLBACK (Stem, width, 1);
if (!lh && stemlet && !beam)
return SCM_EOL;
+ if (lh && robust_scm2int (lh->get_property ("duration-log"), 0) < 1)
+ return SCM_EOL;
+
if (is_invisible (me))
return SCM_EOL;
else if (stemlet)
{
Real beam_translation = Beam::get_beam_translation (beam);
- Real beam_thickness = Beam::get_thickness (beam);
+ Real beam_thickness = Beam::get_beam_thickness (beam);
int beam_count = beam_multiplicity (me).length () + 1;
y2 -= d
return si;
}
-MAKE_SCHEME_CALLBACK(Stem, calc_stem_info, 1);
+MAKE_SCHEME_CALLBACK (Stem, calc_stem_info, 1);
SCM
Stem::calc_stem_info (SCM smob)
{
}
Real beam_translation = Beam::get_beam_translation (beam);
- Real beam_thickness = Beam::get_thickness (beam);
+ Real beam_thickness = Beam::get_beam_thickness (beam);
int beam_count = Beam::get_direction_beam_count (beam, my_dir);
Real length_fraction
= robust_scm2double (me->get_property ("length-fraction"), 1.0);
/* Simple standard stem length */
SCM details = me->get_property ("details");
- SCM lengths = scm_cdr (scm_assq (ly_symbol2scm ("beamed-lengths"), details));
+ SCM lengths = ly_assoc_get (ly_symbol2scm ("beamed-lengths"), details, SCM_EOL);
Real ideal_length
= scm_to_double (robust_list_ref (beam_count - 1, lengths))
- 0.5 * beam_thickness;
/* Condition: sane minimum free stem length (chord to beams) */
- lengths = scm_cdr (scm_assq (ly_symbol2scm ("beamed-minimum-free-lengths"), details));
+ lengths = ly_assoc_get (ly_symbol2scm ("beamed-minimum-free-lengths"), details, SCM_EOL);
Real ideal_minimum_free
= scm_to_double (robust_list_ref (beam_count - 1, lengths))
Real height_of_my_trem = 0.0;
Grob *trem = unsmob_grob (me->get_object ("tremolo-flag"));
if (trem)
- height_of_my_trem = trem->extent (trem, Y_AXIS).length ()
+ {
+ height_of_my_trem
+ = Stem_tremolo::vertical_length (trem)
/* hack a bit of space around the trem. */
+ beam_translation;
+ }
+
/* UGH
It seems that also for ideal minimum length, we must use
the maximum beam count (for this direction):
Obviously not for grace beams.
Also, not for knees. Seems to be a good thing. */
- bool no_extend_b = to_boolean (me->get_property ("no-stem-extend"));
+ bool no_extend = to_boolean (me->get_property ("no-stem-extend"));
bool is_knee = to_boolean (beam->get_property ("knee"));
- if (!no_extend_b && !is_knee)
+ if (!no_extend && !is_knee)
{
/* Highest beam of (UP) beam must never be lower than middle
staffline */
ideal_y -= robust_scm2double (beam->get_property ("shorten"), 0);
- SCM bemfl = scm_cdr (scm_assq (ly_symbol2scm ("beamed-extreme-minimum-free-lengths"),
- details));
+ SCM bemfl = ly_assoc_get (ly_symbol2scm ("beamed-extreme-minimum-free-lengths"),
+ details, SCM_EOL);
Real minimum_free
= scm_to_double (robust_list_ref (beam_count - 1, bemfl))
return beam && Beam::is_cross_staff (beam);
}
-MAKE_SCHEME_CALLBACK (Stem, cross_staff, 1)
+MAKE_SCHEME_CALLBACK (Stem, calc_cross_staff, 1)
SCM
-Stem::cross_staff (SCM smob)
+Stem::calc_cross_staff (SCM smob)
{
return scm_from_bool (is_cross_staff (unsmob_grob (smob)));
}
/* FIXME: Too many properties */
ADD_INTERFACE (Stem,
- "The stem represent the graphical stem. "
- "In addition, it internally connects note heads, beams and"
- "tremolos. "
- "Rests and whole notes have invisible stems."
-
- "\n\nThe following properties may be set in the details list."
+ "The stem represents the graphical stem. In addition, it"
+ " internally connects note heads, beams, and tremolos. Rests"
+ " and whole notes have invisible stems.\n"
+ "\n"
+ "The following properties may be set in the @code{details}"
+ " list.\n"
+ "\n"
"@table @code\n"
- "@item beamed-lengths \n"
- "list of stem lengths given beam multiplicity. \n"
- "@item beamed-minimum-free-lengths \n"
- "list of normal minimum free stem lengths (chord to beams) given beam multiplicity.\n"
+ "@item beamed-lengths\n"
+ "List of stem lengths given beam multiplicity.\n"
+ "@item beamed-minimum-free-lengths\n"
+ "List of normal minimum free stem lengths (chord to beams)"
+ " given beam multiplicity.\n"
"@item beamed-extreme-minimum-free-lengths\n"
- "list of extreme minimum free stem lengths (chord to beams) given beam multiplicity.\n"
+ "List of extreme minimum free stem lengths (chord to beams)"
+ " given beam multiplicity.\n"
"@item lengths\n"
- "Default stem lengths. The list gives a length for each flag-count.\n"
+ "Default stem lengths. The list gives a length for each"
+ " flag count.\n"
"@item stem-shorten\n"
- "How much a stem in a forced direction "
- "should be shortened. The list gives an amount depending on the number "
- "of flags/beams."
- "@end table\n"
- ,
+ "How much a stem in a forced direction should be shortened."
+ " The list gives an amount depending on the number of flags"
+ " and beams.\n"
+ "@end table\n",
/* properties */
"avoid-note-head "
"beam "
"beaming "
+ "beamlet-default-length "
+ "beamlet-max-length-proportion "
"default-direction "
"details "
"direction "
"duration-log "
+ "flag "
"flag-style "
"french-beaming "
"length "