X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=lily%2Fbeam-quanting.cc;h=ebda6eb256fe484354feb94add3a4a979932198f;hb=ed547a139e986e06bae222102072afb8f28f1f3d;hp=eb9d3b0c8c0c5154ab917e2dd7856c8156d911fa;hpb=bdf4ab13203502e7ec7cf9cf5896527643a07c1f;p=lilypond.git diff --git a/lily/beam-quanting.cc b/lily/beam-quanting.cc index eb9d3b0c8c..ebda6eb256 100644 --- a/lily/beam-quanting.cc +++ b/lily/beam-quanting.cc @@ -3,35 +3,55 @@ source file of the GNU LilyPond music typesetter - (c) 1997--2005 Han-Wen Nienhuys + (c) 1997--2007 Han-Wen Nienhuys Jan Nieuwenhuizen */ -#include +#include "beam.hh" -#include "warn.hh" +#include +using namespace std; + +#include "grob.hh" +#include "align-interface.hh" +#include "international.hh" +#include "output-def.hh" +#include "pointer-group-interface.hh" #include "staff-symbol-referencer.hh" -#include "beam.hh" #include "stem.hh" -#include "output-def.hh" -#include "group-interface.hh" -#include "align-interface.hh" +#include "warn.hh" +#include "main.hh" -const int INTER_QUANT_PENALTY = 1000; -const Real SECONDARY_BEAM_DEMERIT = 10.0; -const int STEM_LENGTH_DEMERIT_FACTOR = 5; +Real +get_detail (SCM alist, SCM sym, Real def) +{ + SCM entry = scm_assq (sym, alist); -/* - threshold to combat rounding errors. -*/ -const Real BEAM_EPS = 1e-3; + if (scm_is_pair (entry)) + return robust_scm2double (scm_cdr (entry), def); + return def; +} + +void +Beam_quant_parameters::fill (Grob *him) +{ + SCM details = him->get_property ("details"); -// possibly ridiculous, but too short stems just won't do -const int STEM_LENGTH_LIMIT_PENALTY = 5000; -const int DAMPING_DIRECTION_PENALTY = 800; -const int MUSICAL_DIRECTION_FACTOR = 400; -const int IDEAL_SLOPE_FACTOR = 10; -const Real ROUND_TO_ZERO_SLOPE = 0.02; + /* + TODO: put in define-grobs.scm + */ + INTER_QUANT_PENALTY = get_detail (details, ly_symbol2scm ("inter-quant-penalty"), 1000.0); + SECONDARY_BEAM_DEMERIT = get_detail (details, ly_symbol2scm ("secondary-beam-demerit"), 10.0); + STEM_LENGTH_DEMERIT_FACTOR = get_detail (details, ly_symbol2scm ("stem-length-demerit-factor"), 5); + REGION_SIZE = get_detail (details, ly_symbol2scm ("region-size"), 2); + BEAM_EPS = get_detail (details, ly_symbol2scm ("beam-eps"), 1e-3); + STEM_LENGTH_LIMIT_PENALTY = get_detail (details, ly_symbol2scm ("stem-length-limit-penalty"), 5000); + DAMPING_DIRECTION_PENALTY = get_detail (details, ly_symbol2scm ("damping-direction-penalty"), 800); + HINT_DIRECTION_PENALTY = get_detail (details, ly_symbol2scm ("hint-direction-penalty"), 20); + MUSICAL_DIRECTION_FACTOR = get_detail (details, ly_symbol2scm ("musical-direction-factor"), 400); + IDEAL_SLOPE_FACTOR = get_detail (details, ly_symbol2scm ("ideal-slope-factor"), 10); + ROUND_TO_ZERO_SLOPE = get_detail (details, ly_symbol2scm ("round-to-zero-slope"), 0.02); +} static Real shrink_extra_weight (Real x, Real fac) @@ -45,8 +65,8 @@ struct Quant_score Real yr; Real demerits; -#if DEBUG_QUANTING - String score_card_; +#if DEBUG_BEAM_SCORING + string score_card_; #endif }; @@ -62,11 +82,11 @@ struct Quant_score */ int -best_quant_score_idx (Array const &qscores) +best_quant_score_idx (vector const &qscores) { Real best = 1e6; int best_idx = -1; - for (int i = qscores.size (); i--;) + for (vsize i = qscores.size (); i--;) { if (qscores[i].demerits < best) { @@ -75,24 +95,20 @@ best_quant_score_idx (Array const &qscores) } } - if (best_idx < 0) - { - programming_error ("Huh? No best beam quant score?"); - best_idx = 0; - } - return best_idx; } -MAKE_SCHEME_CALLBACK (Beam, quanting, 1); +MAKE_SCHEME_CALLBACK (Beam, quanting, 2); SCM -Beam::quanting (SCM smob) +Beam::quanting (SCM smob, SCM posns) { Grob *me = unsmob_grob (smob); - SCM s = me->get_property ("positions"); - Real yl = scm_to_double (scm_car (s)); - Real yr = scm_to_double (scm_cdr (s)); + Beam_quant_parameters parameters; + parameters.fill (me); + + Real yl = scm_to_double (scm_car (posns)); + Real yr = scm_to_double (scm_cdr (posns)); /* Calculations are relative to a unit-scaled staff, i.e. the quants are @@ -111,8 +127,8 @@ Beam::quanting (SCM smob) Real quants [] = {straddle, sit, inter, hang }; int num_quants = int (sizeof (quants) / sizeof (Real)); - Array quantsl; - Array quantsr; + vector quantsl; + vector quantsr; /* going to REGION_SIZE == 2, yields another 0.6 second with @@ -127,59 +143,60 @@ Beam::quanting (SCM smob) Do stem computations. These depend on YL and YR linearly, so we can precompute for every stem 2 factors. */ - Link_array stems - = extract_grob_array (me, ly_symbol2scm ("stems")); - Array stem_infos; - Array base_lengths; - Array stem_xposns; + vector stems + = extract_grob_array (me, "stems"); + vector stem_infos; + vector base_lengths; + vector stem_xposns; Drul_array dirs_found (0, 0); Grob *common[2]; for (int a = 2; a--;) common[a] = common_refpoint_of_array (stems, me, Axis (a)); - Grob *fvs = first_visible_stem (me); - Grob *lvs = last_visible_stem (me); + Grob *fvs = first_normal_stem (me); + Grob *lvs = last_normal_stem (me); Real xl = fvs ? fvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0; Real xr = fvs ? lvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0; /* - We store some info to quickly interpolate. - - Sometimes my head is screwed on backwards. The stemlength are - AFFINE linear in YL and YR. If YL == YR == 0, then we might have + We store some info to quickly interpolate. The stemlength are + affine linear in YL and YR. If YL == YR == 0, then we might have stem_y != 0.0, when we're cross staff. */ - for (int i = 0; i < stems.size (); i++) + for (vsize i = 0; i < stems.size (); i++) { Grob *s = stems[i]; Stem_info si (Stem::get_stem_info (s)); si.scale (1 / ss); - stem_infos.push (si); - dirs_found[stem_infos.top ().dir_] = true; + stem_infos.push_back (si); + dirs_found[stem_infos.back ().dir_] = true; bool f = to_boolean (s->get_property ("french-beaming")) && s != lvs && s != fvs; - base_lengths.push (calc_stem_y (me, s, common, xl, xr, - Interval (0, 0), f) / ss); - stem_xposns.push (s->relative_coordinate (common[X_AXIS], X_AXIS)); - } + if (Stem::is_normal_stem (s)) + { + base_lengths.push_back (calc_stem_y (me, s, common, xl, xr, CENTER, + Interval (0, 0), f) / ss); + } + else + { + base_lengths.push_back (0); + } - bool xstaff = false; - if (lvs && fvs) - { - Grob *commony = fvs->common_refpoint (lvs, Y_AXIS); - xstaff = Align_interface::has_interface (commony); + stem_xposns.push_back (s->relative_coordinate (common[X_AXIS], X_AXIS)); } + bool xstaff = Align_interface::has_interface (common[Y_AXIS]); Direction ldir = Direction (stem_infos[0].dir_); - Direction rdir = Direction (stem_infos.top ().dir_); + Direction rdir = Direction (stem_infos.back ().dir_); bool is_knee = dirs_found[LEFT] && dirs_found[RIGHT]; - int region_size = REGION_SIZE; + int region_size = (int) parameters.REGION_SIZE; + /* Knees are harder, lets try some more possibilities for knees. */ @@ -192,36 +209,36 @@ Beam::quanting (SCM smob) for (int i = -region_size; i < region_size; i++) for (int j = 0; j < num_quants; j++) { - quantsl.push (i + quants[j] + int (yl)); - quantsr.push (i + quants[j] + int (yr)); + quantsl.push_back (i + quants[j] + int (yl)); + quantsr.push_back (i + quants[j] + int (yr)); } - Array qscores; + vector qscores; - for (int l = 0; l < quantsl.size (); l++) - for (int r = 0; r < quantsr.size (); r++) + for (vsize l = 0; l < quantsl.size (); l++) + for (vsize r = 0; r < quantsr.size (); r++) { Quant_score qs; qs.yl = quantsl[l]; qs.yr = quantsr[r]; qs.demerits = 0.0; - qscores.push (qs); + qscores.push_back (qs); } /* This is a longish function, but we don't separate this out into neat modular separate subfunctions, as the subfunctions would be called for many values of YL, YR. By precomputing various parameters outside of the loop, we can save a lot of time. */ - for (int i = qscores.size (); i--;) + for (vsize i = qscores.size (); i--;) { Real d = score_slopes_dy (qscores[i].yl, qscores[i].yr, dy_mus, yr- yl, xr - xl, - xstaff); + xstaff, ¶meters); qscores[i].demerits += d; -#if DEBUG_QUANTING +#if DEBUG_BEAM_SCORING qscores[i].score_card_ += to_string ("S%.2f", d); #endif } @@ -229,52 +246,50 @@ Beam::quanting (SCM smob) Real rad = Staff_symbol_referencer::staff_radius (me); Drul_array edge_beam_counts (Stem::beam_multiplicity (stems[0]).length () + 1, - Stem::beam_multiplicity (stems.top ()).length () + 1); + Stem::beam_multiplicity (stems.back ()).length () + 1); Real beam_translation = get_beam_translation (me) / ss; Real reasonable_score = (is_knee) ? 200000 : 100; - for (int i = qscores.size (); i--;) + for (vsize i = qscores.size (); i--;) if (qscores[i].demerits < reasonable_score) { Real d = score_forbidden_quants (qscores[i].yl, qscores[i].yr, rad, slt, thickness, beam_translation, - edge_beam_counts, ldir, rdir); + edge_beam_counts, ldir, rdir, ¶meters); qscores[i].demerits += d; -#if DEBUG_QUANTING +#if DEBUG_BEAM_SCORING qscores[i].score_card_ += to_string (" F %.2f", d); #endif } - for (int i = qscores.size (); i--;) + for (vsize i = qscores.size (); i--;) if (qscores[i].demerits < reasonable_score) { Real d = score_stem_lengths (stems, stem_infos, base_lengths, stem_xposns, xl, xr, is_knee, - qscores[i].yl, qscores[i].yr); + qscores[i].yl, qscores[i].yr, ¶meters); qscores[i].demerits += d; -#if DEBUG_QUANTING +#if DEBUG_BEAM_SCORING qscores[i].score_card_ += to_string (" L %.2f", d); #endif } int best_idx = best_quant_score_idx (qscores); -#if DEBUG_QUANTING +#if DEBUG_BEAM_SCORING SCM inspect_quants = me->get_property ("inspect-quants"); - if (to_boolean (me->get_layout ()->lookup_variable (ly_symbol2scm ("debug-beam-quanting"))) + if (to_boolean (me->layout ()->lookup_variable (ly_symbol2scm ("debug-beam-scoring"))) && scm_is_pair (inspect_quants)) { Drul_array ins = ly_scm2interval (inspect_quants); - int i = 0; - Real mindist = 1e6; - for (; i < qscores.size (); i++) + for (vsize i = 0; i < qscores.size (); i++) { Real d = fabs (qscores[i].yl- ins[LEFT]) + fabs (qscores[i].yr - ins[RIGHT]); if (d < mindist) @@ -284,56 +299,68 @@ Beam::quanting (SCM smob) } } if (mindist > 1e5) - programming_error ("Could not find quant."); + programming_error ("cannot find quant"); } #endif - me->set_property ("positions", - ly_interval2scm (Drul_array (qscores[best_idx].yl, - qscores[best_idx].yr))); -#if DEBUG_QUANTING - if (to_boolean (me->get_layout ()->lookup_variable (ly_symbol2scm ("debug-beam-quanting")))) + Interval final_positions; + if (best_idx < 0) + { + warning (_ ("no feasible beam position")); + final_positions = Interval (0, 0); + } + else + { + final_positions = Drul_array (qscores[best_idx].yl, + qscores[best_idx].yr); + } + +#if DEBUG_BEAM_SCORING + if (best_idx >= 0 + && to_boolean (me->layout ()->lookup_variable (ly_symbol2scm ("debug-beam-scoring")))) { qscores[best_idx].score_card_ += to_string ("i%d", best_idx); // debug quanting me->set_property ("quant-score", - scm_makfrom0str (qscores[best_idx].score_card_.to_str0 ())); + ly_string2scm (qscores[best_idx].score_card_)); } #endif - return SCM_UNSPECIFIED; + return ly_interval2scm (final_positions); } Real -Beam::score_stem_lengths (Link_array const &stems, - Array const &stem_infos, - Array const &base_stem_ys, - Array const &stem_xs, +Beam::score_stem_lengths (vector const &stems, + vector const &stem_infos, + vector const &base_stem_ys, + vector const &stem_xs, Real xl, Real xr, bool knee, - Real yl, Real yr) + Real yl, Real yr, + + Beam_quant_parameters const *parameters) { - Real limit_penalty = STEM_LENGTH_LIMIT_PENALTY; + Real limit_penalty = parameters->STEM_LENGTH_LIMIT_PENALTY; Drul_array score (0, 0); Drul_array count (0, 0); - for (int i = 0; i < stems.size (); i++) + for (vsize i = 0; i < stems.size (); i++) { Grob *s = stems[i]; - if (Stem::is_invisible (s)) + if (!Stem::is_normal_stem (s)) continue; Real x = stem_xs[i]; Real dx = xr - xl; Real beam_y = dx ? yr * (x - xl) / dx + yl * (xr - x) / dx : (yr + yl) / 2; Real current_y = beam_y + base_stem_ys[i]; - Real length_pen = STEM_LENGTH_DEMERIT_FACTOR; + Real length_pen = parameters->STEM_LENGTH_DEMERIT_FACTOR; Stem_info info = stem_infos[i]; Direction d = info.dir_; - score[d] += limit_penalty * (0 >? (d * (info.shortest_y_ - current_y))); + score[d] += limit_penalty * max (0.0, (d * (info.shortest_y_ - current_y))); Real ideal_diff = d * (current_y - info.ideal_y_); Real ideal_score = shrink_extra_weight (ideal_diff, 1.5); @@ -351,9 +378,7 @@ Beam::score_stem_lengths (Link_array const &stems, Direction d = DOWN; do - { - score[d] /= (count[d] >? 1); - } + score[d] /= max (count[d], 1); while (flip (&d) != DOWN); return score[LEFT] + score[RIGHT]; @@ -363,7 +388,9 @@ Real Beam::score_slopes_dy (Real yl, Real yr, Real dy_mus, Real dy_damp, Real dx, - bool xstaff) + bool xstaff, + + Beam_quant_parameters const *parameters) { Real dy = yr - yl; Real dem = 0.0; @@ -375,15 +402,23 @@ Beam::score_slopes_dy (Real yl, Real yr, TODO: find a way to incorporate the complexity of the beam in this penalty. */ - if (fabs (dy / dx) > ROUND_TO_ZERO_SLOPE - && sign (dy_damp) != sign (dy)) + if (sign (dy_damp) != sign (dy)) { - dem += DAMPING_DIRECTION_PENALTY; + if (!dy) + { + if (fabs (dy_damp / dx) > parameters->ROUND_TO_ZERO_SLOPE) + dem += parameters->DAMPING_DIRECTION_PENALTY; + else + dem += parameters->HINT_DIRECTION_PENALTY; + } + else + dem += parameters->DAMPING_DIRECTION_PENALTY; } + + dem += parameters->MUSICAL_DIRECTION_FACTOR + * max (0.0, (fabs (dy) - fabs (dy_mus))); - dem += MUSICAL_DIRECTION_FACTOR *(0 >? (fabs (dy) - fabs (dy_mus))); - - Real slope_penalty = IDEAL_SLOPE_FACTOR; + Real slope_penalty = parameters->IDEAL_SLOPE_FACTOR; /* Xstaff beams tend to use extreme slopes to get short stems. We put in a penalty here. */ @@ -414,16 +449,19 @@ Beam::score_forbidden_quants (Real yl, Real yr, Real slt, Real thickness, Real beam_translation, Drul_array beam_counts, - Direction ldir, Direction rdir) + Direction ldir, Direction rdir, + + Beam_quant_parameters const *parameters) { Real dy = yr - yl; Drul_array y (yl, yr); Drul_array dirs (ldir, rdir); - Real extra_demerit = SECONDARY_BEAM_DEMERIT / (beam_counts[LEFT] >? beam_counts[RIGHT]); + Real extra_demerit = parameters->SECONDARY_BEAM_DEMERIT / (max (beam_counts[LEFT], beam_counts[RIGHT])); Direction d = LEFT; Real dem = 0.0; + Real eps = parameters->BEAM_EPS; do { @@ -445,10 +483,10 @@ Beam::score_forbidden_quants (Real yl, Real yr, gap.add_point (gap2); for (Real k = -radius; - k <= radius + BEAM_EPS; k += 1.0) + k <= radius + eps; k += 1.0) if (gap.contains (k)) { - Real dist = fabs (gap[UP] - k) ? beam_counts[RIGHT]) >= 2) + if (max (beam_counts[LEFT], beam_counts[RIGHT]) >= 2) { Real straddle = 0.0; Real sit = (thickness - slt) / 2; @@ -476,24 +514,24 @@ Beam::score_forbidden_quants (Real yl, Real yr, if (beam_counts[d] >= 2 && fabs (y[d] - dirs[d] * beam_translation) < radius + inter) { - if (dirs[d] == UP && dy <= BEAM_EPS - && fabs (my_modf (y[d]) - sit) < BEAM_EPS) + if (dirs[d] == UP && dy <= eps + && fabs (my_modf (y[d]) - sit) < eps) dem += extra_demerit; - if (dirs[d] == DOWN && dy >= BEAM_EPS - && fabs (my_modf (y[d]) - hang) < BEAM_EPS) + if (dirs[d] == DOWN && dy >= eps + && fabs (my_modf (y[d]) - hang) < eps) dem += extra_demerit; } if (beam_counts[d] >= 3 && fabs (y[d] - 2 * dirs[d] * beam_translation) < radius + inter) { - if (dirs[d] == UP && dy <= BEAM_EPS - && fabs (my_modf (y[d]) - straddle) < BEAM_EPS) + if (dirs[d] == UP && dy <= eps + && fabs (my_modf (y[d]) - straddle) < eps) dem += extra_demerit; - if (dirs[d] == DOWN && dy >= BEAM_EPS - && fabs (my_modf (y[d]) - straddle) < BEAM_EPS) + if (dirs[d] == DOWN && dy >= eps + && fabs (my_modf (y[d]) - straddle) < eps) dem += extra_demerit; } }