X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=lily%2Fbeam.cc;h=48c81d14cf764266d1db2665f99b643dbf370376;hb=6bf1606d827527f02953add0988a4fbbf0dc0c4e;hp=0335ff72565218e592c032d95588e3cf6f1af069;hpb=01d84d552854fb399bd0e819502c6c2db2a1b214;p=lilypond.git diff --git a/lily/beam.cc b/lily/beam.cc index 0335ff7256..48c81d14cf 100644 --- a/lily/beam.cc +++ b/lily/beam.cc @@ -3,810 +3,901 @@ source file of the GNU LilyPond music typesetter - (c) 1997--1999 Han-Wen Nienhuys + (c) 1997--2001 Han-Wen Nienhuys Jan Nieuwenhuizen */ - /* [TODO] - * center beam symbol - * less hairy code - * redo grouping - -TODO: -The relationship Stem <-> Beam is way too hairy. Let's figure who -needs what, and what information should be available when. + -* shorter! (now +- 1000 lines) + + -* less hairy code + + */ - */ -#include +#include // tanh. +#include "molecule.hh" +#include "directional-element-interface.hh" #include "beaming.hh" -#include "proto.hh" -#include "dimensions.hh" #include "beam.hh" #include "misc.hh" -#include "debug.hh" -#include "molecule.hh" -#include "leastsquares.hh" +#include "least-squares.hh" #include "stem.hh" #include "paper-def.hh" #include "lookup.hh" #include "group-interface.hh" +#include "staff-symbol-referencer.hh" +#include "item.hh" +#include "spanner.hh" +#include "warn.hh" -Beam::Beam () +void +Beam::add_stem (Grob*me, Grob*s) { - Group_interface g (this, "stems"); - g.set_interface (); + Pointer_group_interface:: add_element (me, "stems", s); - slope_f_ = 0; - left_y_ = 0; - multiple_i_ = 0; -} + s->add_dependency (me); -/* - TODO: Fix this class. This is wildly inefficient. - */ -Stem * -Beam::stem (int i)const -{ - return Group_interface__extract_elements ((Beam*) this, (Stem*) 0, "stems")[i]; -} + assert (!Stem::beam_l (s)); + s->set_grob_property ("beam", me->self_scm ()); -int -Beam::stem_count ()const -{ - Group_interface gi (this, "stems"); - return gi.count (); + add_bound_item (dynamic_cast (me), dynamic_cast (s)); } - -void -Beam::add_stem (Stem*s) +int +Beam::get_multiplicity (Grob*me) { - Group_interface gi (this, "stems"); - gi.add_element (s); - - s->add_dependency (this); - - assert (!s->beam_l ()); - s->set_elt_property ("beam", self_scm_); + int m = 0; + for (SCM s = me->get_grob_property ("stems"); gh_pair_p (s); s = gh_cdr (s)) + { + Grob * sc = unsmob_grob (gh_car (s)); - if (!spanned_drul_[LEFT]) - set_bounds (LEFT,s); - else - set_bounds (RIGHT,s); + if (Stem::has_interface (sc)) + m = m >? Stem::beam_count (sc,LEFT) >? Stem::beam_count (sc,RIGHT); + } + return m; } -Stem_info -Beam::get_stem_info (Stem *s) +/* + After pre-processing all directions should be set. + Several post-processing routines (stem, slur, script) need stem/beam + direction. + Currenly, this means that beam has set all stem's directions. + [Alternatively, stems could set its own directions, according to + their beam, during 'final-pre-processing'.] + */ +MAKE_SCHEME_CALLBACK (Beam,before_line_breaking,1); +SCM +Beam::before_line_breaking (SCM smob) { - Stem_info i; - for (int i=0; i < sinfo_.size (); i++) + Grob * me = unsmob_grob (smob); + + // Why? + /* + Why what? Why the warning (beams with less than 2 stems are + degenerate beams, should never happen), or why would this ever + happen (don't know). */ + if (visible_stem_count (me) < 2) + { + warning (_ ("beam has less than two stems")); + } + if (visible_stem_count (me) >= 1) { - if (sinfo_[i].stem_l_ == s) - return sinfo_[i]; + if (!Directional_element_interface::get (me)) + Directional_element_interface::set (me, get_default_dir (me)); + + consider_auto_knees (me); + set_stem_directions (me); + set_stem_shorten (me); } - assert (false); - return i; + return SCM_EOL; } -Molecule* -Beam::do_brew_molecule_p () const +Direction +Beam::get_default_dir (Grob*me) { - Molecule *mol_p = new Molecule; - if (!sinfo_.size ()) - return mol_p; - - Real x0 = stem (0)->hpos_f (); - for (int j=0; j 0)? stem (j-1) : 0; - Stem * next = (j < stem_count ()-1) ? stem (j+1) :0; - - Molecule sb = stem_beams (i, next, prev); - Real x = i->hpos_f ()-x0; - sb.translate (Offset (x, (x * slope_f_ + left_y_) * - i->staff_line_leading_f ()/2 )); - mol_p->add_molecule (sb); - } - mol_p->translate_axis (x0 - - spanned_drul_[LEFT]->relative_coordinate (0, X_AXIS), X_AXIS); + Drul_array total; + total[UP] = total[DOWN] = 0; + Drul_array count; + count[UP] = count[DOWN] = 0; + Direction d = DOWN; + + Link_array stems= + Pointer_group_interface__extract_elements (me, (Item*)0, "stems"); + + for (int i=0; i get_grob_property ("dir-function"); + SCM s = gh_call2 (func, + gh_cons (gh_int2scm (count[UP]), + gh_int2scm (count[DOWN])), + gh_cons (gh_int2scm (total[UP]), + gh_int2scm (total[DOWN]))); + + if (gh_number_p (s) && gh_scm2int (s)) + return to_dir (s); + + /* + If dir is not determined: get default + */ + return to_dir (me->get_grob_property ("neutral-direction")); } -Offset -Beam::center () const + +/* + Set all stems with non-forced direction to beam direction. + Urg: non-forced should become `without/with unforced' direction, + once stem gets cleaned-up. + */ +void +Beam::set_stem_directions (Grob*me) { - Stem_info si = sinfo_[0]; + Link_array stems + =Pointer_group_interface__extract_elements (me, (Item*) 0, "stems"); + Direction d = Directional_element_interface::get (me); - Real w= (si.stem_l_->note_delta_f () + extent (X_AXIS).length ())/2.0; - return Offset (w, ( w* slope_f_) * - si.stem_l_->staff_line_leading_f ()/2); -} + for (int i=0; i remove_grob_property ("dir-forced"); + if (!gh_boolean_p (force) || !gh_scm2bool (force)) + Directional_element_interface ::set (s,d); + } +} /* Simplistic auto-knees; only consider vertical gap between two - adjacent chords + adjacent chords. + + `Forced' stem directions are ignored. If you don't want auto-knees, + don't set, or unset auto-knee-gap. */ -bool -Beam::auto_knee (SCM gap, bool interstaff_b) +void +Beam::consider_auto_knees (Grob *me) { - bool knee = false; - int knee_y = 0; - Real internote_f = stem (0)->staff_line_leading_f ()/2; - if (gap != SCM_UNDEFINED) + SCM scm = me->get_grob_property ("auto-knee-gap"); + + if (gh_number_p (scm)) { - int auto_gap_i = gh_scm2int (gap); - for (int i=1; i < stem_count (); i++) + bool knee_b = false; + Real knee_y = 0; + Real staff_space = Staff_symbol_referencer::staff_space (me); + Real gap = gh_scm2double (scm) / staff_space; + + Direction d = Directional_element_interface::get (me); + Link_array stems= + Pointer_group_interface__extract_elements (me, (Item*)0, "stems"); + + Grob *common = me->common_refpoint (stems[0], Y_AXIS); + for (int i=1; i < stems.size (); i++) + if (!Stem::invisible_b (stems[i])) + common = common->common_refpoint (stems[i], Y_AXIS); + + int l = 0; + for (int i=1; i < stems.size (); i++) { - bool is_b = (bool)(sinfo_[i].interstaff_f_ - sinfo_[i-1].interstaff_f_); - int l_y = (int)(stem (i-1)->chord_start_f () / internote_f) - + (int)sinfo_[i-1].interstaff_f_; - int r_y = (int)(stem (i)->chord_start_f () / internote_f) - + (int)sinfo_[i].interstaff_f_; - int gap_i = r_y - l_y; - - /* - Forced stem directions are ignored. If you don't want auto-knees, - don't set, or unset autoKneeGap/autoInterstaffKneeGap. - */ - if ((abs (gap_i) >= auto_gap_i) && (!interstaff_b || is_b)) + if (!Stem::invisible_b (stems[i-1])) + l = i - 1; + if (Stem::invisible_b (stems[l])) + continue; + if (Stem::invisible_b (stems[i])) + continue; + + Real left = Stem::extremal_heads (stems[l])[d] + ->relative_coordinate (common, Y_AXIS); + Real right = Stem::extremal_heads (stems[i])[-d] + ->relative_coordinate (common, Y_AXIS); + + Real dy = right - left; + + if (abs (dy) >= gap) { - knee_y = (r_y + l_y) / 2; - knee = true; + knee_y = (right + left) / 2; + knee_b = true; break; } } - } - if (knee) - { - for (int i=0; i < stem_count (); i++) - { - int y = (int)(stem (i)->chord_start_f () / internote_f) - + (int)sinfo_[i].interstaff_f_; - stem (i)->set_direction ( y < knee_y ? UP : DOWN); - stem (i)->set_elt_property ("dir-forced", SCM_BOOL_T); + + if (knee_b) + { + for (int i=0; i < stems.size (); i++) + { + if (Stem::invisible_b (stems[i])) + continue; + Item *s = stems[i]; + Real y = Stem::extremal_heads (stems[i])[d] + ->relative_coordinate (common, Y_AXIS); + + Directional_element_interface::set (s, y < knee_y ? UP : DOWN); + s->set_grob_property ("dir-forced", SCM_BOOL_T); + } } } - return knee; } -bool -Beam::auto_knees () +/* + Set stem's shorten property if unset. + TODO: + take some y-position (chord/beam/nearest?) into account + scmify forced-fraction + */ +void +Beam::set_stem_shorten (Grob*m) { - if (auto_knee (get_elt_property ("auto-interstaff-knee-gap"), true)) - return true; - - return auto_knee (get_elt_property ("auto-knee-gap"), false); -} + Spanner*me = dynamic_cast (m); + Real forced_fraction = forced_stem_count (me) / visible_stem_count (me); + if (forced_fraction < 0.5) + return; -void -Beam::do_pre_processing () -{ - /* - urg: it seems that info on whether beam (voice) dir was forced - is being junked here? - */ - if (!get_direction ()) - set_direction ( get_default_dir ()); - - set_direction (get_direction ()); -} + int multiplicity = get_multiplicity (me); -void -Beam::do_print () const -{ -#ifndef NPRINT - DEBUG_OUT << "slope_f_ " << slope_f_ << "left ypos " << left_y_; - Spanner::do_print (); -#endif -} + SCM shorten = me->get_grob_property ("beamed-stem-shorten"); + if (shorten == SCM_EOL) + return; -void -Beam::do_post_processing () -{ - if (stem_count () < 2) - { - warning (_ ("beam with less than two stems")); - set_elt_property ("transparent", SCM_BOOL_T); - return; - } - set_steminfo (); - if (auto_knees ()) - { - /* - if auto-knee did its work, most probably stem directions - have changed, so we must recalculate all. - */ - set_direction ( get_default_dir ()); - set_direction (get_direction ()); + int sz = scm_ilength (shorten); + + Real staff_space = Staff_symbol_referencer::staff_space (me); + SCM shorten_elt = scm_list_ref (shorten, gh_int2scm (multiplicity stems= + Pointer_group_interface__extract_elements (me, (Item*)0, "stems"); + + for (int i=0; i < stems.size (); i++) + { + Item* s = stems[i]; + if (Stem::invisible_b (s)) + continue; + if (gh_number_p (s->get_grob_property ("shorten"))) + s->set_grob_property ("shorten", gh_double2scm (shorten_f)); } - calculate_slope (); - set_stemlens (); } +/* + Call list of y-dy-callbacks, that handle setting of + grob-properties y, dy. -#if 0 -Interval -Beam::do_width () const -{ - return Interval (stem (0)->hpos_f (), - stems_.top ()->hpos_f ()); -} -#endif + User may set grob-properties: y-position-hs and height-hs + (to be fixed) that override the calculated y and dy. -Direction -Beam::get_default_dir () const + Because y and dy cannot be calculated and quanted separately, we + always calculate both, then check for user override. + */ +MAKE_SCHEME_CALLBACK (Beam, after_line_breaking, 1); +SCM +Beam::after_line_breaking (SCM smob) { - Drul_array total; - total[UP] = total[DOWN] = 0; - Drul_array count; - count[UP] = count[DOWN] = 0; - Direction d = DOWN; - - for (int i=0; i get_direction () - ? (1 + d * s->get_direction ())/2 - : s->get_center_distance ((Direction)-d); + Grob * me = unsmob_grob (smob); + + me->set_grob_property ("y", gh_double2scm (0)); + me->set_grob_property ("dy", gh_double2scm (0)); - if (current) - { - total[d] += current; - count[d] ++; - } + /* Hmm, callbacks should be called by, a eh, callback mechanism + somewhere (?), I guess, not by looping here. */ + + SCM list = me->get_grob_property ("y-dy-callbacks"); + for (SCM i = list; gh_pair_p (i); i = gh_cdr (i)) + gh_call1 (gh_car (i), smob); - } while (flip(&d) != DOWN); + // UGH. Y is not in staff position unit? + // Ik dacht datwe daar juist van weg wilden? + + // Hmm, nu hebben we 3 dimensies, want inmiddels zijn we daar + // weer terug, maar dan / 2 + // (staff-space iso staff-position) - /* - [Ross] states that the majority of the notes dictates the - direction (and not the mean of "center distance") + set_stem_lengths (me); - But is that because it really looks better, or because he wants - to provide some real simple hands-on rules? - - We have our doubts, so we simply provide all sensible alternatives. + return SCM_UNSPECIFIED; +} - If dir is not determined: up (see stem::get_default_dir ()) */ - Direction beam_dir = CENTER; - Direction neutral_dir = (Direction)(int)paper_l ()->get_var ("stem_default_neutral_direction"); +MAKE_SCHEME_CALLBACK (Beam, least_squares, 1); +SCM +Beam::least_squares (SCM smob) +{ + Grob *me = unsmob_grob (smob); - SCM a = get_elt_property ("beam-dir-algorithm"); - - if (a == ly_symbol2scm ("majority")) // should get default from paper. - beam_dir = (count[UP] == count[DOWN]) ? neutral_dir - : (count[UP] > count[DOWN]) ? UP : DOWN; - else if (a == ly_symbol2scm ("mean")) - // mean center distance - beam_dir = (total[UP] == total[DOWN]) ? neutral_dir - : (total[UP] > total[DOWN]) ? UP : DOWN; - else if (a == ly_symbol2scm ("median")) + if (visible_stem_count (me) <= 1) + return SCM_UNSPECIFIED; + + Real y = 0; + Real dy = 0; + + /* Stem_info, and thus y,dy in this function are corrected for beam-dir */ + Real first_ideal = Stem::calc_stem_info (first_visible_stem (me)).idealy_f_; + if (first_ideal == Stem::calc_stem_info (last_visible_stem (me)).idealy_f_) { - // median center distance - if (count[DOWN] && count[UP]) - { - beam_dir = (total[UP] / count[UP] == total[DOWN] / count[DOWN]) - ? neutral_dir - : (total[UP] / count[UP] > total[DOWN] / count[DOWN]) ? UP : DOWN; - } - else + y = first_ideal; + dy = 0; + } + else + { + Array ideals; + + // ugh -> use commonx + Real x0 = first_visible_stem (me)->relative_coordinate (0, X_AXIS); + Link_array stems= + Pointer_group_interface__extract_elements (me, (Item*)0, "stems"); + + for (int i=0; i < stems.size (); i++) { - beam_dir = (count[UP] == count[DOWN]) ? neutral_dir - : (count[UP] > count[DOWN]) ? UP : DOWN; + Item* s = stems[i]; + if (Stem::invisible_b (s)) + continue; + ideals.push (Offset (s->relative_coordinate (0, X_AXIS) - x0, + Stem::calc_stem_info (s).idealy_f_)); } + Real dydx; + minimise_least_squares (&dydx, &y, ideals); + + Real dx = last_visible_stem (me)->relative_coordinate (0, X_AXIS) - x0; + dy = dydx * dx; } - - return beam_dir; + + /* Store true, not dir-corrected values */ + Direction dir = Directional_element_interface::get (me); + me->set_grob_property ("y", gh_double2scm (y * dir)); + me->set_grob_property ("dy", gh_double2scm (dy * dir)); + return SCM_UNSPECIFIED; } -void -Beam::set_direction (Direction d) +MAKE_SCHEME_CALLBACK (Beam, cancel_suspect_slope, 1); +SCM +Beam::cancel_suspect_slope (SCM smob) { - Directional_spanner::set_direction (d); - for (int i=0; i get_grob_property ("y")) * dir; + Real dy = gh_scm2double (me->get_grob_property ("dy")) * dir; + + /* steep slope running against lengthened stem is suspect */ + Real first_ideal = Stem::calc_stem_info (first_visible_stem (me)).idealy_f_; + Real last_ideal = Stem::calc_stem_info (last_visible_stem (me)).idealy_f_; + Real lengthened = gh_scm2double (me->get_grob_property ("outer-stem-length-limit")); + Real steep = gh_scm2double (me->get_grob_property ("slope-limit")); + + // ugh -> use commonx + Real dx = last_visible_stem (me)->relative_coordinate (0, X_AXIS) - first_visible_stem (me)->relative_coordinate (0, X_AXIS); + Real dydx = dy && dx ? dy/dx : 0; + + if (( (y - first_ideal > lengthened) && (dydx > steep)) + || ((y + dy - last_ideal > lengthened) && (dydx < -steep))) { - Stem *s = stem (i); - s->set_elt_property ("beam-dir", gh_int2scm (d)); - - SCM force = s->remove_elt_property ("dir-forced"); - if (force == SCM_UNDEFINED) - s->set_direction ( d); + Real adjusted_y = y + dy / 2; + /* Store true, not dir-corrected values */ + me->set_grob_property ("y", gh_double2scm (adjusted_y * dir)); + me->set_grob_property ("dy", gh_double2scm (0)); } + return SCM_UNSPECIFIED; } /* - See Documentation/tex/fonts.doc - */ - -void -Beam::solve_slope () + This neat trick is by Werner Lemberg, + damped = tanh (slope) + corresponds with some tables in [Wanske] +*/ +MAKE_SCHEME_CALLBACK (Beam, slope_damping, 1); +SCM +Beam::slope_damping (SCM smob) { - assert (sinfo_.size () > 1); + Grob *me = unsmob_grob (smob); + + if (visible_stem_count (me) <= 1) + return SCM_UNSPECIFIED; - Least_squares l; - for (int i=0; i < sinfo_.size (); i++) + SCM s = me->get_grob_property ("damping"); + int damping = gh_scm2int (s); + + if (damping) { - l.input.push (Offset (sinfo_[i].x_, sinfo_[i].idealy_f_)); + /* y,dy in this function are corrected for beam-dir */ + Direction dir = Directional_element_interface::get (me); + Real y = gh_scm2double (me->get_grob_property ("y")) * dir; + Real dy = gh_scm2double (me->get_grob_property ("dy")) * dir; + + // ugh -> use commonx + Real dx = last_visible_stem (me)->relative_coordinate (0, X_AXIS) + - first_visible_stem (me)->relative_coordinate (0, X_AXIS); + Real dydx = dy && dx ? dy/dx : 0; + dydx = 0.6 * tanh (dydx) / damping; + + Real damped_dy = dydx * dx; + Real adjusted_y = y + (dy - damped_dy) / 2; + /* Store true, not dir-corrected values */ + me->set_grob_property ("y", gh_double2scm (adjusted_y * dir)); + me->set_grob_property ("dy", gh_double2scm (damped_dy * dir)); } - l.minimise (slope_f_, left_y_); + return SCM_UNSPECIFIED; } /* - ugh. Naming: this doesn't check, but sets as well. - */ - -Real -Beam::check_stemlengths_f (bool set_b) + Quantise dy (height) of beam. + Generalisation of [Ross]. + */ +MAKE_SCHEME_CALLBACK (Beam, quantise_dy, 1); +SCM +Beam::quantise_dy (SCM smob) { - Real interbeam_f = paper_l ()->interbeam_f (multiple_i_); + Grob *me = unsmob_grob (smob); - Real beam_f = gh_scm2double (get_elt_property ("beam-thickness")); - Real staffline_f = paper_l ()-> get_var ("stafflinethickness"); - Real epsilon_f = staffline_f / 8; - Real dy_f = 0.0; - for (int i=0; i < sinfo_.size (); i++) - { - Real y = sinfo_[i].x_ * slope_f_ + left_y_; + if (visible_stem_count (me) <= 1) + return SCM_UNSPECIFIED; - // correct for knee - if (get_direction () != sinfo_[i].get_direction ()) - { - Real internote_f = sinfo_[i].stem_l_->staff_line_leading_f ()/2; - y -= get_direction () * (beam_f / 2 - + (sinfo_[i].mult_i_ - 1) * interbeam_f) / internote_f; - if (!i && sinfo_[i].stem_l_->staff_symbol_l () != - sinfo_.top ().stem_l_->staff_symbol_l ()) - y += get_direction () * (multiple_i_ - (sinfo_[i].stem_l_->flag_i_ - 2) >? 0) - * interbeam_f / internote_f; - } + Array a; + SCM proc = me->get_grob_property ("height-quants"); + SCM quants = gh_call2 (proc, me->self_scm (), + gh_double2scm (me->paper_l ()->get_var ("stafflinethickness") + / 1.0)); + + for (SCM s = quants; gh_pair_p (s); s = gh_cdr (s)) + a.push (gh_scm2double (gh_car (s))); + + if (a.size () > 1) + { + /* y,dy in this function are corrected for beam-dir */ + Direction dir = Directional_element_interface::get (me); + Real y = gh_scm2double (me->get_grob_property ("y")) * dir; + Real dy = gh_scm2double (me->get_grob_property ("dy")) * dir; - if (set_b) - sinfo_[i].stem_l_->set_stemend (y - sinfo_[i].interstaff_f_); - - y *= get_direction (); - if (y > sinfo_[i].maxy_f_) - dy_f = dy_f ? sinfo_[i].miny_f_ - y; - } + Real staff_space = Staff_symbol_referencer::staff_space (me); + + Interval iv = quantise_iv (a, abs (dy)/staff_space) * staff_space; + Real q = (abs (dy) - iv[SMALLER] <= iv[BIGGER] - abs (dy)) + ? iv[SMALLER] + : iv[BIGGER]; + + Real quantised_dy = q * sign (dy); + Real adjusted_y = y + (dy - quantised_dy) / 2; + /* Store true, not dir-corrected values */ + me->set_grob_property ("y", gh_double2scm (adjusted_y * dir)); + me->set_grob_property ("dy", gh_double2scm (quantised_dy * dir)); } - return dy_f; + return SCM_UNSPECIFIED; } -void -Beam::set_steminfo () +/* It's tricky to have the user override y,dy directly, so we use this + translation func. Also, if our staff_space != 1 (smaller staff, eg), + user will expect staff-position to be discrete values. */ +MAKE_SCHEME_CALLBACK (Beam, user_override, 1); +SCM +Beam::user_override (SCM smob) { - if(!stem_count ()) - return; - - assert (multiple_i_); + Grob *me = unsmob_grob (smob); + Real staff_space = Staff_symbol_referencer::staff_space (me); - int total_count_i = 0; - int forced_count_i = 0; - for (int i=0; i < stem_count (); i++) + SCM s = me->get_grob_property ("staff-position"); + if (gh_number_p (s)) { - Stem *s = stem (i); - - s->set_default_extents (); - if (s->invisible_b ()) - continue; - if (((int)s->chord_start_f ()) && (s->get_direction () != s->get_default_dir ())) - forced_count_i++; - total_count_i++; + Real y = gh_scm2double (s) * staff_space * 0.5; + me->set_grob_property ("y", gh_double2scm (y)); } - bool grace_b = get_elt_property ("grace") == SCM_BOOL_T; - String type_str = grace_b ? "grace_" : ""; - int stem_max = (int)rint(paper_l ()->get_var ("stem_max")); - Real shorten_f = paper_l ()->get_var (type_str + "forced_stem_shorten" - + to_str (multiple_i_ get_grob_property ("height"); + if (gh_number_p (s)) { - Stem *s = stem (i); - /* - Chord tremolo needs to beam over invisible stems of wholes - */ - SCM trem = get_elt_property ("chord-tremolo"); - if (gh_boolean_p (trem) && gh_scm2bool (trem)) - { - if (s->invisible_b ()) - continue; - } - - Stem_info info (s, multiple_i_); - if (leftx == 0) - leftx = info.x_; - info.x_ -= leftx; - if (info.get_direction () == get_direction ()) - { - if (forced_count_i == total_count_i) - info.idealy_f_ -= shorten_f; - else if (forced_count_i > total_count_i / 2) - info.idealy_f_ -= shorten_f / 2; - } - sinfo_.push (info); + Real dy = gh_scm2double (s) * staff_space * 0.5; + me->set_grob_property ("dy", gh_double2scm (dy)); } + + return SCM_UNSPECIFIED; } -void -Beam::calculate_slope () +/* + Ugh, this must be last, after user_override + Assumes directionised y/dy. + */ +MAKE_SCHEME_CALLBACK (Beam, do_quantise_y, 1); +SCM +Beam::do_quantise_y (SCM smob) { - if (!sinfo_.size ()) - slope_f_ = left_y_ = 0; - else if (sinfo_[0].idealy_f_ == sinfo_.top ().idealy_f_) - { - slope_f_ = 0; - left_y_ = sinfo_[0].idealy_f_; - left_y_ *= get_direction (); - } - else - { - solve_slope (); - Real solved_slope_f = slope_f_; + Grob *me = unsmob_grob (smob); - /* - steep slope running against lengthened stem is suspect - */ - Real dx_f = stem (stem_count () -1)->hpos_f () - stem (0)->hpos_f (); + /* + If the user set y-position, we shouldn't do quanting. + */ + if (gh_number_p (me->get_grob_property ("y-position-hs"))) + return SCM_UNSPECIFIED; - // urg, these y internote-y-dimensions - Real internote_f = stem (0)->staff_line_leading_f ()/2; + Real y = gh_scm2double (me->get_grob_property ("y")); + Real dy = gh_scm2double (me->get_grob_property ("dy")); + + /* we can modify y, so we should quantise y */ + Real half_space = Staff_symbol_referencer::staff_space (me) / 2; + Real y_shift = check_stem_length_f (me, y, dy); + y += y_shift; + y = quantise_y_f (me, y, dy, 0); - Real lengthened = paper_l ()->get_var ("beam_lengthened") / internote_f; - Real steep = paper_l ()->get_var ("beam_steep_slope") / internote_f; - if (((left_y_ - sinfo_[0].idealy_f_ > lengthened) - && (slope_f_ > steep)) - || ((left_y_ + slope_f_ * dx_f - sinfo_.top ().idealy_f_ > lengthened) - && (slope_f_ < -steep))) - { - slope_f_ = 0; - } + /* + Hmm, this is a bit keyhole operation: we're passing `this' as a + parameter, and member vars as SCM properties. We should decide on + SCM/C/C++ boundary */ + me->set_grob_property ("y", gh_double2scm (y)); + set_stem_lengths (me); + y = gh_scm2double (me->get_grob_property ("y")); + + y_shift = check_stem_length_f (me, y, dy); + + if (y_shift > half_space / 4) + { + y += y_shift; /* - This neat trick is by Werner Lemberg, - damped = tanh (slope_f_) - corresponds with some tables in [Wanske] + for significantly lengthened or shortened stems, + request quanting the other way. */ - SCM damp = remove_elt_property ("damping"); - int damping = 1; // ugh. - if (damp!= SCM_UNDEFINED) - damping = gh_int2scm (damp); - - if (damping) - slope_f_ = 0.6 * tanh (slope_f_) / damping; - - quantise_dy (); - - Real damped_slope_dy_f = (solved_slope_f - slope_f_) * dx_f / 2; - left_y_ += damped_slope_dy_f; - - left_y_ *= get_direction (); - slope_f_ *= get_direction (); + int quant_dir = 0; + if (abs (y_shift) > half_space / 2) + quant_dir = sign (y_shift) * Directional_element_interface::get (me); + y = quantise_y_f (me, y, dy, quant_dir); } + + me->set_grob_property ("y", gh_double2scm (y)); + // me->set_grob_property ("dy", gh_double2scm (dy)); + return SCM_UNSPECIFIED; } -void -Beam::quantise_dy () + +Real +Beam::calc_stem_y_f (Grob*me,Item* s, Real y, Real dy) { - /* - [Ross] (simplification of) - Try to set slope_f_ complying with y-span of: - - zero - - beam_f / 2 + staffline_f / 2 - - beam_f + staffline_f - + n * interline - */ + int beam_multiplicity = get_multiplicity (me); + int stem_multiplicity = (Stem::flag_i (s) - 2) >? 0; - SCM q = get_elt_property ("slope-quantisation"); - - if (q == ly_symbol2scm ("none")) - return; + SCM space_proc = me->get_grob_property ("space-function"); + SCM space = gh_call1 (space_proc, gh_int2scm (beam_multiplicity)); - Real interline_f = stem (0)->staff_line_leading_f (); - Real internote_f = interline_f / 2; - Real staffline_f = paper_l ()->get_var ("stafflinethickness"); - Real beam_f = gh_scm2double (get_elt_property ("beam-thickness"));; + Real thick = gh_scm2double (me->get_grob_property ("thickness")) ; + Real interbeam_f = gh_scm2double (space) ; - Real dx_f = stem (stem_count () -1 )->hpos_f () - stem (0)->hpos_f (); + // ugh -> use commonx + Real x0 = first_visible_stem (me)->relative_coordinate (0, X_AXIS); + Real dx = last_visible_stem (me)->relative_coordinate (0, X_AXIS) - x0; + Real stem_y = (dy && dx ? (s->relative_coordinate (0, X_AXIS) - x0) / dx * dy : 0) + y; - // dim(y) = internote; so slope = (y/internote)/x - Real dy_f = dx_f * abs (slope_f_ * internote_f); - - Real quanty_f = 0.0; + /* knee */ + Direction dir = Directional_element_interface::get (me); + Direction sdir = Directional_element_interface::get (s); + + /* knee */ + if (dir!= sdir) + { + stem_y -= dir + * (thick / 2 + (beam_multiplicity - 1) * interbeam_f); + + + + // huh, why not for first visible? + if (Staff_symbol_referencer::staff_symbol_l (s) + != Staff_symbol_referencer::staff_symbol_l (last_visible_stem (me))) + stem_y += Directional_element_interface::get (me) + * (beam_multiplicity - stem_multiplicity) * interbeam_f; + } + + return stem_y; +} - Array allowed_fraction (3); - allowed_fraction[0] = 0; - allowed_fraction[1] = (beam_f / 2 + staffline_f / 2); - allowed_fraction[2] = (beam_f + staffline_f); +Real +Beam::check_stem_length_f (Grob*me,Real y, Real dy) +{ + Real shorten = 0; + Real lengthen = 0; + Direction dir = Directional_element_interface::get (me); + Link_array stems= + Pointer_group_interface__extract_elements (me, (Item*)0, "stems"); - Interval iv = quantise_iv (allowed_fraction, interline_f, dy_f); - quanty_f = (dy_f - iv[SMALLER] <= iv[BIGGER] - dy_f) - ? iv[SMALLER] - : iv[BIGGER]; + for (int i=0; i < stems.size (); i++) + { + Item* s = stems[i]; + if (Stem::invisible_b (s)) + continue; + Real stem_y = calc_stem_y_f (me, s, y, dy); + + stem_y *= dir; + Stem_info info = Stem::calc_stem_info (s); - slope_f_ = (quanty_f / dx_f) / internote_f * sign (slope_f_); + // if (0 > info.maxy_f_ - stem_y) + shorten = shorten ? info.miny_f_ - stem_y; + } + + if (lengthen && shorten) + warning (_ ("weird beam vertical offset")); + + /* when all stems are too short, normal stems win */ + return dir * ((shorten) ? shorten : lengthen); } /* - - Prevent interference from stafflines and beams. See Documentation/tex/fonts.doc - - */ + Hmm. At this time, beam position and slope are determined. Maybe, + stem directions and length should set to relative to the chord's + position of the beam. */ void -Beam::quantise_left_y (bool extend_b) +Beam::set_stem_lengths (Grob *me) { - /* - we only need to quantise the start of the beam as dy is quantised too - if extend_b then stems must *not* get shorter - */ - SCM q = get_elt_property ("slope-quantisation"); + if (visible_stem_count (me) <= 1) + return; + + Real y = gh_scm2double (me->get_grob_property ("y")); + Real dy = gh_scm2double (me->get_grob_property ("dy")); + Real half_space = Staff_symbol_referencer::staff_space (me)/2; + Link_array stems= + Pointer_group_interface__extract_elements (me, (Item*)0, "stems"); - /* - ---------------------------------------------------------- - ######## - ######## - ######## - --------------########------------------------------------ - ######## - - hang straddle sit inter hang - */ + Grob *common = me->common_refpoint (stems[0], Y_AXIS); + for (int i=1; i < stems.size (); i++) + if (!Stem::invisible_b (stems[i])) + common = common->common_refpoint (stems[i], Y_AXIS); - Real space = stem (0)->staff_line_leading_f (); - Real internote_f = space /2; - Real staffline_f = paper_l ()->get_var ("stafflinethickness"); - Real beam_f = gh_scm2double (get_elt_property ("beam-thickness"));; + for (int i=0; i < stems.size (); i++) + { + Item* s = stems[i]; + if (Stem::invisible_b (s)) + continue; - /* - [TODO] - it would be nice to have all allowed positions in a runtime matrix: - (multiplicity, minimum_beam_dy, maximum_beam_dy) - */ + Real stem_y = calc_stem_y_f (me, s, y, dy); - Real straddle = 0; - Real sit = beam_f / 2 - staffline_f / 2; - Real hang = space - beam_f / 2 + staffline_f / 2; + // doesn't play well with dvips + if (scm_definedp (ly_symbol2scm ("ps-testing"), SCM_UNDEFINED) + == SCM_BOOL_T) + if (Stem::get_direction (s) == Directional_element_interface::get (me)) + stem_y += Stem::get_direction (s) + * gh_scm2double (me->get_grob_property ("thickness")) / 2; + + /* caution: stem measures in staff-positions */ + Real id = me->relative_coordinate (common, Y_AXIS) + - stems[i]->relative_coordinate (common, Y_AXIS); + Stem::set_stemend (s, (stem_y + id) / half_space); + } +} - /* - Put all allowed positions into an array. - Whether a position is allowed or not depends on - strictness of quantisation, multiplicity and direction. +/* + Prevent interference from stafflines and beams. - For simplicity, we'll assume dir = UP and correct if - dir = DOWN afterwards. - */ - // isn't this asymmetric ? --hwn - - // dim(left_y_) = internote - Real dy_f = get_direction () * left_y_ * internote_f; + We only need to quantise the (left) y of the beam, + since dy is quantised too. + if extend_b then stems must *not* get shorter + */ +Real +Beam::quantise_y_f (Grob*me,Real y, Real dy, int quant_dir) +{ + int multiplicity = get_multiplicity (me); - Real beamdx_f = stem (stem_count () -1)->hpos_f () - stem (0)->hpos_f (); - Real beamdy_f = beamdx_f * slope_f_ * internote_f; + Real staff_space = Staff_symbol_referencer::staff_space (me); + Real thick = me->paper_l ()->get_var ("stafflinethickness"); - Array allowed_position; - if (q == ly_symbol2scm ("normal")) - { - if ((multiple_i_ <= 2) || (abs (beamdy_f) >= staffline_f / 2)) - allowed_position.push (straddle); - if ((multiple_i_ <= 1) || (abs (beamdy_f) >= staffline_f / 2)) - allowed_position.push (sit); - allowed_position.push (hang); - } - else if (q == ly_symbol2scm ("traditional")) - { - // TODO: check and fix TRADITIONAL - if ((multiple_i_ <= 2) || (abs (beamdy_f) >= staffline_f / 2)) - allowed_position.push (straddle); - if ((multiple_i_ <= 1) && (beamdy_f <= staffline_f / 2)) - allowed_position.push (sit); - if (beamdy_f >= -staffline_f / 2) - allowed_position.push (hang); - } + SCM proc = me->get_grob_property ("vertical-position-quant-function"); + SCM quants = scm_apply (proc, + me->self_scm (), + gh_list (gh_int2scm (multiplicity), + gh_double2scm (dy/staff_space), + gh_double2scm (thick/staff_space), + SCM_EOL, SCM_UNDEFINED)); + + Array a; - Interval iv = quantise_iv (allowed_position, space, dy_f); + for (; gh_pair_p (quants); quants = gh_cdr (quants)) + a.push (gh_scm2double (gh_car (quants))); - Real quanty_f = dy_f - iv[SMALLER] <= iv[BIGGER] - dy_f ? iv[SMALLER] : iv[BIGGER]; - if (extend_b) - quanty_f = iv[BIGGER]; + if (a.size () <= 1) + return y; - // dim(left_y_) = internote - left_y_ = get_direction () * quanty_f / internote_f; -} + Real up_y = Directional_element_interface::get (me) * y; + Interval iv = quantise_iv (a, up_y/staff_space) * staff_space; -void -Beam::set_stemlens () -{ - Real staffline_f = paper_l ()->get_var ("stafflinethickness"); - // enge floots - Real epsilon_f = staffline_f / 8; - - - // je bent zelf eng --hwn. - Real dy_f = check_stemlengths_f (false); - for (int i = 0; i < 2; i++) // 2 ? - { - left_y_ += dy_f * get_direction (); - quantise_left_y (dy_f); - dy_f = check_stemlengths_f (true); - if (abs (dy_f) <= epsilon_f) - { - break; - } - } + Real q = up_y - iv[SMALLER] <= iv[BIGGER] - up_y + ? iv[SMALLER] : iv[BIGGER]; + if (quant_dir) + q = iv[ (Direction)quant_dir]; + + return q * Directional_element_interface::get (me); } void -Beam::set_beaming (Beaming_info_list *beaming) +Beam::set_beaming (Grob*me,Beaming_info_list *beaming) { + Link_array stems= + Pointer_group_interface__extract_elements (me, (Grob*)0, "stems"); + Direction d = LEFT; - for (int i=0; i < stem_count (); i++) + for (int i=0; i < stems.size (); i++) { do { - if (stem (i)->beams_i_drul_[d] < 0) - stem (i)->beams_i_drul_[d] = beaming->infos_.elem (i).beams_i_drul_[d]; + /* Don't overwrite user override (?) */ + if (Stem::beam_count (stems[i], d) == 0 + /* Don't set beaming for outside of outer stems */ + && ! (d == LEFT && i == 0) + && ! (d == RIGHT && i == stems.size () -1)) + { + int b = beaming->infos_.elem (i).beams_i_drul_[d]; + Stem::set_beaming (stems[i], b, d); + } } while (flip (&d) != LEFT); } } -void -Beam::do_add_processing () -{ - for (int i=0; i < stem_count () ; i++) - { - Direction d = LEFT; - do { - multiple_i_ = multiple_i_ >? stem (i)->beams_i_drul_[d]; - } while ((flip (&d)) != LEFT); - } - - /* - Why? - */ - if (stem_count ()) - { - stem (0)->beams_i_drul_[LEFT] =0; - stem (stem_count () -1)->beams_i_drul_[RIGHT] =0; - } -} - - /* beams to go with one stem. - clean me up. + FIXME: clean me up. */ Molecule -Beam::stem_beams (Stem *here, Stem *next, Stem *prev) const +Beam::stem_beams (Grob*me,Item *here, Item *next, Item *prev, + Real dy, Real dydx + ) { - if ((next && !(next->hpos_f () > here->hpos_f ())) || - (prev && !(prev->hpos_f () < here->hpos_f ()))) + // ugh -> use commonx + if ((next && ! (next->relative_coordinate (0, X_AXIS) > here->relative_coordinate (0, X_AXIS))) || + (prev && ! (prev->relative_coordinate (0, X_AXIS) < here->relative_coordinate (0, X_AXIS)))) programming_error ("Beams are not left-to-right"); - Real staffline_f = paper_l ()->get_var ("stafflinethickness"); - Real interbeam_f = paper_l ()->interbeam_f (multiple_i_); + int multiplicity = get_multiplicity (me); - Real internote_f = here->staff_line_leading_f ()/2; - Real beam_f = gh_scm2double (get_elt_property ("beam-thickness"));; - - Real dy = interbeam_f; - Real stemdx = staffline_f; - Real sl = slope_f_* internote_f; + SCM space_proc = me->get_grob_property ("space-function"); + SCM space = gh_call1 (space_proc, gh_int2scm (multiplicity)); + Real thick = gh_scm2double (me->get_grob_property ("thickness")) ; + Real interbeam_f = gh_scm2double (space) ; + + Real bdy = interbeam_f; + +#if 0 + // ugh -> use commonx + Real dx = visible_stem_count (me) ? + last_visible_stem (me)->relative_coordinate (0, X_AXIS) - first_visible_stem (me)->relative_coordinate (0, X_AXIS) + : 0.0; +#endif + Molecule leftbeams; Molecule rightbeams; - // UGH Real nw_f; - if (!here->first_head ()) + if (!Stem::first_head (here)) nw_f = 0; - else if (here->type_i ()== 1) - nw_f = paper_l ()->get_var ("wholewidth"); - else if (here->type_i () == 2) - nw_f = paper_l ()->get_var ("notewidth") * 0.8; - else - nw_f = paper_l ()->get_var ("quartwidth"); + else { + int t = Stem::type_i (here); + + SCM proc = me->get_grob_property ("flag-width-function"); + SCM result = gh_call1 (proc, gh_int2scm (t)); + nw_f = gh_scm2double (result); + } + + Direction dir = Directional_element_interface::get (me); + + /* [Tremolo] beams on whole notes may not have direction set? */ + if (dir == CENTER) + dir = Directional_element_interface::get (here); + /* half beams extending to the left. */ if (prev) { - int lhalfs= lhalfs = here->beams_i_drul_[LEFT] - prev->beams_i_drul_[RIGHT] ; - int lwholebeams= here->beams_i_drul_[LEFT] beams_i_drul_[RIGHT] ; + int lhalfs= lhalfs = Stem::beam_count (here,LEFT) - Stem::beam_count (prev,RIGHT); + int lwholebeams= Stem::beam_count (here,LEFT) hpos_f () - prev->hpos_f (); + + // FIXME: TODO (check) stem width / sloped beams + Real w = here->relative_coordinate (0, X_AXIS) + - prev->relative_coordinate (0, X_AXIS); + Real stem_w = gh_scm2double (prev->get_grob_property ("thickness")) + // URG + * me->paper_l ()->get_var ("stafflinethickness"); + w = w/2 beam (sl, w, beam_f); - a.translate (Offset (-w, -w * sl)); + a = Lookup::beam (dydx, w + stem_w, thick); + a.translate (Offset (-w, -w * dydx)); + a.translate_axis (-stem_w/2, X_AXIS); for (int j = 0; j < lhalfs; j++) { Molecule b (a); - b.translate_axis (-get_direction () * dy * (lwholebeams+j), Y_AXIS); + b.translate_axis (-dir * bdy * (lwholebeams+j), Y_AXIS); leftbeams.add_molecule (b); } } if (next) { - int rhalfs = here->beams_i_drul_[RIGHT] - next->beams_i_drul_[LEFT]; - int rwholebeams = here->beams_i_drul_[RIGHT] beams_i_drul_[LEFT]; + int rhalfs = Stem::beam_count (here,RIGHT) + - Stem::beam_count (next,LEFT); + int rwholebeams= Stem::beam_count (here,RIGHT) + relative_coordinate (0, X_AXIS) + - here->relative_coordinate (0, X_AXIS); - Real w = next->hpos_f () - here->hpos_f (); - Molecule a = lookup_l ()->beam (sl, w + stemdx, beam_f); - a.translate_axis( - stemdx/2, X_AXIS); + Real stem_w = gh_scm2double (next->get_grob_property ("thickness")) + // URG + * me->paper_l ()->get_var ("stafflinethickness"); + + Molecule a = Lookup::beam (dydx, w + stem_w, thick); + a.translate_axis (- stem_w/2, X_AXIS); int j = 0; Real gap_f = 0; - - SCM gap = get_elt_property ("beam-gap"); - if (gap != SCM_UNDEFINED) + + SCM gap = me->get_grob_property ("gap"); + if (gh_number_p (gap)) { - int gap_i = gh_scm2int ( (gap)); + int gap_i = gh_scm2int ((gap)); int nogap = rwholebeams - gap_i; for (; j < nogap; j++) { Molecule b (a); - b.translate_axis (-get_direction () * dy * j, Y_AXIS); + b.translate_axis (-dir * bdy * j, Y_AXIS); rightbeams.add_molecule (b); } - // TODO: notehead widths differ for different types - gap_f = nw_f / 2; + if (Stem::invisible_b (here)) + gap_f = nw_f; + else + gap_f = nw_f / 2; w -= 2 * gap_f; - a = lookup_l ()->beam (sl, w + stemdx, beam_f); + a = Lookup::beam (dydx, w + stem_w, thick); } for (; j < rwholebeams; j++) { Molecule b (a); - if (!here->invisible_b ()) - b.translate (Offset (gap_f, -get_direction () * dy * j)); + Real tx = 0; + if (Stem::invisible_b (here)) + // ugh, see chord-tremolo.ly + tx = (-dir + 1) / 2 * nw_f * 1.5 + gap_f/4; else - b.translate (Offset (0, -get_direction () * dy * j)); + tx = gap_f; + b.translate (Offset (tx, -dir * bdy * j)); rightbeams.add_molecule (b); } w = w/2 beam (sl, w, beam_f); + a = Lookup::beam (dydx, w, thick); for (; j < rwholebeams + rhalfs; j++) { Molecule b (a); - b.translate_axis (-get_direction () * dy * j, Y_AXIS); + b.translate_axis (- dir * bdy * j, Y_AXIS); rightbeams.add_molecule (b); } @@ -820,4 +911,203 @@ Beam::stem_beams (Stem *here, Stem *next, Stem *prev) const return leftbeams; } +MAKE_SCHEME_CALLBACK (Beam,brew_molecule,1); +SCM +Beam::brew_molecule (SCM smob) +{ + Grob * me =unsmob_grob (smob); + + Molecule mol; + if (!gh_pair_p (me->get_grob_property ("stems"))) + return SCM_EOL; + Real x0,dx; + Link_arraystems = + Pointer_group_interface__extract_elements (me, (Item*) 0, "stems"); + if (visible_stem_count (me)) + { + // ugh -> use commonx + x0 = first_visible_stem (me)->relative_coordinate (0, X_AXIS); + dx = last_visible_stem (me)->relative_coordinate (0, X_AXIS) - x0; + } + else + { + x0 = stems[0]->relative_coordinate (0, X_AXIS); + dx = stems.top ()->relative_coordinate (0, X_AXIS) - x0; + } + + + + /* + TODO: the naming of the grob properties sucks. + */ + SCM dy_s = me->get_grob_property ("dy"); + SCM y_s = me->get_grob_property ("y"); + + + Real dy = gh_number_p (dy_s) ? gh_scm2double (dy_s) : 0.0; + Real dydx = dy && dx ? dy/dx : 0; + Real y = gh_number_p (y_s) ? gh_scm2double (y_s) : 0.0; + + + for (int j=0; j 0)? stems[j-1] : 0; + Item * next = (j < stems.size ()-1) ? stems[j+1] :0; + + Molecule sb = stem_beams (me, i, next, prev, dy, dydx); + Real x = i->relative_coordinate (0, X_AXIS)-x0; + sb.translate (Offset (x, x * dydx + y)); + mol.add_molecule (sb); + } + mol.translate_axis (x0 + - dynamic_cast (me)->get_bound (LEFT)->relative_coordinate (0, X_AXIS), X_AXIS); + + return mol.smobbed_copy (); +} + +int +Beam::forced_stem_count (Grob*me) +{ + Link_arraystems = + Pointer_group_interface__extract_elements (me, (Item*) 0, "stems"); + int f = 0; + for (int i=0; i < stems.size (); i++) + { + Item *s = stems[i]; + + if (Stem::invisible_b (s)) + continue; + + if (( (int)Stem::chord_start_f (s)) + && (Stem::get_direction (s) != Stem::get_default_dir (s))) + f++; + } + return f; +} + + + + +/* TODO: + use filter and standard list functions. + */ +int +Beam::visible_stem_count (Grob*me) +{ + Link_arraystems = + Pointer_group_interface__extract_elements (me, (Item*) 0, "stems"); + int c = 0; + for (int i = stems.size (); i--;) + { + if (!Stem::invisible_b (stems[i])) + c++; + } + return c; +} + +Item* +Beam::first_visible_stem (Grob*me) +{ + Link_arraystems = + Pointer_group_interface__extract_elements (me, (Item*) 0, "stems"); + + for (int i = 0; i < stems.size (); i++) + { + if (!Stem::invisible_b (stems[i])) + return stems[i]; + } + return 0; +} + +Item* +Beam::last_visible_stem (Grob*me) +{ + Link_arraystems = + Pointer_group_interface__extract_elements (me, (Item*) 0, "stems"); + for (int i = stems.size (); i--;) + { + if (!Stem::invisible_b (stems[i])) + return stems[i]; + } + return 0; +} + + +/* + [TODO] + handle rest under beam (do_post: beams are calculated now) + what about combination of collisions and rest under beam. + + Should lookup + + rest -> stem -> beam -> interpolate_y_position () +*/ +MAKE_SCHEME_CALLBACK (Beam,rest_collision_callback,2); +SCM +Beam::rest_collision_callback (SCM element_smob, SCM axis) +{ + Grob *rest = unsmob_grob (element_smob); + Axis a = (Axis) gh_scm2int (axis); + + assert (a == Y_AXIS); + + Grob * st = unsmob_grob (rest->get_grob_property ("stem")); + Grob * stem = st; + if (!stem) + return gh_double2scm (0.0); + Grob * beam = unsmob_grob (stem->get_grob_property ("beam")); + if (!beam || !Beam::has_interface (beam) || !Beam::visible_stem_count (beam)) + return gh_double2scm (0.0); + + // make callback for rest from this. + Real beam_dy = 0; + Real beam_y = 0; + + + // todo: make sure this calced already. + SCM s = beam->get_grob_property ("dy"); + if (gh_number_p (s)) + beam_dy = gh_scm2double (s); + + s = beam->get_grob_property ("y"); + if (gh_number_p (s)) + beam_y = gh_scm2double (s); + + // ugh -> use commonx + Real x0 = first_visible_stem (beam)->relative_coordinate (0, X_AXIS); + Real dx = last_visible_stem (beam)->relative_coordinate (0, X_AXIS) - x0; + Real dydx = beam_dy && dx ? beam_dy/dx : 0; + + Direction d = Stem::get_direction (stem); + Real beamy = (stem->relative_coordinate (0, X_AXIS) - x0) * dydx + beam_y; + + Real staff_space = Staff_symbol_referencer::staff_space (rest); + + + Real rest_dim = rest->extent (rest, Y_AXIS)[d]*2.0 / staff_space ; // refp?? + + Real minimum_dist + = gh_scm2double (rest->get_grob_property ("minimum-beam-collision-distance")); + Real dist = + minimum_dist + -d * (beamy - rest_dim) >? 0; + + int stafflines = Staff_symbol_referencer::line_count (rest); + + // move discretely by half spaces. + int discrete_dist = int (ceil (dist)); + + // move by whole spaces inside the staff. + if (discrete_dist < stafflines+1) + discrete_dist = int (ceil (discrete_dist / 2.0)* 2.0); + + return gh_double2scm (-d * discrete_dist); +} + + +bool +Beam::has_interface (Grob*me) +{ + return me->has_interface (ly_symbol2scm ("beam-interface")); +}