X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=lily%2Fbeam.cc;h=25f90cb6eb3e09ddbf428b817cfc2f5f2442d325;hb=07a5ed85c189a97d04c550679826dfc5eca2eb18;hp=a9dd24bda379306a3309e03769e52b883e604fab;hpb=9efbad2d9487a05b04423e7e9f062968e8f8eaf4;p=lilypond.git diff --git a/lily/beam.cc b/lily/beam.cc index a9dd24bda3..25f90cb6eb 100644 --- a/lily/beam.cc +++ b/lily/beam.cc @@ -3,154 +3,114 @@ source file of the GNU LilyPond music typesetter - (c) 1997--1998, 1998 Han-Wen Nienhuys + (c) 1997--2000 Han-Wen Nienhuys Jan Nieuwenhuizen */ - /* [TODO] - * centre beam symbol + * shorter! (now +- 1000 lines) * less hairy code - * redo grouping - */ + * move paper vars to scm + + remove *-hs variables, and do all y-position stuff in staff-space. +*/ + -#include +#include // tanh. -#include "p-col.hh" -#include "array.hh" -#include "proto.hh" -#include "dimensions.hh" +#include "molecule.hh" +#include "directional-element-interface.hh" +#include "beaming.hh" #include "beam.hh" -#include "abbreviation-beam.hh" #include "misc.hh" -#include "debug.hh" -#include "atom.hh" -#include "molecule.hh" -#include "leastsquares.hh" +#include "least-squares.hh" #include "stem.hh" #include "paper-def.hh" #include "lookup.hh" -#include "grouping.hh" -#include "stem-info.hh" - - -IMPLEMENT_IS_TYPE_B1 (Beam, Spanner); - -Beam::Beam () -{ - slope_f_ = 0; - solved_slope_f_ = 0; - left_y_ = 0; - damping_i_ = 1; - quantisation_ = NORMAL; - multiple_i_ = 0; -} +#include "group-interface.hh" +#include "staff-symbol-referencer.hh" +#include "cross-staff.hh" +#include "item.hh" +#include "spanner.hh" +#include "warn.hh" void -Beam::add_stem (Stem*s) +Beam::add_stem (Grob*me, Grob*s) { - stems_.push (s); - s->add_dependency (this); - s->beam_l_ = this; - - if (!spanned_drul_[LEFT]) - set_bounds (LEFT,s); - else - set_bounds (RIGHT,s); -} - -Molecule* -Beam::brew_molecule_p () const -{ - Molecule *mol_p = new Molecule; - - Real internote_f = paper ()->internote_f (); + Pointer_group_interface:: add_element(me, "stems", s); + + s->add_dependency (me); - Real x0 = stems_[0]->hpos_f (); - for (int j=0; j 0)? stems_[j-1] : 0; - Stem * next = (j < stems_.size ()-1) ? stems_[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_) * internote_f)); - mol_p->add_molecule (sb); - } - mol_p->translate_axis (x0 - - spanned_drul_[LEFT]->absolute_coordinate (X_AXIS), X_AXIS); + assert (!Stem::beam_l (s)); + s->set_grob_property ("beam", me->self_scm ()); - return mol_p; + add_bound_item (dynamic_cast (me), dynamic_cast (s)); } -Offset -Beam::center () const +int +Beam::get_multiplicity (Grob*me) { - Real w= (paper ()->note_width () + width ().length ())/2.0; - return Offset (w, (left_y_ + w* slope_f_)*paper ()->internote_f ()); -} + 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)); -void -Beam::do_pre_processing () -{ - if (!dir_) - set_default_dir (); + if (Stem::has_interface (sc)) + m = m >? Stem::beam_count (sc,LEFT) >? Stem::beam_count (sc,RIGHT); + } + return m; } -void -Beam::do_print () const +/* + 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) { -#ifndef NPRINT - DOUT << "slope_f_ " << slope_f_ << "left ypos " << left_y_; - Spanner::do_print (); -#endif -} + Grob * me = unsmob_grob (smob); -void -Beam::do_post_processing () -{ - if (stems_.size () < 2) + // Why? + if (visible_stem_count (me) < 2) { - warning (_ ("beam with less than two stems")); - transparent_b_ = true; - return ; + warning (_ ("beam has less than two stems")); } - solve_slope (); - set_stemlens (); -} -void -Beam::do_substitute_dependent (Score_element*o,Score_element*n) -{ - if (o->is_type_b (Stem::static_name ())) - stems_.substitute ((Stem*)dynamic_cast (o), n? (Stem*) dynamic_cast (n):0); -} + if (!Directional_element_interface::get (me)) + Directional_element_interface::set (me, get_default_dir (me)); -Interval -Beam::do_width () const -{ - return Interval (stems_[0]->hpos_f (), - stems_.top ()->hpos_f ()); + auto_knees (me); + set_stem_directions (me); + set_stem_shorten (me); + + return SCM_EOL; } -void -Beam::set_default_dir () +Direction +Beam::get_default_dir (Grob*me) { Drul_array total; total[UP] = total[DOWN] = 0; Drul_array count; count[UP] = count[DOWN] = 0; Direction d = DOWN; - - for (int i=0; i dir_ - ? (1 + d * s->dir_)/2 - : s->get_center_distance ((Direction)-d); + + 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); - /* - [Ross] states that the majority of the notes dictates the - direction (and not the mean of "center distance") - - 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. + /* + If dir is not determined: get default */ + return to_dir (me->get_grob_property ("default-neutral-direction")); +} - Dir_algorithm a = (Dir_algorithm)rint(paper ()->get_var ("beam_dir_algorithm")); - switch (a) + +/* + 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) +{ + Link_array stems + =Pointer_group_interface__extract_elements (me, (Item*) 0, "stems"); + Direction d = Directional_element_interface::get (me); + + for (int i=0; i count[DOWN]) ? UP : DOWN; - break; - case MEAN: - // mean centre distance - dir_ = (total[UP] > total[DOWN]) ? UP : DOWN; - break; - default: - case MEDIAN: - // median centre distance - if (!count[UP]) - dir_ = DOWN; - else if (!count[DOWN]) - dir_ = UP; - else - dir_ = (total[UP] / count[UP] > total[DOWN] / count[DOWN]) ? UP : DOWN; - break; + Grob *s = stems[i]; + SCM force = s->remove_grob_property ("dir-forced"); + if (!gh_boolean_p (force) || !gh_scm2bool (force)) + Directional_element_interface ::set (s,d); } +} + +void +Beam::auto_knees (Grob*me) +{ + if (!auto_knee (me,"auto-interstaff-knee-gap", true)) + auto_knee (me, "auto-knee-gap", false); +} + +/* + Simplistic auto-knees; only consider vertical gap between two + adjacent chords. + + `Forced' stem directions are ignored. If you don't want auto-knees, + don't set, or unset autoKneeGap/autoInterstaffKneeGap. + */ +bool +Beam::auto_knee (Grob*me, String gap_str, bool interstaff_b) +{ + bool knee_b = false; + int knee_y = 0; + SCM gap = me->get_grob_property (gap_str.ch_C()); - for (int i=0; i stems= + Pointer_group_interface__extract_elements (me, (Item*)0, "stems"); + + if (gh_number_p (gap)) + { + Spanner*sp = dynamic_cast (me); + int auto_gap_i = gh_scm2int (gap); + for (int i=1; i < stems.size (); i++) + { + bool is_b = (bool)(calc_interstaff_dist (stems[i], sp) + - calc_interstaff_dist (stems[i-1], sp)); + int l_y = (int)(Stem::head_positions(stems[i-1])[d]) + + (int)calc_interstaff_dist (stems[i-1], sp); + int r_y = (int)(Stem::head_positions(stems[i])[d]) + + (int)calc_interstaff_dist (stems[i], sp); + int gap_i = r_y - l_y; + + if ((abs (gap_i) >= auto_gap_i) && (!interstaff_b || is_b)) + { + knee_y = (r_y + l_y) / 2; + knee_b = true; + break; + } + } + } + if (knee_b) { - Stem *s = stems_[i]; - s->beam_dir_ = dir_; - if (!s->dir_forced_b_) - s->dir_ = dir_; + for (int i=0; i < stems.size (); i++) + { + Item *s = stems[i]; + int y = (int)(Stem::head_positions(s)[d]) + + (int)calc_interstaff_dist (s, dynamic_cast (me)); + + Directional_element_interface::set (s,y < knee_y ? UP : DOWN); + s->set_grob_property ("dir-forced", SCM_BOOL_T); + } } + return knee_b; } /* - See Documentation/tex/fonts.doc + Set stem's shorten property if unset. + TODO: + take some y-position (chord/beam/nearest?) into account + scmify forced-fraction */ void -Beam::solve_slope () +Beam::set_stem_shorten (Grob*m) { - /* - should use minimum energy formulation (cf linespacing) - */ + Spanner*me = dynamic_cast (m); + if (!visible_stem_count (me)) + return; - assert (multiple_i_); - Array sinfo; - DOUT << "Beam::solve_slope: \n"; - for (int j=0; j mult_i_ = multiple_i_; - i->set_default_extents (); - if (i->invisible_b ()) - continue; + int multiplicity = get_multiplicity (me); - Stem_info info (i); - sinfo.push (info); - } - if (! sinfo.size ()) - slope_f_ = left_y_ = 0; - else if (sinfo.size () == 1) + SCM shorten = me->get_grob_property ("beamed-stem-shorten"); + if (shorten == SCM_EOL) + return; + + 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++) { - slope_f_ = 0; - left_y_ = sinfo[0].idealy_f_; + 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)); } - else - { - Real leftx = sinfo[0].x_; - Least_squares l; - for (int i=0; i < sinfo.size (); i++) - { - sinfo[i].x_ -= leftx; - l.input.push (Offset (sinfo[i].x_, sinfo[i].idealy_f_)); - } +} - l.minimise (slope_f_, left_y_); +/* + Set elt properties height and y-position if not set. + Adjust stem lengths to reach beam. + */ +MAKE_SCHEME_CALLBACK(Beam,after_line_breaking,1); +SCM +Beam::after_line_breaking (SCM smob) +{ + Grob * me = unsmob_grob (smob); - } + /* first, calculate y, dy */ + Real y, dy; + calc_default_position_and_height (me, &y, &dy); + if (visible_stem_count (me)) + { + if (suspect_slope_b (me, y, dy)) + dy = 0; - solved_slope_f_ = dir_ * slope_f_; + Real damped_dy = calc_slope_damping_f (me, dy); + Real quantised_dy = quantise_dy_f (me, damped_dy); + y += (dy - quantised_dy) / 2; + dy = quantised_dy; + } /* - This neat trick is by Werner Lemberg, damped = tanh (slope_f_) corresponds - with some tables in [Wanske] - */ - if (damping_i_) - slope_f_ = 0.6 * tanh (slope_f_) / damping_i_; - - /* - [TODO] - think - - dropping lq for stemlengths solves [d d d] [d g d] "bug..." + until here, we used only stem_info, which acts as if dir=up + */ + y *= Directional_element_interface::get (me); + dy *= Directional_element_interface::get (me); - but may be a bit too crude, and result in lots of - too high beams... - perhaps only if slope = 0 ? - */ + Real half_space = Staff_symbol_referencer::staff_space (me) / 2; -// left_y_ = sinfo[0].minyf_; + /* weird: why do we do calc_position_and_height () ? regardless of + this setting? - if (sinfo.size () >= 1) + */ + /* check for user-override of dy */ + SCM s = me->remove_grob_property ("height-hs"); + if (gh_number_p (s)) { - Real staffline_f = paper ()->rule_thickness (); - Real epsilon_f = staffline_f / 8; - if (abs (slope_f_) < epsilon_f) - left_y_ = (sinfo[0].idealy_f_ + sinfo.top ().idealy_f_) / 2; - else - /* - symmetrical, but results often in having stemlength = minimal - - left_y_ = sinfo[0].dir_ == dir_ ? sinfo[0].miny_f_ : sinfo[0].maxy_f_; - - what about - */ - { - Real dx = stems_.top ()->hpos_f () - stems_[0]->hpos_f (); - if (sinfo[0].dir_ == sinfo.top ().dir_) - left_y_ = sinfo[0].idealy_f_ >? sinfo.top ().idealy_f_ - slope_f_ * dx; - // knee - else - left_y_ = sinfo[0].idealy_f_; - } + dy = gh_scm2double (s) * half_space; } + me->set_grob_property ("height", gh_double2scm (dy)); - // uh? - Real dy = 0.0; - for (int i=0; i < sinfo.size (); i++) + /* check for user-override of y */ + s = me->remove_grob_property ("y-position-hs"); + if (gh_number_p (s)) { - Real y = sinfo[i].x_ * slope_f_ + left_y_; - Real my = sinfo[i].miny_f_; - - if (my - y > dy) - dy = my -y; + y = gh_scm2double (s) * half_space; + } + else + { + /* we can modify y, so we should quantise y */ + Real y_shift = check_stem_length_f (me, y, dy); + y += y_shift; + y = quantise_y_f (me,y, dy, 0); + set_stem_length (me, y, dy); + y_shift = check_stem_length_f (me, y, dy); + + if (y_shift > half_space / 4) + { + y += y_shift; + + /* + for significantly lengthened or shortened stems, + request quanting the other way. + */ + 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); + } } - left_y_ += dy; - left_y_ *= dir_; - slope_f_ *= dir_; + // UGH. Y is not in staff position unit? + // Ik dacht datwe daar juist van weg wilden? + set_stem_length (me, y, dy); + me->set_grob_property ("y-position", gh_double2scm (y)); - quantise_dy (); + return SCM_UNSPECIFIED; } +/* + See Documentation/tex/fonts.doc + */ void -Beam::quantise_dy () +Beam::calc_default_position_and_height (Grob*me,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 - */ - - if (quantisation_ <= NONE) + *y = 0; + *dy = 0; + if (visible_stem_count (me) <= 1) return; - Real interline_f = paper ()->interline_f (); - Real internote_f = interline_f / 2; - Real staffline_f = paper ()->rule_thickness (); - Real beam_f = paper ()->beam_thickness_f (); + 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_) + { + *dy = 0; + *y = first_ideal; + return; + } - Real dx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f (); + Array ideals; - // dim(y) = internote; so slope = (y/internote)/x - Real dy_f = dx_f * abs (slope_f_ * internote_f); - - Real quanty_f = 0.0; + // 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"); - /* UGR. ICE in 2.8.1; bugreport filed. */ - Array allowed_fraction (3); - allowed_fraction[0] = 0; - allowed_fraction[1] = (beam_f / 2 + staffline_f / 2); - allowed_fraction[2] = (beam_f + staffline_f); + for (int i=0; i < stems.size (); i++) + { + 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); // duh, takes references + Real dx = last_visible_stem (me)->relative_coordinate (0, X_AXIS) - x0; + *dy = dydx * dx; +} - Interval iv = quantise_iv (allowed_fraction, interline_f, dy_f); - quanty_f = (dy_f - iv.min () <= iv.max () - dy_f) - ? iv.min () - : iv.max (); +bool +Beam::suspect_slope_b (Grob*me, Real y, Real dy) +{ + /* first, calculate y, dy */ + /* + 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; - slope_f_ = (quanty_f / dx_f) / internote_f * sign (slope_f_); + if (((y - first_ideal > lengthened) && (dydx > steep)) + || ((y + dy - last_ideal > lengthened) && (dydx < -steep))) + { + return true; + } + return false; } -static int test_pos = 0; +/* + This neat trick is by Werner Lemberg, + damped = tanh (slope) + corresponds with some tables in [Wanske] +*/ +Real +Beam::calc_slope_damping_f (Grob*me,Real dy) +{ + SCM damp = me->get_grob_property ("damping"); + int damping = gh_scm2int (damp); + if (damping) + { + // 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; + return dydx * dx; + } + return dy; +} -/* - - Prevent interference from stafflines and beams. See Documentation/tex/fonts.doc - - */ -void -Beam::quantise_left_y (bool extend_b) +Real +Beam::calc_stem_y_f (Grob*me,Item* s, Real y, Real dy) { - /* - we only need to quantise the start of the beam as dy is quantised too - if extend_b then stems must *not* get shorter - */ + int beam_multiplicity = get_multiplicity (me); + int stem_multiplicity = (Stem::flag_i (s) - 2) >? 0; - if (quantisation_ <= NONE) - return; + SCM space_proc = me->get_grob_property ("space-function"); + SCM space = gh_call1 (space_proc, gh_int2scm (beam_multiplicity)); - /* - ---------------------------------------------------------- - ######## - ######## - ######## - --------------########------------------------------------ - ######## - - hang straddle sit inter hang - */ + Real thick = gh_scm2double (me->get_grob_property ("thickness")) ; + Real interbeam_f = gh_scm2double (space) ; - Real interline_f = paper ()->interline_f (); - Real internote_f = paper ()->internote_f (); - Real staffline_f = paper ()->rule_thickness (); - Real beam_f = paper ()->beam_thickness_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; - /* - [TODO] - it would be nice to have all allowed positions in a runtime matrix: - (multiplicity, minimum_beam_dy, maximum_beam_dy) - */ + /* 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); - Real straddle = 0; - Real sit = beam_f / 2 - staffline_f / 2; - Real inter = interline_f / 2; - Real hang = interline_f - beam_f / 2 + staffline_f / 2; - /* - Put all allowed positions into an array. - Whether a position is allowed or not depends on - strictness of quantisation, multiplicity and direction. + + // 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; + } - For simplicity, we'll assume dir = UP and correct if - dir = DOWN afterwards. - */ + return stem_y; +} - // dim(left_y_) = internote - Real dy_f = dir_ * left_y_ * internote_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); - Real beamdx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f (); - Real beamdy_f = beamdx_f * slope_f_ * internote_f; + Link_array stems= + Pointer_group_interface__extract_elements (me, (Item*)0, "stems"); - Array allowed_position; - if (quantisation_ != TEST) + for (int i=0; i < stems.size(); i++) { - if (quantisation_ <= 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 - // 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); - } - } - else - { - if (test_pos == 0) - { - allowed_position.push (hang); - cout << "hang" << hang << endl; - } - else if (test_pos==1) - { - allowed_position.push (straddle); - cout << "straddle" << straddle << endl; - } - else if (test_pos==2) - { - allowed_position.push (sit); - cout << "sit" << sit << endl; - } - else if (test_pos==3) - { - allowed_position.push (inter); - cout << "inter" << inter << endl; - } - } + Item* s = stems[i]; + if (Stem::invisible_b (s)) + continue; -#if 0 - // this currently never happens - Real q = (dy_f / interline_f - dy_i) * interline_f; - if ((quantisation_ < NORMAL) && (q < interline_f / 3 - beam_f / 2)) - allowed_position.push (inter); -#endif + Real stem_y = calc_stem_y_f (me, s, y, dy); + + stem_y *= dir; + Stem_info info = Stem::calc_stem_info (s); - Interval iv = quantise_iv (allowed_position, interline_f, dy_f); + // if (0 > info.maxy_f_ - stem_y) + shorten = shorten ? info.miny_f_ - stem_y; + } - Real quanty_f = dy_f - iv.min () <= iv.max () - dy_f ? iv.min () : iv.max (); - if (extend_b) - quanty_f = iv.max (); + if (lengthen && shorten) + warning (_ ("weird beam vertical offset")); - // dim(left_y_) = internote - left_y_ = dir_ * quanty_f / internote_f; + /* when all stems are too short, normal stems win */ + return dir * ((shorten) ? shorten : lengthen); } +/* + 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::set_stemlens () +Beam::set_stem_length (Grob*me,Real y, Real dy) { - Real staffline_f = paper ()->rule_thickness (); - Real interbeam_f = paper ()->interbeam_f (multiple_i_); - Real internote_f = paper ()->internote_f (); - Real beam_f = paper ()->beam_thickness_f (); - - // enge floots - Real epsilon_f = staffline_f / 8; + Real half_space = Staff_symbol_referencer::staff_space (me)/2; + Link_array stems= + Pointer_group_interface__extract_elements (me, (Item*)0, "stems"); - /* - Damped and quantised slopes, esp. in monotone scales such as + for (int i=0; i < stems.size (); i++) + { + Item* s = stems[i]; + if (Stem::invisible_b (s)) + continue; - [c d e f g a b c] + Real stem_y = calc_stem_y_f (me, s, y, dy); - will soon produce the minimal stem-length for one of the extreme - stems, which is wrong (and ugly). The minimum stemlength should - be kept rather small, in order to handle extreme beaming, such as + /* caution: stem measures in staff-positions */ + Stem::set_stemend (s,(stem_y + calc_interstaff_dist (s, dynamic_cast (me))) / half_space); + } +} - [c c' 'c] %assuming no knee - - correctly. - To avoid these short stems for normal cases, we'll correct for - the loss in slope, if necessary. - - [TODO] - ugh, another hack. who's next? - Writing this all down, i realise (at last) that the Right Thing to - do is to assign uglyness to slope and stem-lengths and then minimise - the total uglyness of a beam. - Steep slopes are ugly, shortened stems are ugly, lengthened stems - are ugly. - How to do this? - - */ +/* + [Ross] (simplification of) + Set dy complying with: + - zero + - thick / 2 + staffline_f / 2 + - thick + staffline_f + + n * staff_space +*/ +Real +Beam::quantise_dy_f (Grob*me,Real dy) +{ + Array a; - Real dx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f (); - Real damp_correct_f = paper ()->get_var ("beam_slope_damp_correct_factor"); - Real damped_slope_dy_f = (solved_slope_f_ - slope_f_) * dx_f - * sign (slope_f_); - damped_slope_dy_f *= damp_correct_f; - if (damped_slope_dy_f <= epsilon_f) - damped_slope_dy_f = 0; - - DOUT << "Beam::set_stemlens: \n"; - Real x0 = stems_[0]->hpos_f (); - Real dy_f = 0; - // urg - for (int jj = 0; jj < 10; jj++) - { - left_y_ += dy_f * dir_; - quantise_left_y (dy_f); - dy_f = 0; - for (int i=0; i < stems_.size (); i++) - { - Stem *s = stems_[i]; - if (s->transparent_b_) - continue; - - Real x = s->hpos_f () - x0; - // urg move this to stem-info - Real sy = left_y_ + slope_f_ * x; - if (dir_ != s->dir_) - sy -= dir_ * (beam_f / 2 - + (s->mult_i_ - 1) * interbeam_f) / internote_f; - s->set_stemend (sy); - Real y = s->stem_end_f () * dir_; - Stem_info info (s); - if (y > info.maxy_f_) - dy_f = dy_f ? info.miny_f_ - y; - } - } - if (damped_slope_dy_f && (dy_f >= 0)) - dy_f += damped_slope_dy_f; - damped_slope_dy_f = 0; - if (abs (dy_f) <= epsilon_f) - { - DOUT << "Beam::set_stemlens: " << jj << " iterations\n"; - break; - } - } + 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) + return dy; - test_pos++; - test_pos %= 4; + 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]; + + return q * sign (dy); } /* - FIXME - ugh. this is broken and should be rewritten. - - [c8. c32 c32] + Prevent interference from stafflines and beams. + See Documentation/tex/fonts.doc + + We only need to quantise the (left) y-position of the beam, + since dy is quantised too. + if extend_b then stems must *not* get shorter */ -void -Beam::set_grouping (Rhythmic_grouping def, Rhythmic_grouping cur) +Real +Beam::quantise_y_f (Grob*me,Real y, Real dy, int quant_dir) { - def.OK (); - cur.OK (); - assert (cur.children.size () == stems_.size ()); + int multiplicity = get_multiplicity (me); - cur.split (def); + Real staff_space = Staff_symbol_referencer::staff_space (me); + Real thick = me->paper_l ()->get_var ("stafflinethickness"); - Array b; - { - Array flags; - for (int j=0; j flag_i_ - 2; - assert (f>0); - flags.push (f); - } - int fi =0; - b= cur.generate_beams (flags, fi); - b.insert (0,0); - b.push (0); - assert (stems_.size () == b.size ()/2); - } + 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; + + for (; gh_pair_p (quants); quants = gh_cdr (quants)) + a.push (gh_scm2double (gh_car (quants))); + + if (a.size () <= 1) + return y; + + Real up_y = Directional_element_interface::get (me) * y; + Interval iv = quantise_iv (a, up_y/staff_space) * staff_space; - for (int j=0, i=0; i < b.size () && j stems= + Pointer_group_interface__extract_elements (me, (Grob*)0, "stems"); + + Direction d = LEFT; + for (int i=0; i < stems.size(); i++) { - Stem *s = stems_[j]; - s->beams_left_i_ = b[i]; - s->beams_right_i_ = b[i+1]; - multiple_i_ = multiple_i_ >? (b[i] >? b[i+1]); + do + { + if (Stem::beam_count (stems[i], d) == 0) + Stem::set_beaming ( stems[i], beaming->infos_.elem (i).beams_i_drul_[d],d); + } + while (flip (&d) != LEFT); } } + + /* beams to go with one stem. + + 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) { - assert (!next || next->hpos_f () > here->hpos_f ()); - assert (!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 ()->rule_thickness (); - Real interbeam_f = paper ()->interbeam_f (multiple_i_); - Real internote_f = paper ()->internote_f (); - Real beam_f = paper ()->beam_thickness_f (); + Real staffline_f = me->paper_l ()->get_var ("stafflinethickness"); + int multiplicity = get_multiplicity (me); - Real dy = interbeam_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; Real stemdx = staffline_f; - Real sl = slope_f_* internote_f; - lookup_l ()->beam (sl, 20 PT, 1 PT); + + // 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; + Real dy = gh_scm2double (me->get_grob_property ("height")); + Real dydx = dy && dx ? dy/dx : 0; Molecule leftbeams; Molecule rightbeams; + Real nw_f; + if (!Stem::first_head (here)) + nw_f = 0; + 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); + /* half beams extending to the left. */ if (prev) { - int lhalfs= lhalfs = here->beams_left_i_ - prev->beams_right_i_ ; - int lwholebeams= here->beams_left_i_ beams_right_i_ ; + 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 (); - w = w/2 note_width (); - Atom a; + Real w = here->relative_coordinate (0, X_AXIS) - prev->relative_coordinate (0, X_AXIS); + w = w/2 beam (sl, w, beam_f); - a.translate (Offset (-w, -w * sl)); + a = Lookup::beam (dydx, w, thick); + a.translate (Offset (-w, -w * dydx)); for (int j = 0; j < lhalfs; j++) { - Atom b (a); - b.translate_axis (-dir_ * dy * (lwholebeams+j), Y_AXIS); - leftbeams.add_atom (b); + Molecule b (a); + b.translate_axis (-dir * bdy * (lwholebeams+j), Y_AXIS); + leftbeams.add_molecule (b); } } if (next) { - int rhalfs = here->beams_right_i_ - next->beams_left_i_; - int rwholebeams = here->beams_right_i_ beams_left_i_; + int rhalfs = Stem::beam_count (here,RIGHT) - Stem::beam_count (next,LEFT); + int rwholebeams= Stem::beam_count (here,RIGHT) hpos_f () - here->hpos_f (); - Atom a = lookup_l ()->beam (sl, w + stemdx, beam_f); + Real w = next->relative_coordinate (0, X_AXIS) - here->relative_coordinate (0, X_AXIS); + Molecule a = Lookup::beam (dydx, w + stemdx, thick); a.translate_axis( - stemdx/2, X_AXIS); int j = 0; Real gap_f = 0; - if (here->beam_gap_i_) + + SCM gap = me->get_grob_property ("gap"); + if (gh_number_p (gap)) { - int nogap = rwholebeams - here->beam_gap_i_; + int gap_i = gh_scm2int ( (gap)); + int nogap = rwholebeams - gap_i; + for (; j < nogap; j++) { - Atom b (a); - b.translate_axis (-dir_ * dy * j, Y_AXIS); - rightbeams.add_atom (b); + Molecule b (a); + b.translate_axis (-dir * bdy * j, Y_AXIS); + rightbeams.add_molecule (b); } // TODO: notehead widths differ for different types - gap_f = paper ()->note_width () / 2; + gap_f = nw_f / 2; w -= 2 * gap_f; - a = lookup_l ()->beam (sl, w + stemdx, beam_f); + a = Lookup::beam (dydx, w + stemdx, thick); } for (; j < rwholebeams; j++) { - Atom b (a); - b.translate (Offset (gap_f, -dir_ * dy * j)); - rightbeams.add_atom (b); + Molecule b (a); + b.translate (Offset (Stem::invisible_b (here) ? 0 : gap_f, -dir * bdy * j)); + rightbeams.add_molecule (b); } - w = w/2 note_width (); + w = w/2 beam (sl, w, beam_f); + a = Lookup::beam (dydx, w, thick); for (; j < rwholebeams + rhalfs; j++) { - Atom b (a); - b.translate_axis (-dir_ * dy * j, Y_AXIS); - rightbeams.add_atom (b); + Molecule b (a); + b.translate_axis (- dir * bdy * j, Y_AXIS); + rightbeams.add_molecule (b); } } @@ -716,3 +762,205 @@ 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; + } + + + Real dy = gh_scm2double (me->get_grob_property ("height")); + Real dydx = dy && dx ? dy/dx : 0; + Real y = gh_scm2double (me->get_grob_property ("y-position")); + + + 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); + 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 ("height"); + if (gh_number_p (s)) + beam_dy = gh_scm2double (s); + + s = beam->get_grob_property ("y-position"); + 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")); +} + +void +Beam::set_interface (Grob*me) +{ + /* + why the init? No way to tell difference between default and user + override. */ + me->set_grob_property ("height", gh_int2scm (0)); // ugh. + me->set_grob_property ("y-position" ,gh_int2scm (0)); + me->set_interface (ly_symbol2scm("beam-interface")); +}