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
+
+ TODO: fix naming.
+
+ Stem-end, chord-start, etc. is all confusing naming.
*/
-#include <math.h> // m_pi
+#include <math.h> // rint
#include "lookup.hh"
#include "directional-element-interface.hh"
#include "note-head.hh"
#include "stem.hh"
-#include "debug.hh"
+#include "warn.hh"
#include "paper-def.hh"
#include "rhythmic-head.hh"
#include "font-interface.hh"
#include "staff-symbol-referencer.hh"
#include "spanner.hh"
#include "side-position-interface.hh"
+#include "dot-column.hh"
+#include "stem-tremolo.hh"
void
-Stem::set_beaming (Grob*me ,int i, Direction d)
+Stem::set_beaming (Grob*me, int beam_count, Direction d)
{
SCM pair = me->get_grob_property ("beaming");
if (!gh_pair_p (pair))
{
- pair = gh_cons (gh_int2scm (0),gh_int2scm (0));
- me-> set_grob_property ("beaming", pair);
+ pair = gh_cons (SCM_EOL, SCM_EOL);
+ me->set_grob_property ("beaming", pair);
}
- index_set_cell (pair, d, gh_int2scm (i));
-}
-int
-Stem::beam_count (Grob*me,Direction d)
-{
- SCM p=me->get_grob_property ("beaming");
- if (gh_pair_p (p))
- return gh_scm2int (index_cell (p,d));
- else
- return 0;
+ SCM l = index_get_cell (pair, d);
+ for( int i = 0; i< beam_count; i++)
+ {
+ l = gh_cons (gh_int2scm (i), l);
+ }
+ index_set_cell (pair, d, l);
}
+
Interval
Stem::head_positions (Grob*me)
{
- if (!heads_i (me))
+ if (!head_count (me))
{
Interval iv;
return iv;
Drul_array<Grob*> e (extremal_heads (me));
- return Interval (Staff_symbol_referencer::position_f (e[DOWN]),
- Staff_symbol_referencer::position_f (e[UP]));
+ return Interval (Staff_symbol_referencer::get_position (e[DOWN]),
+ Staff_symbol_referencer::get_position (e[UP]));
}
Real
-Stem::chord_start_f (Grob*me)
+Stem::chord_start_y (Grob*me)
{
return head_positions (me)[get_direction (me)]
* Staff_symbol_referencer::staff_space (me)/2.0;
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 ;
}
me->set_grob_property ("stem-end-position", gh_double2scm (se));
}
-int
-Stem::type_i (Grob*me)
-{
- return first_head (me) ? Rhythmic_head::balltype_i (first_head (me)) : 2;
-}
/*
Note head that determines hshift for upstems
- */
+
+ WARNING: triggers direction
+*/
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 (heads_i (me) == 1)
+ if (head_count (me) == 1)
{
/*
UGH.
*/
- return unsmob_grob (ly_car (me->get_grob_property ("heads")));
+ return unsmob_grob (ly_car (me->get_grob_property ("note-heads")));
}
else
return first_head (me);
int
-Stem::heads_i (Grob*me)
+Stem::head_count (Grob*me)
{
- return Pointer_group_interface::count (me, "heads");
+ return Pointer_group_interface::count (me, "note-heads");
}
/*
The note head which forms one end of the stem.
- */
+
+ WARNING: triggers direction
+*/
Grob*
Stem::first_head (Grob*me)
{
- return extremal_heads (me)[-get_direction (me)];
+ Direction d = get_direction (me);
+ if (!d)
+ return 0;
+ return extremal_heads (me)[-d];
+}
+
+/*
+ The note head opposite to the first head.
+ */
+Grob*
+Stem::last_head (Grob*me)
+{
+ Direction d = get_direction (me);
+ if (!d)
+ return 0;
+ return extremal_heads (me)[d];
}
/*
Drul_array<Grob *> exthead;
exthead[LEFT] = exthead[RIGHT] =0;
- for (SCM s = me->get_grob_property ("heads"); gh_pair_p (s); s = ly_cdr (s))
+ for (SCM s = me->get_grob_property ("note-heads"); gh_pair_p (s); s = ly_cdr (s))
{
Grob * n = unsmob_grob (ly_car (s));
- int p = int (Staff_symbol_referencer::position_f (n));
+ int p = int (Staff_symbol_referencer::get_position (n));
Direction d = LEFT;
do {
Stem::note_head_positions (Grob *me)
{
Array<int> ps ;
- for (SCM s = me->get_grob_property ("heads"); gh_pair_p (s); s = ly_cdr (s))
+ for (SCM s = me->get_grob_property ("note-heads"); gh_pair_p (s); s = ly_cdr (s))
{
Grob * n = unsmob_grob (ly_car (s));
- int p = int (Staff_symbol_referencer::position_f (n));
+ int p = int (Staff_symbol_referencer::get_position (n));
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 ("heads"), n);
- }
- else
- {
- n->set_grob_property ("rest", n->self_scm ());
+ Pointer_group_interface::add_grob (me, ly_symbol2scm ("note-heads"), n);
}
}
bool
Stem::invisible_b (Grob*me)
{
- return ! (heads_i (me) && Rhythmic_head::balltype_i (support_head (me)) >= 1);
-}
-
-int
-Stem::get_center_distance (Grob*me, Direction d)
-{
- int staff_center = 0;
- int distance = (int) (d* (head_positions (me)[d] - staff_center));
- return distance >? 0;
+ return ! (head_count (me)
+ && gh_scm2int (me->get_grob_property ("duration-log")) >= 1);
}
Direction
Stem::get_default_dir (Grob*me)
{
- int du = get_center_distance (me,UP);
- int dd = get_center_distance (me,DOWN);
-
- if (sign (dd - du))
- return Direction (sign (dd -du));
+ int staff_center = 0;
+ Interval hp = head_positions (me);
+ if (hp.is_empty ())
+ {
+ return CENTER;
+ }
+
+ int udistance = (int) (UP * hp[UP] - staff_center);
+ int ddistance = (int) (DOWN* hp[DOWN] - staff_center);
+
+ if (sign (ddistance - udistance))
+ return Direction (sign (ddistance -udistance));
return to_dir (me->get_grob_property ("neutral-direction"));
}
Real
Stem::get_default_stem_end_position (Grob*me)
{
- bool grace_b = to_boolean (me->get_grob_property ("grace"));
+ Real ss = Staff_symbol_referencer::staff_space (me);
+
+ int durlog = duration_log (me);
+
SCM s;
Array<Real> a;
- Real length_f = 0.;
+
+ 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");
- for (SCM q = s; q != SCM_EOL; q = ly_cdr (q))
- a.push (gh_scm2double (ly_car (q)));
-
- // stem uses half-spaces
- length_f = a[ ((flag_i (me) - 2) >? 0) <? (a.size () - 1)] * 2;
+ if (gh_pair_p (s))
+ {
+ length = 2* gh_scm2double (robust_list_ref (durlog -2, s));
+ }
}
- a.clear ();
- s = me->get_grob_property ("stem-shorten");
- for (SCM q = s; gh_pair_p (q); q = ly_cdr (q))
- a.push (gh_scm2double (ly_car (q)));
-
-
- // stem uses half-spaces
-
- // fixme: use scm_list_n_ref () iso. array[]
- Real shorten_f = a[ ((flag_i (me) - 2) >? 0) <? (a.size () - 1)] * 2;
/* 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 (( (int)chord_start_f (me))
- && (get_direction (me) != get_default_dir (me)))
- length_f -= shorten_f;
-
- Interval hp = head_positions (me);
- Real st = hp[dir] + dir * length_f;
+ /* stems in unnatural (forced) direction should be shortened,
+ according to [Roush & Gourlay] */
+ 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;
+ }
/*
- Make a little room if we have a flag and there is a dot.
+ 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.
- TODO:
+ 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 ;
- maybe we should consider moving the dot to the right?
+ /*
+ 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;
+
+ /*
+ TODO: change name to extend-stems to staff/center/'()
*/
- if (!beam_l (me)
- && flag_i (me))
+ bool no_extend_b = to_boolean (me->get_grob_property ("no-stem-extend"));
+ if (!no_extend_b && dir * st < 0) // junkme?
+ st = 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::dots_l (closest_to_flag ) : 0;
+ ? Rhythmic_head::get_dots (closest_to_flag ) : 0;
if (dots)
{
- Real dp = Staff_symbol_referencer::position_f (dots);
- Real flagy = flag (me).extent (Y_AXIS)[-dir] * 2; // should divide by staffspace
+ Real dp = Staff_symbol_referencer::get_position (dots);
+ Real flagy = flag (me).extent (Y_AXIS)[-dir] * 2
+ / ss;
/*
Very gory: add myself to the X-support of the parent,
which should be a dot-column.
*/
if (dir * (st + flagy - dp) < 0.5)
- Side_position_interface::add_support (dots->get_parent (X_AXIS), me);
-
- /*
- previous approach was to lengthen the stem. This is not
- good typesetting practice. */
+ {
+ 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.
+ */
+ }
+ }
}
}
- bool no_extend_b = to_boolean (me->get_grob_property ("no-stem-extend"));
- if (!grace_b && !no_extend_b && dir * st < 0) // junkme?
- st = 0.0;
-
return st;
}
/*
- Number of hooks on the flag, ie. the log of the duration.
+
+ the log of the duration (Number of hooks on the flag minus two)
*/
int
-Stem::flag_i (Grob*me)
+Stem::duration_log (Grob*me)
{
SCM s = me->get_grob_property ("duration-log");
return (gh_number_p (s)) ? gh_scm2int (s) : 2;
void
Stem::position_noteheads (Grob*me)
{
- if (!heads_i (me))
+ if (!head_count (me))
return;
Link_array<Grob> heads =
- Pointer_group_interface__extract_grobs (me, (Grob*)0, "heads");
+ Pointer_group_interface__extract_grobs (me, (Grob*)0, "note-heads");
heads.sort (compare_position);
Direction dir =get_direction (me);
heads.reverse ();
+ 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::position_f (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::position_f (heads[i]);
- int dy =abs (lastpos- (int)p);
+ Real p = Staff_symbol_referencer::get_position (heads[i]);
+ 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 ();
- heads[i]->translate_axis (l * get_direction (me), X_AXIS);
+ Direction d = get_direction (me);
+ /*
+ 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 - reverse_overlap) * d , X_AXIS);
+
+
+ /* TODO:
+
+ For some cases we should kern some more: when the
+ distance between the next or prev note is too large, we'd
+ get large white gaps, eg.
+
+ |
+ X|
+ |X <- kern this.
+ |
+ X
+
+ */
}
parity = !parity;
}
Stem::before_line_breaking (SCM smob)
{
Grob*me = unsmob_grob (smob);
- stem_end_position (me); // ugh. Trigger direction calc.
- position_noteheads (me);
- if (invisible_b (me))
+
+ /*
+ Do the calculations for visible stems, but also for invisible stems
+ with note heads (i.e. half notes.)
+ */
+ if (head_count (me))
+ {
+ stem_end_position (me); // ugh. Trigger direction calc.
+ position_noteheads (me);
+ }
+ else
{
- me->remove_grob_property ("molecule-callback");
- // suicide ();
+ me->set_grob_property ("molecule-callback", SCM_EOL);
}
return SCM_UNSPECIFIED;
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_i (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). */
- Grob *first = first_head(me);
- int sz = Staff_symbol_referencer::line_count (me)-1;
- int p = (int)rint (Staff_symbol_referencer::position_f (first));
- staffline_offs = (((p ^ sz) & 0x1) == 0) ? "1" : "0";
+ note heads to a single stem in mensural notation).
+ */
+
+ /*
+ perhaps the detection whether this correction is needed should
+ happen in a different place to avoid the recursion.
+
+ --hwn.
+ */
+ 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_str
- = String ("flags-") + style + to_str (c) + staffline_offs + to_str (flag_i (me));
- Molecule m
- = Font_interface::get_default_font (me)->find_by_name (index_str);
- if (!fstyle.empty_b ())
- m.add_molecule (Font_interface::get_default_font (me)->find_by_name (String ("flags-") + to_str (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 (flag_i (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);
+
+ /*
+ 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;
-
- Real y1 = Staff_symbol_referencer::position_f (first_head (me));
+ 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->paper_l ()->get_var ("stafflinethickness");
-
- 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));
- if (!beam_l (me) && abs (flag_i (me)) > 2)
+ 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);
}
Grob *me = unsmob_grob (element_smob);
Real r=0;
+
+ if (head_count (me) == 0)
+ {
+ return gh_double2scm (0.0);
+ }
+
if (Grob * f = first_head (me))
{
Interval head_wid = Note_head::head_extent(f, X_AXIS);
+
+ Real attach =0.0;
- Real attach =
- Note_head::stem_attachment_coordinate(f, X_AXIS);
+ if (invisible_b (me))
+ {
+ attach = 0.0;
+ }
+ else
+ attach = Note_head::stem_attachment_coordinate(f, X_AXIS);
Direction d = get_direction (me);
if (attach)
{
Real rule_thick
- = gh_scm2double (me->get_grob_property ("thickness"))
- * me->paper_l ()->get_var ("stafflinethickness");
-
+ = thickness (me);
r += - d * rule_thick * 0.5;
}
}
-
Grob*
-Stem::beam_l (Grob*me)
+Stem::get_beam (Grob*me)
{
SCM b= me->get_grob_property ("beam");
return unsmob_grob (b);
}
-
-// ugh still very long.
Stem_info
-Stem::calc_stem_info (Grob*me)
+Stem::get_stem_info (Grob *me)
{
- Grob * beam = beam_l (me);
-
- Direction beam_dir = Directional_element_interface::get (beam);
- if (!beam_dir)
+ /* Return cached info if available */
+ SCM scm_info = me->get_grob_property ("stem-info");
+ if (!gh_pair_p (scm_info))
{
- programming_error ("Beam dir not set.");
- beam_dir = UP;
+ calc_stem_info (me);
+ scm_info = me->get_grob_property ("stem-info");
}
-
+
+ Stem_info si;
+ 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 = get_grob_direction (me);
Real staff_space = Staff_symbol_referencer::staff_space (me);
- Real half_space = staff_space / 2;
- int multiplicity = Beam::get_multiplicity (beam);
+ Grob *beam = get_beam (me);
+ Real beam_translation = Beam::get_beam_translation (beam);
+ Real beam_thickness = Beam::get_thickness (beam);
+ int beam_count = Beam::get_direction_beam_count (beam, my_dir);
- SCM space_proc = beam->get_grob_property ("space-function");
- SCM space = gh_call1 (space_proc, gh_int2scm (multiplicity));
- Real interbeam_f = gh_scm2double (space) * staff_space;
+ /* Simple standard stem length */
+ SCM lengths = me->get_grob_property ("beamed-lengths");
+ Real ideal_length =
+ 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, lengths))
+ * staff_space;
+
- Real thick = gh_scm2double (beam->get_grob_property ("thickness"));
- Stem_info info;
- info.idealy_f_ = chord_start_f (me);
+ /* 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
+ + (beam_count - 1) * beam_translation;
- // for simplicity, we calculate as if dir == UP
- info.idealy_f_ *= beam_dir;
- SCM grace_prop = me->get_grob_property ("grace");
+ Real ideal_minimum_length = ideal_minimum_free
+ + height_of_my_beams
+ /* stem only extends to center of beam */
+ - 0.5 * beam_thickness;
+
+ ideal_length = ideal_length >? ideal_minimum_length;
- bool grace_b = to_boolean (grace_prop);
-
- Array<Real> a;
- SCM s;
- s = me->get_grob_property ("beamed-minimum-lengths");
- a.clear ();
- for (SCM q = s; q != SCM_EOL; q = ly_cdr (q))
- a.push (gh_scm2double (ly_car (q)));
+ /* Convert to Y position, calculate for dir == UP */
+ Real note_start =
+ /* staff positions */
+ head_positions (me)[my_dir] * 0.5
+ * my_dir * staff_space;
+ Real ideal_y = note_start + ideal_length;
- Real minimum_length = a[multiplicity <? (a.size () - 1)] * staff_space;
- s = me->get_grob_property ("beamed-lengths");
+ /* Conditions for Y position */
- a.clear ();
- for (SCM q = s; q != SCM_EOL; q = ly_cdr (q))
- a.push (gh_scm2double (ly_car (q)));
+ /* Lowest beam of (UP) beam must never be lower than second staffline
+
+ Reference?
+
+ Although this (additional) rule is probably correct,
+ I expect that highest beam (UP) should also never be lower
+ than middle staffline, just as normal stems.
- Real stem_length = a[multiplicity <? (a.size () - 1)] * staff_space;
+ Reference?
- if (!beam_dir || (beam_dir == Directional_element_interface::get (me)))
- /* normal beamed stem */
+ Obviously not for grace beams.
+
+ Also, not for knees. Seems to be a good thing. */
+ bool no_extend_b = to_boolean (me->get_grob_property ("no-stem-extend"));
+ bool knee_b = to_boolean (beam->get_grob_property ("knee"));
+ if (!no_extend_b && !knee_b)
{
- if (multiplicity)
- {
- info.idealy_f_ += thick + (multiplicity - 1) * interbeam_f;
- }
- info.miny_f_ = info.idealy_f_;
- info.maxy_f_ = INT_MAX;
+ /* Highest beam of (UP) beam must never be lower than middle
+ staffline */
+ ideal_y = ideal_y >? 0;
+ /* Lowest beam of (UP) beam must never be lower than second staffline */
+ ideal_y = ideal_y >? (-staff_space
+ - beam_thickness + height_of_my_beams);
+ }
- info.idealy_f_ += stem_length;
- info.miny_f_ += minimum_length;
- /*
- lowest beam of (UP) beam must never be lower than second staffline
+ ideal_y -= robust_scm2double (beam->get_grob_property ("shorten"), 0);
- Hmm, reference (Wanske?)
+ Real minimum_free =
+ gh_scm2double (robust_list_ref
+ (beam_count - 1,
+ me->get_grob_property
+ ("beamed-extreme-minimum-free-lengths")))
+ * staff_space;
- Although this (additional) rule is probably correct,
- I expect that highest beam (UP) should also never be lower
- than middle staffline, just as normal stems.
-
- */
- bool no_extend_b = to_boolean (me->get_grob_property ("no-stem-extend"));
- if (!grace_b && !no_extend_b)
- {
- /* highest beam of (UP) beam must never be lower than middle
- staffline
- lowest beam of (UP) beam must never be lower than second staffline
- */
- info.miny_f_ =
- info.miny_f_ >? 0
- >? (- 2 * half_space - thick
- + (multiplicity > 0) * thick
- + interbeam_f * (multiplicity - 1));
- }
- }
- else
- /* knee */
- {
- info.idealy_f_ -= thick;
- info.maxy_f_ = info.idealy_f_;
- info.miny_f_ = -INT_MAX;
+ Real minimum_length = minimum_free
+ + height_of_my_beams
+ /* stem only extends to center of beam */
+ - 0.5 * beam_thickness;
- info.idealy_f_ -= stem_length;
- info.maxy_f_ -= minimum_length;
- }
+ Real minimum_y = note_start + minimum_length;
+
- info.idealy_f_ = (info.maxy_f_ <? info.idealy_f_) >? info.miny_f_;
+ ideal_y *= my_dir;
+ Real shortest_y = minimum_y * my_dir;
+
+ me->set_grob_property ("stem-info",
+ scm_list_n (gh_double2scm (ideal_y),
+ gh_double2scm (shortest_y),
+ SCM_UNDEFINED));
+}
- s = beam->get_grob_property ("shorten");
- if (gh_number_p (s))
- info.idealy_f_ -= gh_scm2double (s);
+Slice
+Stem::beam_multiplicity (Grob *stem)
+{
+ SCM beaming= stem->get_grob_property ("beaming");
+ Slice l = int_list_to_slice (gh_car (beaming));
+ Slice r = int_list_to_slice (gh_cdr (beaming));
+ l.unite (r);
- Grob *common = me->common_refpoint (beam, Y_AXIS);
- Real interstaff_f = beam_dir *
- (me->relative_coordinate (common, Y_AXIS)
- - beam->relative_coordinate (common, Y_AXIS));
+ return l;
+}
- info.idealy_f_ += interstaff_f;
- info.miny_f_ += interstaff_f;
- info.maxy_f_ += interstaff_f ;
- return info;
-}
+/*
+ these are too many props.
+ */
+ADD_INTERFACE (Stem,"stem-interface",
+ "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");
-bool
-Stem::has_interface (Grob*m)
+
+
+/****************************************************************/
+
+Stem_info::Stem_info()
{
- return m && m->has_interface (ly_symbol2scm ("stem-interface"));
+ ideal_y_ = shortest_y_ =0;
+ dir_ = CENTER;
}
void
-Stem::set_interface (Grob*me)
-{
- me->set_interface (ly_symbol2scm ("stem-interface"));
+Stem_info::scale (Real x)
+{
+ ideal_y_ *= x;
+ shortest_y_ *= x;
}