X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=lily%2Fbeam.cc;h=027a08d70f0499b37ee6b50296c2fe31d8260470;hb=41e82f1eed38b69f60f74221c52ce14692318d6d;hp=ebda7c1ccd0eafb4c00d1291289aeb7ec88c38c7;hpb=c24b8835be7d548804a94f6caceadcf914bb8198;p=lilypond.git diff --git a/lily/beam.cc b/lily/beam.cc index ebda7c1ccd..027a08d70f 100644 --- a/lily/beam.cc +++ b/lily/beam.cc @@ -3,590 +3,836 @@ source file of the GNU LilyPond music typesetter - (c) 1997 Han-Wen Nienhuys + (c) 1997--2000 Han-Wen Nienhuys + Jan Nieuwenhuizen - TODO +*/ + +/* + [TODO] + * less hairy code + * move paper vars to scm - Less hairy code. knee: ([\stem 1; c8 \stem -1; c8] + remove *-hs variables. + */ -#include -#include "p-col.hh" -#include "varray.hh" -#include "proto.hh" -#include "dimen.hh" +#include // tanh. +#include "directional-element-interface.hh" +#include "beaming.hh" +#include "dimensions.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" -#include "main.hh" // experimental features - - -IMPLEMENT_IS_TYPE_B1 (Beam, Spanner); - -// ugh, hardcoded -const int MINIMUM_STEMLEN[] = { - 0, // just in case - 5, - 4, - 3, - 2, - 2, -}; +#include "group-interface.hh" +#include "staff-symbol-referencer.hh" +#include "cross-staff.hh" Beam::Beam () { - slope_f_ = 0; - left_y_ = 0.0; - damping_i_ = 1; - quantisation_ = NORMAL; - multiple_i_ = 0; + Group_interface g (this, "stems"); + g.set_interface (); + + set_elt_property ("height", gh_int2scm (0)); // ugh. + set_elt_property ("y-position" ,gh_int2scm (0)); } void -Beam::add (Stem*s) +Beam::add_stem (Stem*s) { - stems_.push (s); + Group_interface gi (this, "stems"); + gi.add_element (s); + s->add_dependency (this); - s->beam_l_ = this; - if (!spanned_drul_[LEFT]) - set_bounds (LEFT,s); + assert (!s->beam_l ()); + s->set_elt_property ("beam", self_scm_); + + if (!get_bound (LEFT)) + set_bound (LEFT,s); else - set_bounds (RIGHT,s); + set_bound (RIGHT,s); } -Molecule* -Beam::brew_molecule_p () const +int +Beam::get_multiplicity () const { - Molecule *mol_p = new Molecule; - Real inter_f = paper ()->internote_f (); - 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; + Score_element * sc = unsmob_element (gh_car (s)); - Molecule sb = stem_beams (i, next, prev); - Real x = i->hpos_f ()-x0; - sb.translate (Offset (x, (x * slope_f_ + left_y_)* inter_f)); - mol_p->add (sb); + if (Stem * st = dynamic_cast (sc)) + m = m >? st->beam_count (LEFT) >? st->beam_count (RIGHT); } - mol_p->translate_axis (x0 - spanned_drul_[LEFT]->absolute_coordinate (X_AXIS), X_AXIS); - return mol_p; -} - -Offset -Beam::center () const -{ - Real w= (paper ()->note_width () + width ().length ())/2.0; - return Offset (w, (left_y_ + w* slope_f_)*paper ()->internote_f ()); + return m; } +/* + 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'.] + */ void -Beam::do_pre_processing () +Beam::before_line_breaking () { - if (!dir_) - set_default_dir (); + // Why? + if (visible_stem_count () < 2) + { + warning (_ ("beam has less than two stems")); + // set_elt_property ("transparent", SCM_BOOL_T); + } + + if (!directional_element (this).get ()) + directional_element (this).set (get_default_dir ()); + + auto_knees (); + set_stem_directions (); + set_stem_shorten (); } -void -Beam::do_print () const +/* + FIXME + */ +Direction +Beam::get_default_dir () const { -#ifndef NPRINT - DOUT << "slope_f_ " < total; + total[UP] = total[DOWN] = 0; + Drul_array count; + count[UP] = count[DOWN] = 0; + Direction d = DOWN; + + for (int i=0; i get_center_distance ((Direction)-d); + + if (current) + { + total[d] += current; + count[d] ++; + } + + } while (flip(&d) != DOWN); + + + SCM s = scm_eval (gh_list (ly_symbol2scm ("beam-dir-algorithm"), + ly_quote_scm (gh_cons (gh_int2scm (count[UP]), + gh_int2scm (count[DOWN]))), + ly_quote_scm (gh_cons (gh_int2scm (total[UP]), + gh_int2scm (total[DOWN]))), + SCM_UNDEFINED)); + if (gh_number_p (s) && gh_scm2int (s)) + return to_dir (s); + + /* + If dir is not determined: get from paper + */ + return (Direction)(int) + paper_l ()->get_var ("stem_default_neutral_direction"); } + +/* + 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::do_post_processing () +Beam::set_stem_directions () { - if (stems_.size () < 2) + Direction d = directional_element (this).get (); + for (int i=0; i remove_elt_property ("dir-forced"); + if (!gh_boolean_p (force) || !gh_scm2bool (force)) + directional_element (s).set (d); } - solve_slope (); - set_stemlens (); -} +} void -Beam::do_substitute_dependent (Score_elem*o,Score_elem*n) +Beam::auto_knees () { - if (o->is_type_b (Stem::static_name ())) - stems_.substitute ((Stem*)o->item (), n? (Stem*) n->item ():0); + if (!auto_knee ("auto-interstaff-knee-gap", true)) + auto_knee ("auto-knee-gap", false); } -Interval -Beam::do_width () const +/* + 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 (String gap_str, bool interstaff_b) { - return Interval (stems_[0]->hpos_f (), - stems_.top ()->hpos_f ()); + bool knee_b = false; + int knee_y = 0; + SCM gap = get_elt_property (gap_str); + Direction d = directional_element (this).get (); + + if (gh_number_p (gap)) + { + int auto_gap_i = gh_scm2int (gap); + for (int i=1; i < stem_count (); i++) + { + bool is_b = (bool)(calc_interstaff_dist (stem (i), this) + - calc_interstaff_dist (stem (i-1), this)); + int l_y = (int)(stem (i-1)->head_positions()[d]) + + (int)calc_interstaff_dist (stem (i-1), this); + int r_y = (int)(stem (i)->head_positions()[d]) + + (int)calc_interstaff_dist (stem (i), this); + 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) + { + for (int i=0; i < stem_count (); i++) + { + int y = (int)(stem (i)->head_positions()[d]) + + (int)calc_interstaff_dist (stem (i), this); + directional_element (stem (i)).set (y < knee_y ? UP : DOWN); + stem (i)->set_elt_property ("dir-forced", SCM_BOOL_T); + } + } + return knee_b; } +/* + Set stem's shorten property if unset. + TODO: + take some y-position (chord/beam/nearest?) into account + scmify forced-fraction + */ void -Beam::set_default_dir () +Beam::set_stem_shorten () { - 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)); + if (!visible_stem_count ()) + return; - if (current) - { - total[d] += current; - count[d] ++; - } + Real forced_fraction = forced_stem_count () / visible_stem_count (); + if (forced_fraction < 0.5) + return; - } while ((d *= -1) != DOWN); - - do { - if (!total[d]) - count[d] = 1; - } while ((d *= -1) != DOWN); + int multiplicity = get_multiplicity (); + + // grace stems? + SCM shorten = scm_eval (ly_symbol2scm ("beamed-stem-shorten")); + + if (shorten == SCM_EOL) + return; + + int sz = scm_ilength (shorten); - /* + Staff_symbol_referencer_interface st (this); + Real staff_space = st.staff_space (); + SCM shorten_elt = scm_list_ref (shorten, gh_int2scm (multiplicity total[DOWN]) ? UP : DOWN; + /* cute, but who invented this -- how to customise ? */ + if (forced_fraction < 1) + shorten_f /= 2; - for (int i=0; i dir_ = dir_; + Stem* s = stem (i); + if (s->invisible_b ()) + continue; + if (gh_number_p (s->get_elt_property ("shorten"))) + s->set_elt_property ("shorten", gh_double2scm (shorten_f)); } } /* - should use minimum energy formulation (cf linespacing) -*/ + Set elt properties height and y-position if not set. + Adjust stem lengths to reach beam. + */ void -Beam::solve_slope () +Beam::after_line_breaking () { - Array sinfo; - for (int j=0; j set_default_extents (); - if (i->invisible_b ()) - continue; + Real damped_dy = calc_slope_damping_f (dy); + Real quantised_dy = quantise_dy_f (damped_dy); - Stem_info info (i); - sinfo.push (info); + y += (dy - quantised_dy) / 2; + dy = quantised_dy; } - if (! sinfo.size ()) - slope_f_ = left_y_ = 0; - else if (sinfo.size () == 1) + /* + until here, we used only stem_info, which acts as if dir=up + */ + y *= directional_element (this).get (); + dy *= directional_element (this).get (); + + Staff_symbol_referencer_interface st (this); + Real half_space = st.staff_space () / 2; + + /* check for user-override of dy */ + SCM s = remove_elt_property ("height-hs"); + if (gh_number_p (s)) { - slope_f_ = 0; - left_y_ = sinfo[0].idealy_f_; + dy = gh_scm2double (s) * half_space; } - else - { + set_elt_property ("height", gh_double2scm (dy)); - Real leftx = sinfo[0].x; - Least_squares l; - for (int i=0; i < sinfo.size (); i++) + /* check for user-override of y */ + s = remove_elt_property ("y-position-hs"); + if (gh_number_p (s)) + { + y = gh_scm2double (s) * half_space; + } + else + { + /* we can modify y, so we should quantise y */ + Real y_shift = check_stem_length_f (y, dy); + y += y_shift; + y = quantise_y_f (y, dy, 0); + set_stem_length (y, dy); + y_shift = check_stem_length_f (y, dy); + + if (y_shift > half_space / 4) { - sinfo[i].x -= leftx; - l.input.push (Offset (sinfo[i].x, sinfo[i].idealy_f_)); + 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 (this).get (); + y = quantise_y_f (y, dy, quant_dir); } - - l.minimise (slope_f_, left_y_); } + // UGH. Y is not in staff position unit? + // Ik dacht datwe daar juist van weg wilden? + set_stem_length (y, dy); + set_elt_property ("y-position", gh_double2scm (y)); +} - Real dy = 0.0; - for (int i=0; i < sinfo.size (); i++) - { - Real y = sinfo[i].x * slope_f_ + left_y_; - Real my = sinfo[i].miny_f_; +/* + See Documentation/tex/fonts.doc + */ +void +Beam::calc_default_position_and_height (Real* y, Real* dy) const +{ + *y = 0; + *dy = 0; + if (visible_stem_count () <= 1) + return; - if (my - y > dy) - dy = my -y; + Real first_ideal = first_visible_stem ()->calc_stem_info ().idealy_f_; + if (first_ideal == last_visible_stem ()->calc_stem_info ().idealy_f_) + { + *dy = 0; + *y = first_ideal; + return; } - left_y_ += dy; - left_y_ *= dir_; - slope_f_ *= dir_; + Array ideals; + Real x0 = first_visible_stem ()->relative_coordinate (0, X_AXIS); + for (int i=0; i < stem_count (); i++) + { + Stem* s = stem (i); + if (s->invisible_b ()) + continue; + ideals.push (Offset (s->relative_coordinate (0, X_AXIS) - x0, + s->calc_stem_info ().idealy_f_)); + } + Real dydx; + minimise_least_squares (&dydx, y, ideals); // duh, takes references - /* - 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_; - - quantise_yspan (); - - // y-values traditionally use internote dimension: therefore slope = (y/in)/x - // but mf and beam-lookup use PT dimension for y (as used for x-values) - // ugh --- there goes our simplified but careful quantisation - Real sl = slope_f_ * paper ()->internote_f (); - paper ()->lookup_l ()->beam (sl, 20 PT, 1 PT); - slope_f_ = sl / paper ()->internote_f (); + Real dx = last_visible_stem ()->relative_coordinate (0, X_AXIS) - x0; + *dy = dydx * dx; } -void -Beam::quantise_yspan () +bool +Beam::suspect_slope_b (Real y, Real dy) const { + /* first, calculate y, dy */ /* - [Ross] (simplification of) - Try to set slope_f_ complying with y-span of: - - zero - - beam_thickness / 2 + staffline_thickness / 2 - - beam_thickness + staffline_thickness - + n * interline - */ - - if (!quantisation_) - return; + steep slope running against lengthened stem is suspect + */ + Real first_ideal = first_visible_stem ()->calc_stem_info ().idealy_f_; + Real last_ideal = last_visible_stem ()->calc_stem_info ().idealy_f_; + Real lengthened = paper_l ()->get_var ("beam_lengthened"); + Real steep = paper_l ()->get_var ("beam_steep_slope"); - Real interline_f = paper ()->interline_f (); - Real internote_f = interline_f / 2; - Real staffline_thickness = paper ()->rule_thickness (); - Real beam_thickness = 0.48 * (interline_f - staffline_thickness); - - const int QUANTS = 3; - Real qdy[QUANTS] = { - 0, - beam_thickness / 2 + staffline_thickness / 2, - beam_thickness + staffline_thickness - }; - - Real xspan_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f (); - // y-values traditionally use internote dimension: therefore slope = (y/in)/x - Real yspan_f = xspan_f * abs (slope_f_ * internote_f); - int yspan_i = (int)(yspan_f / interline_f); - Real q = (yspan_f / interline_f - yspan_i) * interline_f; - int i = 0; - for (; i < QUANTS - 1; i++) - if ((q >= qdy[i]) && (q <= qdy[i + 1])) - { - if (q - qdy[i] < qdy[i + 1] - q) - break; - else - { - i++; - break; - } - } - q = qdy[i]; + Real dx = last_visible_stem ()->relative_coordinate (0, X_AXIS) - first_visible_stem ()->relative_coordinate (0, X_AXIS); + Real dydx = dy && dx ? dy/dx : 0; - yspan_f = (Real)yspan_i * interline_f + q; - // y-values traditionally use internote dimension: therefore slope = (y/in)/x - slope_f_ = yspan_f / xspan_f / internote_f * sign (slope_f_); + if (((y - first_ideal > lengthened) && (dydx > steep)) + || ((y + dy - last_ideal > lengthened) && (dydx < -steep))) + { + return true; + } + return false; } -void -Beam::quantise_left_y (Beam::Pos pos, bool extend_b) +/* + This neat trick is by Werner Lemberg, + damped = tanh (slope) + corresponds with some tables in [Wanske] +*/ +Real +Beam::calc_slope_damping_f (Real dy) const { - /* - quantising left y should suffice, as slope is quantised too - if extend then stems must not get shorter - */ + SCM damp = get_elt_property ("damping"); // remove? + int damping = 1; // ugh. + if (gh_number_p (damp)) + damping = gh_scm2int (damp); - if (!quantisation_) - return; + if (damping) + { + Real dx = last_visible_stem ()->relative_coordinate (0, X_AXIS) + - first_visible_stem ()->relative_coordinate (0, X_AXIS); + Real dydx = dy && dx ? dy/dx : 0; + dydx = 0.6 * tanh (dydx) / damping; + return dydx * dx; + } + return dy; +} - Real interline_f = paper ()->interline_f (); - Real internote_f = interline_f / 2; - Real staffline_thickness = paper ()->rule_thickness (); - Real beam_thickness = 0.48 * (interline_f - staffline_thickness); - - const int QUANTS = 7; - Real qy[QUANTS] = - { - 0, - beam_thickness / 2, - beam_thickness, - interline_f / 2 + beam_thickness / 2 + staffline_thickness / 2, - interline_f, - interline_f + beam_thickness / 2, - interline_f + beam_thickness - }; - /* - ugh, using i triggers gcc 2.7.2.1 internal compiler error (far down): - for (int i = 0; i < QUANTS; i++) - */ +Real +Beam::calc_stem_y_f (Stem* s, Real y, Real dy) const +{ + Real thick = gh_scm2double (get_elt_property ("beam-thickness")); + int beam_multiplicity = get_multiplicity (); + int stem_multiplicity = (s->flag_i () - 2) >? 0; + + Real interbeam_f = paper_l ()->interbeam_f (beam_multiplicity); + Real x0 = first_visible_stem ()->relative_coordinate (0, X_AXIS); + Real dx = last_visible_stem ()->relative_coordinate (0, X_AXIS) - x0; + Real stem_y = (dy && dx ? (s->relative_coordinate (0, X_AXIS) - x0) / dx * dy : 0) + y; + + /* knee */ + Direction dir = directional_element(this).get (); + Direction sdir = directional_element (s).get (); + + /* knee */ + if (dir!= sdir) + { + stem_y -= dir + * (thick / 2 + (beam_multiplicity - 1) * interbeam_f); + + Staff_symbol_referencer_interface me (s); + Staff_symbol_referencer_interface last (last_visible_stem ()); + + // huh, why not for first visible? + if (//(s != first_visible_stem ()) && + me.staff_symbol_l () != last.staff_symbol_l ()) + stem_y += directional_element (this).get () + * (beam_multiplicity - stem_multiplicity) * interbeam_f; + } + return stem_y; +} + +Real +Beam::check_stem_length_f (Real y, Real dy) const +{ + Real shorten = 0; + Real lengthen = 0; + Direction dir = directional_element (this).get (); - // fixme! - for (int ii = 0; ii < QUANTS; ii++) - qy[ii] -= 0.5 *beam_thickness; - Pos qpos[QUANTS] = - { - HANG, - STRADDLE, - SIT, - INTER, - HANG, - STRADDLE, - SIT - }; - - // y-values traditionally use internote dimension - Real y = left_y_ * internote_f; - int y_i = (int)floor(y / interline_f); - y = (y / interline_f - y_i) * interline_f; - - if (y < 0) - for (int ii = 0; ii < QUANTS; ii++) - qy[ii] -= interline_f; - - int lower_i = 0; - int i = 0; - for (; i < QUANTS; i++) + for (int i=0; i < stem_count (); i++) { - if (qy[i] > y) - break; - // found if lower_i is allowed, and nearer (from below) y than new pos - if ((pos & qpos[lower_i]) && (y - qy[lower_i] < y - qy[i])) - break; - // if new pos is allowed or old pos isn't: assign new pos - if ((pos & qpos[i]) || !(pos & qpos[lower_i])) - lower_i = i; + Stem* s = stem (i); + if (s->invisible_b ()) + continue; + + Real stem_y = calc_stem_y_f (s, y, dy); + + stem_y *= dir; + Stem_info info = s->calc_stem_info (); + + // if (0 > info.maxy_f_ - stem_y) + shorten = shorten ? info.miny_f_ - stem_y; } - int upper_i = QUANTS - 1; - for (i = QUANTS - 1; i >= 0; i--) + if (lengthen && shorten) + warning (_ ("weird beam vertical offset")); + + /* 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_stem_length (Real y, Real dy) +{ + Staff_symbol_referencer_interface st (this); + Real half_space = st.staff_space ()/2; + for (int i=0; i < stem_count (); i++) { - if (qy[i] < y) - break; - // found if upper_i is allowed, and nearer (from above) y than new pos - if ((pos & qpos[upper_i]) && (qy[upper_i] - y < qy[i] - y)) - break; - // if new pos is allowed or old pos isn't: assign new pos - if ((pos & qpos[i]) || !(pos & qpos[upper_i])) - upper_i = i; + Stem* s = stem (i); + if (s->invisible_b ()) + continue; + + Real stem_y = calc_stem_y_f (s, y, dy); + + /* caution: stem measures in staff-positions */ + s->set_stemend ((stem_y + calc_interstaff_dist (s, this)) / half_space); } +} - // y-values traditionally use internote dimension - Real upper_y = (qy[upper_i] + interline_f * y_i) / internote_f; - Real lower_y = (qy[lower_i] + interline_f * y_i) / internote_f; +/* + [Ross] (simplification of) + Set dy complying with: + - zero + - thick / 2 + staffline_f / 2 + - thick + staffline_f + + n * staff_space +*/ +Real +Beam::quantise_dy_f (Real dy) const +{ + Array a; + for (SCM s = scm_eval (ly_symbol2scm ("beam-height-quants")); s !=SCM_EOL; s = gh_cdr (s)) + a.push (gh_scm2double (gh_car (s))); + + if (a.size () <= 1) + return dy; - if (extend_b) - left_y_ = (dir_ > 0 ? upper_y : lower_y); - else - left_y_ = (upper_y - y < y - lower_y ? upper_y : lower_y); + Staff_symbol_referencer_interface st (this); + Real staff_space = st.staff_space (); + + 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); } -void -Beam::set_stemlens () +/* + 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 + */ +Real +Beam::quantise_y_f (Real y, Real dy, int quant_dir) { - Real x0 = stems_[0]->hpos_f (); - Real dy = 0; - - Real interline_f = paper ()->interline_f (); - Real internote_f = interline_f / 2; - Real staffline_thickness = paper ()->rule_thickness (); - Real beam_thickness = 0.48 * (interline_f - staffline_thickness); - Real interbeam_f = paper ()->interbeam_f (); - if (multiple_i_ > 3) - interbeam_f += 2.0 * staffline_thickness / 4; - Real xspan_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f (); - /* - ugh, y values are in "internote" dimension - */ - Real yspan_f = xspan_f * abs (slope_f_ * internote_f); - int yspan_i = (int)(yspan_f / interline_f); + int multiplicity = get_multiplicity (); + Staff_symbol_referencer_interface st (this); + Real staff_space = st.staff_space (); + SCM quants = scm_eval (gh_list (ly_symbol2scm ("beam-vertical-position-quants"), + gh_int2scm (multiplicity), + gh_double2scm (dy/staff_space), + SCM_UNDEFINED)); - Pos left_pos = NONE; + Array a; - if ((yspan_f < staffline_thickness / 2) || (quantisation_ == NORMAL)) - left_pos = (Pos)(STRADDLE | SIT | HANG); - else - left_pos = (Pos) (sign (slope_f_) > 0 ? STRADDLE | HANG - : SIT | STRADDLE); + for (; quants != SCM_EOL; quants = gh_cdr (quants)) + a.push (gh_scm2double (gh_car (quants))); - /* - ugh, slope currently mangled by availability mf chars... - be more generous regarding beam position between stafflines - */ - Real q = (yspan_f / interline_f - yspan_i) * interline_f; - if (q < interline_f / 3 - beam_thickness / 2) - left_pos = (Pos) (left_pos | INTER); + if (a.size () <= 1) + return y; - if (multiple_i_ > 1) - left_pos = (Pos) (dir_ > 0 ? HANG : SIT); + Real up_y = directional_element (this).get () * y; + Interval iv = quantise_iv (a, up_y/staff_space) * staff_space; - // ugh, rounding problems! - const Real EPSILON = interline_f / 10; - do - { - left_y_ += dy * dir_; - quantise_left_y (left_pos, dy); - dy = 0; - for (int j=0; j < stems_.size (); j++) - { - Stem *s = stems_[j]; - if (s->transparent_b_) - continue; - - Real x = s->hpos_f () - x0; - s->set_stemend (left_y_ + slope_f_ * x); - Real y = s->stem_length_f (); - int mult = max (stems_[j]->beams_left_i_, stems_[j]->beams_right_i_); - if (mult > 1) - // dim(y) = internote - y -= (mult - 1) * interbeam_f / internote_f; - if (y < MINIMUM_STEMLEN[mult]) - dy = dy >? (MINIMUM_STEMLEN[mult] - y); - } - } while (abs (dy) > EPSILON); + 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 (this).get (); } void -Beam::set_grouping (Rhythmic_grouping def, Rhythmic_grouping cur) +Beam::set_beaming (Beaming_info_list *beaming) { - def.OK (); - cur.OK (); - assert (cur.children.size () == stems_.size ()); - - cur.split (def); - - 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); - } - - for (int j=0, i=0; i < b.size () && j beams_left_i_ = b[i]; - s->beams_right_i_ = b[i+1]; - multiple_i_ = multiple_i_ >? (b[i] >? b[i+1]); + do + { + if (stem (i)->beam_count (d) == 0) + stem (i)->set_beaming ( beaming->infos_.elem (i).beams_i_drul_[d],d); + } + while (flip (&d) != LEFT); } } + + /* beams to go with one stem. + + BURP + clean me up. */ Molecule Beam::stem_beams (Stem *here, Stem *next, Stem *prev) const { - assert (!next || next->hpos_f () > here->hpos_f ()); - assert (!prev || prev->hpos_f () < here->hpos_f ()); - - Real staffline_thickness = paper ()->rule_thickness (); - Real interbeam_f = paper ()->interbeam_f (); - Real internote_f =paper ()->internote_f (); - Real interline_f = 2 * internote_f; - Real beamheight_f = 0.48 * (interline_f - staffline_thickness); - if (multiple_i_ > 3) - interbeam_f += 2.0 * staffline_thickness / 4; - Real dy = interbeam_f; - Real stemdx = staffline_thickness; - Real sl = slope_f_* internote_f; - paper ()->lookup_l ()->beam (sl, 20 PT, 1 PT); + 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"); + int multiplicity = get_multiplicity (); + + + Real interbeam_f = paper_l ()->interbeam_f (multiplicity); + Real thick = gh_scm2double (get_elt_property ("beam-thickness")); + + Real bdy = interbeam_f; + Real stemdx = staffline_f; + + Real dx = visible_stem_count () ? + last_visible_stem ()->relative_coordinate (0, X_AXIS) - first_visible_stem ()->relative_coordinate (0, X_AXIS) + : 0.0; + Real dy = gh_scm2double (get_elt_property ("height")); + Real dydx = dy && dx ? dy/dx : 0; Molecule leftbeams; Molecule rightbeams; + // UGH + Real nw_f; + if (!here->first_head ()) + 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"); + + + Direction dir = directional_element (this).get (); + /* 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_ ; - Real w = (here->hpos_f () - prev->hpos_f ())/4 note_width ();; - Atom a; + int lhalfs= lhalfs = here->beam_count (LEFT) - prev->beam_count (RIGHT); + int lwholebeams= here->beam_count (LEFT) beam_count (RIGHT) ; + /* + Half beam should be one note-width, + but let's make sure two half-beams never touch + */ + Real w = here->relative_coordinate (0, X_AXIS) - prev->relative_coordinate (0, X_AXIS); + w = w/2 lookup_l ()->beam (sl, w, beamheight_f); - a.translate (Offset (-w, -w * sl)); + a = lookup_l ()->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 (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 = here->beam_count (RIGHT) - next->beam_count (LEFT); + int rwholebeams= here->beam_count (RIGHT) beam_count (LEFT) ; - Real w = next->hpos_f () - here->hpos_f (); - Atom a = paper ()->lookup_l ()->beam (sl, w + stemdx, beamheight_f); + Real w = next->relative_coordinate (0, X_AXIS) - here->relative_coordinate (0, X_AXIS); + Molecule a = lookup_l ()->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 = get_elt_property ("beam-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 (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 = paper ()->lookup_l ()->beam (sl, w + stemdx, beamheight_f); + a = lookup_l ()->beam (dydx, w + stemdx, thick); } for (; j < rwholebeams; j++) { - Atom b (a); - b.translate (Offset (gap_f, -dir_ * dy * j)); - rightbeams.add (b); + Molecule b (a); + b.translate (Offset (here->invisible_b () ? 0 : gap_f, -dir * bdy * j)); + rightbeams.add_molecule (b); } - w = w/4 note_width (); + w = w/2 lookup_l ()->beam (sl, w, beamheight_f); + a = lookup_l ()->beam (dydx, w, thick); for (; j < rwholebeams + rhalfs; j++) { - Atom b (a); - b.translate_axis (-dir_ * dy * j, Y_AXIS); - rightbeams.add (b); + Molecule b (a); + b.translate_axis (- dir * bdy * j, Y_AXIS); + rightbeams.add_molecule (b); } } - leftbeams.add (rightbeams); + leftbeams.add_molecule (rightbeams); /* Does beam quanting think of the asymetry of beams? Refpoint is on bottom of symbol. (FIXTHAT) --hwn. */ - if (experimental_features_global_b && dir_ < 0) - leftbeams.translate_axis (-beamheight_f, Y_AXIS); return leftbeams; } + + +Molecule +Beam::do_brew_molecule () const +{ + Molecule mol; + if (!stem_count ()) + return mol; + Real x0,dx; + if (visible_stem_count ()) + { + x0 = first_visible_stem ()->relative_coordinate (0, X_AXIS); + dx = last_visible_stem ()->relative_coordinate (0, X_AXIS) - x0; + } + else + { + x0 = stem (0)->relative_coordinate (0, X_AXIS); + dx = stem_top ()->relative_coordinate (0, X_AXIS) - x0; + } + + + Real dy = gh_scm2double (get_elt_property ("height")); + Real dydx = dy && dx ? dy/dx : 0; + Real y = gh_scm2double (get_elt_property ("y-position")); + 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->relative_coordinate (0, X_AXIS)-x0; + sb.translate (Offset (x, x * dydx + y)); + mol.add_molecule (sb); + } + mol.translate_axis (x0 + - get_bound (LEFT)->relative_coordinate (0, X_AXIS), X_AXIS); + + return mol; +} + +int +Beam::forced_stem_count () const +{ + int f = 0; + for (int i=0; i < stem_count (); i++) + { + Stem *s = stem (i); + + if (s->invisible_b ()) + continue; + + if (((int)s->chord_start_f ()) + && (s->get_direction () != s->get_default_dir ())) + f++; + } + return f; +} + +/* + TODO: Fix this class. This is wildly inefficient. + And it sux. Yet another array/list 'interface'. + */ +Stem * +Beam::stem (int i) const +{ + return Group_interface__extract_elements ((Beam*) this, (Stem*) 0, "stems")[i]; +} + +int +Beam::stem_count () const +{ + Group_interface gi (this, "stems"); + return gi.count (); +} + +Stem* +Beam::stem_top () const +{ + SCM s = get_elt_property ("stems"); + + return gh_pair_p (s) ? dynamic_cast (unsmob_element (gh_car (s))) : 0; +} + +/* burp */ +int +Beam::visible_stem_count () const +{ + int c = 0; + for (int i = 0; i < stem_count (); i++) + { + if (!stem (i)->invisible_b ()) + c++; + } + return c; +} + +Stem* +Beam::first_visible_stem () const +{ + for (int i = 0; i < stem_count (); i++) + { + Stem* s = stem (i); + if (!s->invisible_b ()) + return s; + } + return 0; +} + +Stem* +Beam::last_visible_stem () const +{ + for (int i = stem_count (); i > 0; i--) + { + Stem* s = stem (i - 1); + if (!s->invisible_b ()) + return s; + } + return 0; +}