source file of the GNU LilyPond music typesetter
- (c) 1996--2002 Han-Wen Nienhuys <hanwen@cs.uu.nl>
+ (c) 1996--2004 Han-Wen Nienhuys <hanwen@cs.uu.nl>
Jan Nieuwenhuizen <janneke@gnu.org>
TODO: This is way too hairy
#include "spanner.hh"
#include "side-position-interface.hh"
#include "dot-column.hh"
+#include "stem-tremolo.hh"
void
Stem::set_beaming (Grob*me, int beam_count, Direction d)
Direction
Stem::get_direction (Grob*me)
{
- Direction d = Directional_element_interface::get (me);
+ Direction d = get_grob_direction (me);
if (!d)
{
d = get_default_dir (me);
// urg, AAARGH!
- Directional_element_interface::set (me, d);
+ set_grob_direction (me, d);
}
return d ;
}
Grob*
Stem::support_head (Grob*me)
{
- SCM h = me->get_grob_property ("support-head");
- Grob * nh = unsmob_grob (h);
- if (nh)
- return nh;
- else if (head_count (me) == 1)
+ if (head_count (me) == 1)
{
/*
UGH.
ps.push (p);
}
-
+
ps.sort (icmp);
return ps;
}
n->set_grob_property ("stem", me->self_scm ());
n->add_dependency (me);
+ /*
+ TODO: why not store Rest pointers?
+ */
if (Note_head::has_interface (n))
{
Pointer_group_interface::add_grob (me, ly_symbol2scm ("note-heads"), n);
{
int staff_center = 0;
Interval hp = head_positions (me);
- if (hp.empty_b())
+ if (hp.is_empty ())
{
return CENTER;
}
Real
Stem::get_default_stem_end_position (Grob*me)
{
- SCM up_to_staff = me->get_grob_property ("up-to-staff");
- if (to_boolean(up_to_staff))
- {
- int line_count = Staff_symbol_referencer::line_count (me);
-
- Direction dir = get_direction (me);
+ Real ss = Staff_symbol_referencer::staff_space (me);
+
+ int durlog = duration_log (me);
- return dir* (line_count + 3.5);
- }
-
- bool grace_b = to_boolean (me->get_grob_property ("grace"));
SCM s;
Array<Real> a;
- Real length_f = 3.5;
+
+ Real length = 7; // WARNING: IN HALF SPACES
SCM scm_len = me->get_grob_property ("length");
if (gh_number_p (scm_len))
{
- length_f = gh_scm2double (scm_len);
+ length = gh_scm2double (scm_len);
}
else
{
s = me->get_grob_property ("lengths");
if (gh_pair_p (s))
{
- length_f = 2* gh_scm2double (robust_list_ref (duration_log(me) -2, s));
+ length = 2* gh_scm2double (robust_list_ref (durlog -2, s));
}
}
- Real shorten_f = 0.0;
-
- SCM sshorten = me->get_grob_property ("stem-shorten");
- if (gh_pair_p (sshorten))
- {
- shorten_f = 2* gh_scm2double (robust_list_ref ((duration_log (me) - 2) >? 0, sshorten));
- }
- /* On boundary: shorten only half */
- if (abs (chord_start_y (me)) == 0.5)
- shorten_f *= 0.5;
/* URGURGURG
'set-default-stemlen' sets direction too
if (!dir)
{
dir = get_default_dir (me);
- Directional_element_interface::set (me, dir);
+ set_grob_direction (me, dir);
}
-
+
+
/* stems in unnatural (forced) direction should be shortened,
according to [Roush & Gourlay] */
- if (chord_start_y (me)
- && (get_direction (me) != get_default_dir (me)))
- length_f -= shorten_f;
+ if (!chord_start_y (me)
+ || (get_direction (me) != get_default_dir (me)))
+ {
+
+
+ SCM sshorten = me->get_grob_property ("stem-shorten");
+ SCM scm_shorten = gh_pair_p (sshorten) ?
+ robust_list_ref ((duration_log (me) - 2) >? 0, sshorten): SCM_EOL;
+ Real shorten = 2* robust_scm2double (scm_shorten,0);
+
+
+ /* On boundary: shorten only half */
+ if (abs (head_positions (me)[get_direction (me)]) <= 1)
+ shorten *= 0.5;
+
+ length -= shorten;
+ }
+
+ /*
+ Tremolo stuff:
+ */
+ Grob * trem = unsmob_grob (me->get_grob_property ("tremolo-flag"));
+ if (trem && !unsmob_grob (me->get_grob_property ("beam")))
+ {
+ /*
+ Crude hack: add extra space if tremolo flag is there.
+
+ We can't do this for the beam, since we get into a loop
+ (Stem_tremolo::raw_molecule() looks at the beam.)
+
+ --hwn
+ */
+
+ Real minlen =
+ 1.0 + 2 * Stem_tremolo::raw_molecule (trem).extent (Y_AXIS).length () / ss;
+
+ if (durlog >= 3)
+ {
+ Interval flag_ext = flag (me).extent (Y_AXIS) ;
+ if (!flag_ext.is_empty ())
+ minlen += 2 * flag_ext.length () / ss ;
+ /*
+ The clash is smaller for down stems (since the tremolo is
+ angled up.)
+ */
+ if (dir == DOWN)
+ minlen -= 1.0;
+ }
+
+ length = length >? (minlen + 1.0);
+ }
+
Interval hp = head_positions (me);
- Real st = hp[dir] + dir * length_f;
+ Real st = hp[dir] + dir * length;
+ /*
+ TODO: change name to extend-stems to staff/center/'()
+ */
bool no_extend_b = to_boolean (me->get_grob_property ("no-stem-extend"));
- if (!grace_b && !no_extend_b && dir * st < 0) // junkme?
+ if (!no_extend_b && dir * st < 0) // junkme?
st = 0.0;
/*
*/
if (!get_beam (me) && dir == UP
- && duration_log (me) > 2)
+ && durlog > 2)
{
Grob * closest_to_flag = extremal_heads (me)[dir];
Grob * dots = closest_to_flag
{
Real dp = Staff_symbol_referencer::get_position (dots);
Real flagy = flag (me).extent (Y_AXIS)[-dir] * 2
- / Staff_symbol_referencer::staff_space (me);
+ / ss;
/*
Very gory: add myself to the X-support of the parent,
heads.reverse ();
- bool invisible = invisible_b (me);
- Real thick = 0.0;
- if (invisible)
- thick = gh_scm2double (me->get_grob_property ("thickness"))
- * me->get_paper ()->get_var ("linethickness");
+ Real thick = thickness (me);
-
Grob *hed = support_head (me);
Real w = Note_head::head_extent (hed,X_AXIS)[dir];
for (int i=0; i < heads.size (); i++)
X_AXIS);
}
- bool parity= true; // todo: make me settable.
- int lastpos = int (Staff_symbol_referencer::get_position (heads[0]));
+ bool parity= true;
+ Real lastpos = Real (Staff_symbol_referencer::get_position (heads[0]));
for (int i=1; i < heads.size (); i ++)
{
Real p = Staff_symbol_referencer::get_position (heads[i]);
- int dy =abs (lastpos- (int)p);
+ Real dy =fabs (lastpos- p);
- if (dy <= 1)
+ /*
+ dy should always be 0.5, 0.0, 1.0, but provide safety margin
+ for rounding errors.
+ */
+ if (dy < 1.1)
{
if (parity)
{
Real l = Note_head::head_extent (heads[i], X_AXIS).length ();
Direction d = get_direction (me);
- heads[i]->translate_axis (l * d, X_AXIS);
+ /*
+ Reversed head should be shifted l-thickness, but this
+ looks too crowded, so we only shift l-0.5*thickness.
+
+ This leads to assymetry: Normal heads overlap the
+ stem 100% whereas reversed heads only overlaps the
+ stem 50%
+
+ */
+
+ Real reverse_overlap =0.5;
+ heads[i]->translate_axis ((l-thick*reverse_overlap) * d, X_AXIS);
if (invisible_b(me))
- heads[i]->translate_axis (-thick *2* d , X_AXIS);
+ heads[i]->translate_axis (-thick*(2 - reverse_overlap) * d , X_AXIS);
/* TODO:
Interval iv;
if (mol != SCM_EOL)
iv = unsmob_molecule (mol)->extent (a);
+ if (Grob *b =get_beam (me))
+ {
+ Direction d = get_direction (me);
+ iv[d] += d * Beam::get_thickness (b) /2.0 ;
+ }
+
return ly_interval2scm (iv);
}
Molecule
Stem::flag (Grob*me)
{
- /* TODO: rename flag-style into something more appropriate,
- e.g. "stroke-style", maybe with values "" (i.e. no stroke),
- "single" and "double". Needs more discussion.
- */
- String style, fstyle, staffline_offs;
- SCM fst = me->get_grob_property ("flag-style");
- if (gh_string_p (fst))
+ /* 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_grob_property ("flag-style");
+ if (gh_symbol_p (flag_style_scm))
{
- fstyle = ly_scm2string (fst);
+ flag_style = ly_symbol2string (flag_style_scm);
}
- SCM st = me->get_grob_property ("style");
- if (gh_symbol_p (st))
- {
- style = (ly_scm2string (scm_symbol_to_string (st)));
- }
- else
+ if (flag_style == "no-flag")
{
- style = "";
+ return Molecule ();
}
+
bool adjust = to_boolean (me->get_grob_property ("adjust-if-on-staffline"));
- if (String::compare (style, "mensural") == 0)
+ String staffline_offs;
+ if (String::compare (flag_style, "mensural") == 0)
/* Mensural notation: For notes on staff lines, use different
flags than for notes between staff lines. The idea is that
flags are always vertically aligned with the staff lines,
flag's shape accordingly. In the worst case, the shape
looks slightly misplaced, but that will usually be the
programmer's fault (e.g. when trying to attach multiple
- note heads to a single stem in mensural notation). */
+ note heads to a single stem in mensural notation).
+ */
/*
perhaps the detection whether this correction is needed should
--hwn.
*/
- Grob *first = first_head(me);
- int sz = Staff_symbol_referencer::line_count (me)-1;
- int p = (int)rint (Staff_symbol_referencer::get_position (first));
- staffline_offs = (((p ^ sz) & 0x1) == 0) ? "1" : "0";
+ int p = (int)rint (Staff_symbol_referencer::get_position (first_head (me)));
+ staffline_offs = Staff_symbol_referencer::on_staffline (me, p) ?
+ "1" : "0";
}
else
{
{
staffline_offs = "";
}
- char c = (get_direction (me) == UP) ? 'u' : 'd';
- String index_string
- = String ("flags-") + style + to_string (c) + staffline_offs + to_string (duration_log (me));
- Molecule m
- = Font_interface::get_default_font (me)->find_by_name (index_string);
- if (!fstyle.empty_b ())
- m.add_molecule (Font_interface::get_default_font (me)->find_by_name (String ("flags-") + to_string (c) + fstyle));
- return m;
+
+ char dir = (get_direction (me) == UP) ? 'u' : 'd';
+ String font_char =
+ flag_style + to_string (dir) + staffline_offs + to_string (duration_log (me));
+ Font_metric *fm = Font_interface::get_default_font (me);
+ Molecule flag = fm->find_by_name ("flags-" + font_char);
+ if (flag.is_empty ())
+ {
+ me->warning (_f ("flag `%s' not found", font_char));
+ }
+
+ SCM stroke_style_scm = me->get_grob_property ("stroke-style");
+ if (gh_string_p (stroke_style_scm))
+ {
+ String stroke_style = ly_scm2string (stroke_style_scm);
+ if (!stroke_style.is_empty ())
+ {
+ String font_char = to_string (dir) + stroke_style;
+ Molecule 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_molecule (stroke);
+ }
+ }
+ }
+
+ return flag;
}
MAKE_SCHEME_CALLBACK (Stem,dim_callback,2);
{
Axis a = (Axis) gh_scm2int (ax);
assert (a == X_AXIS);
- Grob *se = unsmob_grob (e);
+ Grob *me = unsmob_grob (e);
Interval r (0, 0);
- if (unsmob_grob (se->get_grob_property ("beam")) || abs (duration_log (se)) <= 2)
+ if (unsmob_grob (me->get_grob_property ("beam")) || abs (duration_log (me)) <= 2)
; // TODO!
else
{
- r = flag (se).extent (X_AXIS);
+ r = flag (me).extent (X_AXIS)
+ + thickness (me)/2;
}
return ly_interval2scm (r);
}
-
+Real
+Stem::thickness (Grob* me)
+{
+ return gh_scm2double (me->get_grob_property ("thickness"))
+ * Staff_symbol_referencer::line_thickness (me);
+}
MAKE_SCHEME_CALLBACK (Stem,brew_molecule,1);
Grob*me = unsmob_grob (smob);
Molecule mol;
Direction d = get_direction (me);
-
-
- Real y1;
-
/*
- This is required to avoid stems passing in tablature chords...
- */
+ TODO: make the stem start a direction ?
-
- /*
- TODO: make the stem start a direction ?
+ This is required to avoid stems passing in tablature chords...
*/
-
+ Grob *lh = to_boolean (me->get_grob_property ("avoid-note-head"))
+ ? last_head (me) : lh = first_head (me);
+ if (!lh)
+ return SCM_EOL;
+
+ if (invisible_b (me))
+ return SCM_EOL;
- if (to_boolean (me->get_grob_property ("avoid-note-head")))
- {
- Grob * lh = last_head (me);
- if (!lh)
- return SCM_EOL;
- y1 = Staff_symbol_referencer::get_position (lh);
- }
- else
- {
- Grob * lh = first_head (me);
- if (!lh)
- return SCM_EOL;
- y1 = Staff_symbol_referencer::get_position (lh);
- }
-
+ Real y1 = Staff_symbol_referencer::get_position (lh);
Real y2 = stem_end_position (me);
Interval stem_y (y1 <? y2,y2 >? y1);
-
+
// dy?
Real dy = Staff_symbol_referencer::staff_space (me) * 0.5;
must not take ledgers into account.
*/
Interval head_height = Note_head::head_extent (hed,Y_AXIS);
- Real y_attach = Note_head::stem_attachment_coordinate ( hed, Y_AXIS);
+ Real y_attach = Note_head::stem_attachment_coordinate (hed, Y_AXIS);
y_attach = head_height.linear_combination (y_attach);
stem_y[Direction (-d)] += d * y_attach/dy;
}
+
- if (!invisible_b (me))
- {
- Real stem_width = gh_scm2double (me->get_grob_property ("thickness"))
- // URG
- * me->get_paper ()->get_var ("linethickness");
-
- Molecule ss =Lookup::filledbox (Box (Interval (-stem_width/2, stem_width/2),
- Interval (stem_y[DOWN]*dy, stem_y[UP]*dy)));
- mol.add_molecule (ss);
- }
+ // URG
+ Real stem_width = thickness (me);
+ Real blot =
+ me->get_paper ()->get_realvar (ly_symbol2scm ("blotdiameter"));
+
+ Box b = Box (Interval (-stem_width/2, stem_width/2),
+ Interval (stem_y[DOWN]*dy, stem_y[UP]*dy));
+
+ Molecule ss = Lookup::round_filled_box (b, blot);
+ mol.add_molecule (ss);
if (!get_beam (me) && abs (duration_log (me)) > 2)
{
Molecule fl = flag (me);
- fl.translate_axis (stem_y[d]*dy, Y_AXIS);
+ fl.translate_axis (stem_y[d]*dy - d * blot/2, Y_AXIS);
+ fl.translate_axis (stem_width/2, X_AXIS);
mol.add_molecule (fl);
}
if (Grob * f = first_head (me))
{
Interval head_wid = Note_head::head_extent(f, X_AXIS);
-
Real attach =0.0;
if (attach)
{
Real rule_thick
- = gh_scm2double (me->get_grob_property ("thickness"))
- * me->get_paper ()->get_var ("linethickness");
-
+ = thickness (me);
r += - d * rule_thick * 0.5;
}
return gh_double2scm (r);
}
+
Grob*
Stem::get_beam (Grob*me)
{
Stem_info
Stem::get_stem_info (Grob *me)
{
- /* DOCME!!! So, what's this all about? */
- SCM up_to_staff = me->get_grob_property ("up-to-staff");
- if (gh_scm2bool(up_to_staff))
- {
- /* Up-to-staff : the stem end out of the staff. */
-
- /* FIXME: duplicate code. */
- int line_count = Staff_symbol_referencer::line_count (me);
- Stem_info si ;
- Direction dir = get_direction (me);
-
- si.ideal_y_ = dir* (line_count + 1.5);
- si.dir_ = dir;
- si.shortest_y_ = si.ideal_y_;
- return si;
- }
-
-
/* Return cached info if available */
SCM scm_info = me->get_grob_property ("stem-info");
if (!gh_pair_p (scm_info))
}
Stem_info si;
- si.dir_ = Directional_element_interface::get (me);
+ si.dir_ = get_grob_direction (me);
si.ideal_y_ = gh_scm2double (gh_car (scm_info));
si.shortest_y_ = gh_scm2double (gh_cadr (scm_info));
return si;
}
+
+/*
+ TODO: add extra space for tremolos!
+ */
void
Stem::calc_stem_info (Grob *me)
{
- Direction my_dir = Directional_element_interface::get (me);
+ Direction my_dir = get_grob_direction (me);
Real staff_space = Staff_symbol_referencer::staff_space (me);
Grob *beam = get_beam (me);
Real beam_translation = Beam::get_beam_translation (beam);
- Real beam_thickness = gh_scm2double (beam->get_grob_property ("thickness"));
+ Real beam_thickness = Beam::get_thickness (beam);
int beam_count = Beam::get_direction_beam_count (beam, my_dir);
/* Simple standard stem length */
+ SCM lengths = me->get_grob_property ("beamed-lengths");
Real ideal_length =
- gh_scm2double (robust_list_ref
- (beam_count - 1,
- me->get_grob_property ("beamed-lengths")))
+ gh_scm2double (robust_list_ref (beam_count - 1,lengths))
+
* staff_space
/* stem only extends to center of beam */
- 0.5 * beam_thickness;
-
/* Condition: sane minimum free stem length (chord to beams) */
+ lengths = me->get_grob_property ("beamed-minimum-free-lengths");
Real ideal_minimum_free =
- gh_scm2double (robust_list_ref
- (beam_count - 1,
- me->get_grob_property ("beamed-minimum-free-lengths")))
+ gh_scm2double (robust_list_ref (beam_count - 1, lengths))
* staff_space;
- int my_beam_count = Stem::beam_multiplicity (me).length () + 1;
+
+ /* UGH
+ It seems that also for ideal minimum length, we must use
+ the maximum beam count (for this direction):
+
+ \score{ \notes\relative c''{ [a8 a32] }}
+
+ must be horizontal. */
Real height_of_my_beams = beam_thickness
- + (my_beam_count - 1) * beam_translation;
-
+ + (beam_count - 1) * beam_translation;
+
Real ideal_minimum_length = ideal_minimum_free
+ height_of_my_beams
/* stem only extends to center of beam */
Real note_start =
/* staff positions */
head_positions (me)[my_dir] * 0.5
- * my_dir;
+ * my_dir * staff_space;
Real ideal_y = note_start + ideal_length;
Obviously not for grace beams.
Also, not for knees. Seems to be a good thing. */
- SCM grace = me->get_grob_property ("grace");
- bool grace_b = to_boolean (grace);
bool no_extend_b = to_boolean (me->get_grob_property ("no-stem-extend"));
bool knee_b = to_boolean (beam->get_grob_property ("knee"));
- if (!grace_b && !no_extend_b && !knee_b)
+ if (!no_extend_b && !knee_b)
{
/* Highest beam of (UP) beam must never be lower than middle
staffline */
}
- SCM shorten = beam->get_grob_property ("shorten");
- if (gh_number_p (shorten))
- ideal_y -= gh_scm2double (shorten);
-
+ ideal_y -= robust_scm2double (beam->get_grob_property ("shorten"), 0);
Real minimum_free =
gh_scm2double (robust_list_ref
}
+/*
+ these are too many props.
+ */
ADD_INTERFACE (Stem,"stem-interface",
- "A stem",
- "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 style no-stem-extend flag-style");
+ "A stem",
+ "tremolo-flag french-beaming "
+ "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 "
+ "note-heads direction length flag-style "
+ "no-stem-extend stroke-style");
+
+/****************************************************************/
+
+Stem_info::Stem_info()
+{
+ ideal_y_ = shortest_y_ =0;
+ dir_ = CENTER;
+}
+
+void
+Stem_info::scale (Real x)
+{
+ ideal_y_ *= x;
+ shortest_y_ *= x;
+}