From eaa41b4e4dc1d3b893c5a75711f26db6a7f5fa18 Mon Sep 17 00:00:00 2001 From: Han-Wen Nienhuys Date: Mon, 31 Jan 2011 23:37:04 -0200 Subject: [PATCH] Cleanup beam scoring code. Introduce Beam_scoring_problem to encapsulate parameters and methods for beam scoring. This is a preparation for using a priority queue on the to-be-computed scores. --- lily/beam-quanting.cc | 459 +++++++++++++-------------- lily/beam.cc | 21 +- lily/include/beam-scoring-problem.hh | 150 +++++++++ lily/include/beam.hh | 45 +-- 4 files changed, 390 insertions(+), 285 deletions(-) create mode 100644 lily/include/beam-scoring-problem.hh diff --git a/lily/beam-quanting.cc b/lily/beam-quanting.cc index 2ecf070d40..d5de148b3e 100644 --- a/lily/beam-quanting.cc +++ b/lily/beam-quanting.cc @@ -18,20 +18,22 @@ along with LilyPond. If not, see . */ -#include "beam.hh" +#include "beam-scoring-problem.hh" #include using namespace std; -#include "grob.hh" #include "align-interface.hh" +#include "beam.hh" +#include "directional-element-interface.hh" +#include "grob.hh" #include "international.hh" +#include "main.hh" #include "output-def.hh" #include "pointer-group-interface.hh" #include "staff-symbol-referencer.hh" #include "stem.hh" #include "warn.hh" -#include "main.hh" Real get_detail (SCM alist, SCM sym, Real def) @@ -66,16 +68,47 @@ shrink_extra_weight (Real x, Real fac) return fabs (x) * ((x < 0) ? fac : 1.0); } -struct Quant_score +/****************************************************************/ + +Beam_configuration::Beam_configuration () +{ + y = Interval (0.0, 0.0); + demerits = 0.0; + next_scorer_todo = ORIGINAL_DISTANCE; +} + +bool Beam_configuration::done () const { - Real yl; - Real yr; - Real demerits; + return next_scorer_todo >= NUM_SCORERS; +} + +void Beam_configuration::add (Real demerit, const string &reason) +{ + demerits += demerit; #if DEBUG_BEAM_SCORING - string score_card_; + if (demerit) + score_card_ += to_string (" %s %.2f", reason.c_str (), demerit); #endif -}; +} + +Beam_configuration* Beam_configuration::new_config (Interval start, + Interval offset) +{ + Beam_configuration* qs = new Beam_configuration; + qs->y = Interval (int (start[LEFT]) + offset[LEFT], + int (start[RIGHT]) + offset[RIGHT]); + + // This orders the sequence so we try combinations closest to the + // the ideal offset first. + Real start_score = abs (offset[RIGHT]) + abs (offset[LEFT]); + qs->demerits = start_score / 1000.0; + qs->next_scorer_todo = ORIGINAL_DISTANCE + 1; + + return qs; +} + +/****************************************************************/ /* TODO: @@ -87,17 +120,16 @@ struct Quant_score - Add demerits for quants per se, as to forbid a specific quant entirely */ - int -best_quant_score_idx (vector const &qscores) +best_quant_score_idx (vector const &configs) { Real best = 1e6; int best_idx = -1; - for (vsize i = qscores.size (); i--;) + for (vsize i = configs.size (); i--;) { - if (qscores[i].demerits < best) + if (configs[i]->demerits < best) { - best = qscores [i].demerits; + best = configs [i]->demerits; best_idx = i; } } @@ -105,198 +137,162 @@ best_quant_score_idx (vector const &qscores) return best_idx; } -MAKE_SCHEME_CALLBACK (Beam, quanting, 2); -SCM -Beam::quanting (SCM smob, SCM posns) -{ - Grob *me = unsmob_grob (smob); - - 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 - divided by the current staff_space. - */ - Real ss = Staff_symbol_referencer::staff_space (me); - Real beam_thickness = Beam::get_beam_thickness (me) / ss; - Real slt = Staff_symbol_referencer::line_thickness (me) / ss; - - Real dy_mus = robust_scm2double (me->get_property ("least-squares-dy"), 0); - Real straddle = 0.0; - Real sit = (beam_thickness - slt) / 2; - Real inter = 0.5; - Real hang = 1.0 - (beam_thickness - slt) / 2; - Real quants [] = {straddle, sit, inter, hang }; - - int num_quants = int (sizeof (quants) / sizeof (Real)); - vector quantsl; - vector quantsr; - - /* - going to REGION_SIZE == 2, yields another 0.6 second with - wtk1-fugue2. - - (result indexes between 70 and 575) ? --hwn. - - */ - - /* - Do stem computations. These depend on YL and YR linearly, so we can - precompute for every stem 2 factors. - */ - vector stems - = extract_grob_array (me, "stems"); - vector stem_infos; - vector base_lengths; - vector stem_xposns; +// This is a temporary hack to see how much we can gain by using a +// priority queue on the beams to score. +static int score_count = 0; +LY_DEFINE (ly_beam_score_count, "ly:beam-score-count", 0, 0, 0, + (), + "count number of beam scores.") { + return scm_from_int (score_count); +} - Drul_array dirs_found (0, 0); - Grob *common[2]; +void Beam_scoring_problem::init_stems () +{ + extract_grob_set (beam, "stems", stems); for (int a = 2; a--;) - common[a] = common_refpoint_of_array (stems, me, Axis (a)); + common[a] = common_refpoint_of_array (stems, beam, Axis (a)); - 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; + Grob *fvs = Beam::first_normal_stem (beam); + Grob *lvs = Beam::last_normal_stem (beam); + + x_span = Interval (fvs ? fvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0, + lvs ? lvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0); - /* - 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. + Drul_array dirs_found (0, 0); - */ for (vsize i = 0; i < stems.size (); i++) { Grob *s = stems[i]; - + if (!Stem::is_normal_stem (s)) + continue; + Stem_info si (Stem::get_stem_info (s)); - si.scale (1 / ss); + si.scale (1 / staff_space); stem_infos.push_back (si); - dirs_found[stem_infos.back ().dir_] = true; + dirs_found[si.dir_] = true; bool f = to_boolean (s->get_property ("french-beaming")) - && s != lvs && s != fvs; - - 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); - } + && s != lvs && s != fvs; - stem_xposns.push_back (s->relative_coordinate (common[X_AXIS], X_AXIS)); + Real y = Beam::calc_stem_y (beam, s, common, x_span[LEFT], x_span[RIGHT], CENTER, + Interval (0, 0), f); + base_lengths.push_back (y / staff_space); + stem_xpositions.push_back (s->relative_coordinate (common[X_AXIS], X_AXIS)); } - bool xstaff = Align_interface::has_interface (common[Y_AXIS]); + + edge_dirs = Drul_array (CENTER, CENTER); + if (stem_infos.size ()) + { + edge_dirs = Drul_array (stem_infos[0].dir_, + stem_infos.back().dir_); + } + is_xstaff = Align_interface::has_interface (common[Y_AXIS]); + is_knee = dirs_found[LEFT] && dirs_found[RIGHT]; + + staff_radius = Staff_symbol_referencer::staff_radius (beam); + edge_beam_counts = Drul_array + (Stem::beam_multiplicity (stems[0]).length () + 1, + Stem::beam_multiplicity (stems.back ()).length () + 1); + + beam_translation = Beam::get_beam_translation (beam) / staff_space; +} - Direction ldir = Direction (stem_infos[0].dir_); - Direction rdir = Direction (stem_infos.back ().dir_); - bool is_knee = dirs_found[LEFT] && dirs_found[RIGHT]; +Beam_scoring_problem::Beam_scoring_problem (Grob *me, Drul_array ys) +{ + beam = me; + unquanted_y = ys; + + /* + Calculations are relative to a unit-scaled staff, i.e. the quants are + divided by the current staff_space. + */ + staff_space = Staff_symbol_referencer::staff_space (me); + beam_thickness = Beam::get_beam_thickness (me) / staff_space; + line_thickness = Staff_symbol_referencer::line_thickness (me) / staff_space; + // This is the least-squares DY, corrected for concave beams. + musical_dy = robust_scm2double (me->get_property ("least-squares-dy"), 0); + + parameters.fill (me); + init_stems (); +} + +void +Beam_scoring_problem::generate_quants (vector *scores) const +{ int region_size = (int) parameters.REGION_SIZE; /* Knees are harder, lets try some more possibilities for knees. - */ + */ if (is_knee) region_size += 2; + + Real straddle = 0.0; + Real sit = (beam_thickness - line_thickness) / 2; + Real inter = 0.5; + Real hang = 1.0 - (beam_thickness - line_thickness) / 2; + Real base_quants [] = {straddle, sit, inter, hang}; + int num_base_quants = int (sizeof (base_quants) / sizeof (Real)); /* Asymetry ? should run to <= region_size ? */ + vector unshifted_quants; for (int i = -region_size; i < region_size; i++) - for (int j = 0; j < num_quants; j++) + for (int j = 0; j < num_base_quants; j++) { - quantsl.push_back (i + quants[j] + int (yl)); - quantsr.push_back (i + quants[j] + int (yr)); + unshifted_quants.push_back (i + base_quants[j]); } - vector qscores; - 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; + scores->clear (); + for (vsize i = 0; i < unshifted_quants.size (); i++) + for (vsize j = 0; j < unshifted_quants.size (); j++) + scores->push_back (Beam_configuration::new_config (unquanted_y, + Interval (unshifted_quants[i], + unshifted_quants[j]))); +} - qscores.push_back (qs); - } +Drul_array +Beam_scoring_problem::solve () const { + vector configs; - /* 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 (vsize i = qscores.size (); i--;) + generate_quants (&configs); + for (vsize i = configs.size (); i--;) { - Real d = score_slopes_dy (qscores[i].yl, qscores[i].yr, - dy_mus, yr- yl, - xr - xl, - xstaff, ¶meters); - qscores[i].demerits += d; - -#if DEBUG_BEAM_SCORING - qscores[i].score_card_ += to_string ("S%.2f", d); -#endif + score_count ++; + score_slopes_dy (configs[i]); } - Real rad = Staff_symbol_referencer::staff_radius (me); - Drul_array edge_beam_counts - (Stem::beam_multiplicity (stems[0]).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 (vsize i = qscores.size (); i--;) - if (qscores[i].demerits < reasonable_score) + for (vsize i = configs.size (); i--;) + if (configs[i]->demerits < reasonable_score) { - Real d = score_forbidden_quants (qscores[i].yl, qscores[i].yr, - rad, slt, beam_thickness, beam_translation, - edge_beam_counts, ldir, rdir, ¶meters); - qscores[i].demerits += d; - -#if DEBUG_BEAM_SCORING - qscores[i].score_card_ += to_string (" F %.2f", d); -#endif + score_count ++; + score_forbidden_quants (configs[i]); } - for (vsize i = qscores.size (); i--;) - if (qscores[i].demerits < reasonable_score) + for (vsize i = configs.size (); i--;) + if (configs[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, ¶meters); - qscores[i].demerits += d; - -#if DEBUG_BEAM_SCORING - qscores[i].score_card_ += to_string (" L %.2f", d); -#endif + score_count ++; + score_stem_lengths (configs[i]); } - int best_idx = best_quant_score_idx (qscores); + int best_idx = best_quant_score_idx (configs); #if DEBUG_BEAM_SCORING - SCM inspect_quants = me->get_property ("inspect-quants"); - if (to_boolean (me->layout ()->lookup_variable (ly_symbol2scm ("debug-beam-scoring"))) + SCM inspect_quants = beam->get_property ("inspect-quants"); + if (to_boolean (beam->layout ()->lookup_variable (ly_symbol2scm ("debug-beam-scoring"))) && scm_is_pair (inspect_quants)) { Drul_array ins = ly_scm2interval (inspect_quants); Real mindist = 1e6; - for (vsize i = 0; i < qscores.size (); i++) + for (vsize i = 0; i < configs.size (); i++) { - Real d = fabs (qscores[i].yl- ins[LEFT]) + fabs (qscores[i].yr - ins[RIGHT]); + Real d = fabs (configs[i]->y[LEFT]- ins[LEFT]) + fabs (configs[i]->y[RIGHT] - ins[RIGHT]); if (d < mindist) { best_idx = i; @@ -316,51 +312,41 @@ Beam::quanting (SCM smob, SCM posns) } else { - final_positions = Drul_array (qscores[best_idx].yl, - qscores[best_idx].yr); + final_positions = configs[best_idx]->y; } #if DEBUG_BEAM_SCORING if (best_idx >= 0 - && to_boolean (me->layout ()->lookup_variable (ly_symbol2scm ("debug-beam-scoring")))) + && to_boolean (beam->layout ()->lookup_variable (ly_symbol2scm ("debug-beam-scoring")))) { - qscores[best_idx].score_card_ += to_string ("i%d", best_idx); + configs[best_idx]->score_card_ += to_string ("i%d", best_idx); // debug quanting - me->set_property ("quant-score", - ly_string2scm (qscores[best_idx].score_card_)); + beam->set_property ("quant-score", + ly_string2scm (configs[best_idx]->score_card_)); } #endif - return ly_interval2scm (final_positions); + junk_pointers (configs); + return final_positions; } -Real -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, - - Beam_quant_parameters const *parameters) +void +Beam_scoring_problem::score_stem_lengths (Beam_configuration* config) const { - Real limit_penalty = parameters->STEM_LENGTH_LIMIT_PENALTY; + Real limit_penalty = parameters.STEM_LENGTH_LIMIT_PENALTY; Drul_array score (0, 0); Drul_array count (0, 0); - for (vsize i = 0; i < stems.size (); i++) + for (vsize i = 0; i < stem_xpositions.size (); i++) { - Grob *s = stems[i]; - 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 = parameters->STEM_LENGTH_DEMERIT_FACTOR; + Real x = stem_xpositions[i]; + Real dx = x_span.delta (); + Real beam_y = dx + ? config->y[RIGHT] * (x - x_span[LEFT]) / dx + config->y[LEFT] * (x_span[RIGHT] - x) / dx + : (config->y[RIGHT] + config->y[LEFT]) / 2; + Real current_y = beam_y + base_lengths[i]; + Real length_pen = parameters.STEM_LENGTH_DEMERIT_FACTOR; Stem_info info = stem_infos[i]; Direction d = info.dir_; @@ -373,7 +359,7 @@ Beam::score_stem_lengths (vector const &stems, /* We introduce a power, to make the scoring strictly convex. Otherwise a symmetric knee beam (up/down/up/down) does not have an optimum in the middle. */ - if (knee) + if (is_knee) ideal_score = pow (ideal_score, 1.1); score[d] += length_pen * ideal_score; @@ -381,25 +367,22 @@ Beam::score_stem_lengths (vector const &stems, count[d]++; } + /* Divide by number of stems, to make the measure scale-free. */ Direction d = DOWN; do score[d] /= max (count[d], 1); while (flip (&d) != DOWN); - return score[LEFT] + score[RIGHT]; + config->add (score[LEFT] + score[RIGHT], "L"); } -Real -Beam::score_slopes_dy (Real yl, Real yr, - Real dy_mus, Real dy_damp, - Real dx, - bool xstaff, - - Beam_quant_parameters const *parameters) +void +Beam_scoring_problem::score_slopes_dy (Beam_configuration *config) const { - Real dy = yr - yl; + Real dy = config->y.delta (); + Real damped_dy = unquanted_y.delta(); Real dem = 0.0; - + /* DAMPING_DIRECTION_PENALTY is a very harsh measure, while for complex beaming patterns, horizontal is often a good choice. @@ -407,34 +390,34 @@ Beam::score_slopes_dy (Real yl, Real yr, TODO: find a way to incorporate the complexity of the beam in this penalty. */ - if (sign (dy_damp) != sign (dy)) + if (sign (damped_dy) != sign (dy)) { if (!dy) { - if (fabs (dy_damp / dx) > parameters->ROUND_TO_ZERO_SLOPE) - dem += parameters->DAMPING_DIRECTION_PENALTY; + if (fabs (damped_dy / x_span.delta ()) > parameters.ROUND_TO_ZERO_SLOPE) + dem += parameters.DAMPING_DIRECTION_PENALTY; else - dem += parameters->HINT_DIRECTION_PENALTY; + dem += parameters.HINT_DIRECTION_PENALTY; } else - dem += parameters->DAMPING_DIRECTION_PENALTY; + dem += parameters.DAMPING_DIRECTION_PENALTY; } - dem += parameters->MUSICAL_DIRECTION_FACTOR - * max (0.0, (fabs (dy) - fabs (dy_mus))); + dem += parameters.MUSICAL_DIRECTION_FACTOR + * max (0.0, (fabs (dy) - fabs (musical_dy))); - Real slope_penalty = parameters->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. */ - if (xstaff) + if (is_xstaff) slope_penalty *= 10; /* Huh, why would a too steep beam be better than a too flat one ? */ - dem += shrink_extra_weight (fabs (dy_damp) - fabs (dy), 1.5) + dem += shrink_extra_weight (fabs (damped_dy) - fabs (dy), 1.5) * slope_penalty; - return dem; + config->add (dem, "S"); } static Real @@ -448,31 +431,23 @@ my_modf (Real x) because for 32nd and 64th beams the forbidden quants are relatively more important than stem lengths. */ -Real -Beam::score_forbidden_quants (Real yl, Real yr, - Real radius, - Real slt, - Real beam_thickness, Real beam_translation, - Drul_array beam_counts, - Direction ldir, Direction rdir, - - Beam_quant_parameters const *parameters) +void +Beam_scoring_problem::score_forbidden_quants (Beam_configuration *config) const { - Real dy = yr - yl; - Drul_array y (yl, yr); - Drul_array dirs (ldir, rdir); + Real dy = config->y.delta (); - Real extra_demerit = parameters->SECONDARY_BEAM_DEMERIT / (max (beam_counts[LEFT], beam_counts[RIGHT])); + Real extra_demerit = parameters.SECONDARY_BEAM_DEMERIT / + max (edge_beam_counts[LEFT], edge_beam_counts[RIGHT]); Direction d = LEFT; Real dem = 0.0; - Real eps = parameters->BEAM_EPS; + Real eps = parameters.BEAM_EPS; do { - for (int j = 1; j <= beam_counts[d]; j++) + for (int j = 1; j <= edge_beam_counts[d]; j++) { - Direction stem_dir = dirs[d]; + Direction stem_dir = edge_dirs[d]; /* The 2.2 factor is to provide a little leniency for @@ -480,15 +455,15 @@ Beam::score_forbidden_quants (Real yl, Real yr, will be in the gap of the (2, sit) quant, leading to a false demerit. */ - Real gap1 = y[d] - stem_dir * ((j - 1) * beam_translation + beam_thickness / 2 - slt / 2.2); - Real gap2 = y[d] - stem_dir * (j * beam_translation - beam_thickness / 2 + slt / 2.2); + Real gap1 = config->y[d] - stem_dir * ((j - 1) * beam_translation + beam_thickness / 2 - line_thickness / 2.2); + Real gap2 = config->y[d] - stem_dir * (j * beam_translation - beam_thickness / 2 + line_thickness / 2.2); Interval gap; gap.add_point (gap1); gap.add_point (gap2); - for (Real k = -radius; - k <= radius + eps; k += 1.0) + for (Real k = -staff_radius; + k <= staff_radius + eps; k += 1.0) if (gap.contains (k)) { Real dist = min (fabs (gap[UP] - k), fabs (gap[DOWN] - k)); @@ -506,43 +481,45 @@ Beam::score_forbidden_quants (Real yl, Real yr, } while ((flip (&d)) != LEFT); - if (max (beam_counts[LEFT], beam_counts[RIGHT]) >= 2) + if (max (edge_beam_counts[LEFT], edge_beam_counts[RIGHT]) >= 2) { Real straddle = 0.0; - Real sit = (beam_thickness - slt) / 2; + Real sit = (beam_thickness - line_thickness) / 2; Real inter = 0.5; - Real hang = 1.0 - (beam_thickness - slt) / 2; + Real hang = 1.0 - (beam_thickness - line_thickness) / 2; Direction d = LEFT; do { - if (beam_counts[d] >= 2 - && fabs (y[d] - dirs[d] * beam_translation) < radius + inter) + if (edge_beam_counts[d] >= 2 + && fabs (config->y[d] - edge_dirs[d] * beam_translation) < staff_radius + inter) { - if (dirs[d] == UP && dy <= eps - && fabs (my_modf (y[d]) - sit) < eps) + // TODO up/down symmetry. + if (edge_dirs[d] == UP && dy <= eps + && fabs (my_modf (config->y[d]) - sit) < eps) dem += extra_demerit; - if (dirs[d] == DOWN && dy >= eps - && fabs (my_modf (y[d]) - hang) < eps) + if (edge_dirs[d] == DOWN && dy >= eps + && fabs (my_modf (config->y[d]) - hang) < eps) dem += extra_demerit; } - if (beam_counts[d] >= 3 - && fabs (y[d] - 2 * dirs[d] * beam_translation) < radius + inter) + if (edge_beam_counts[d] >= 3 + && fabs (config->y[d] - 2 * edge_dirs[d] * beam_translation) < staff_radius + inter) { - if (dirs[d] == UP && dy <= eps - && fabs (my_modf (y[d]) - straddle) < eps) + // TODO up/down symmetry. + if (edge_dirs[d] == UP && dy <= eps + && fabs (my_modf (config->y[d]) - straddle) < eps) dem += extra_demerit; - if (dirs[d] == DOWN && dy >= eps - && fabs (my_modf (y[d]) - straddle) < eps) + if (edge_dirs[d] == DOWN && dy >= eps + && fabs (my_modf (config->y[d]) - straddle) < eps) dem += extra_demerit; } } while (flip (&d) != LEFT); } - return dem; + config->add (dem, "F"); } diff --git a/lily/beam.cc b/lily/beam.cc index 3197c7d29f..d31e570186 100644 --- a/lily/beam.cc +++ b/lily/beam.cc @@ -37,14 +37,16 @@ #include "beam.hh" +#include "beam-scoring-problem.hh" #include "beaming-pattern.hh" #include "directional-element-interface.hh" -#include "main.hh" +#include "grob-array.hh" #include "international.hh" #include "interval-set.hh" #include "item.hh" #include "least-squares.hh" #include "lookup.hh" +#include "main.hh" #include "misc.hh" #include "output-def.hh" #include "pointer-group-interface.hh" @@ -52,7 +54,6 @@ #include "staff-symbol-referencer.hh" #include "stem.hh" #include "warn.hh" -#include "grob-array.hh" #if DEBUG_BEAM_SCORING #include "text-interface.hh" // debug output. @@ -1144,7 +1145,6 @@ Beam::slope_damping (SCM smob, SCM posns) if (normal_stem_count (me) <= 1) return posns; - SCM s = me->get_property ("damping"); Real damping = scm_to_double (s); Real concaveness = robust_scm2double (me->get_property ("concaveness"), 0.0); @@ -1186,6 +1186,21 @@ Beam::slope_damping (SCM smob, SCM posns) return ly_interval2scm (pos); } + +MAKE_SCHEME_CALLBACK (Beam, quanting, 2); +SCM +Beam::quanting (SCM smob, SCM posns) +{ + Grob *me = unsmob_grob (smob); + Drul_array ys(0, 0); + ys = robust_scm2drul (posns, ys); + Beam_scoring_problem problem (me, ys); + + ys = problem.solve (); + return ly_interval2scm (ys); +} + + /* Report slice containing the numbers that are both in (car BEAMING) and (cdr BEAMING) diff --git a/lily/include/beam-scoring-problem.hh b/lily/include/beam-scoring-problem.hh new file mode 100644 index 0000000000..8026f43061 --- /dev/null +++ b/lily/include/beam-scoring-problem.hh @@ -0,0 +1,150 @@ +/* + This file is part of LilyPond, the GNU music typesetter. + + Copyright (C) 1996--2011 Han-Wen Nienhuys + Jan Nieuwenhuizen + + LilyPond is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + LilyPond is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with LilyPond. If not, see . +*/ + +#ifndef BEAM_SCORING_PROBLEM_HH +#define BEAM_SCORING_PROBLEM_HH + +#include "interval.hh" +#include "lily-proto.hh" +#include "std-vector.hh" +#include "stem-info.hh" +#include "main.hh" // DEBUG_BEAM_SCORING + +// Unused for now. +enum Scorers { + // Should be ordered by increasing expensiveness. + ORIGINAL_DISTANCE, + SLOPES, + FORBIDDEN, + STEM_LENGTHS, + NUM_SCORERS, +}; + +struct Beam_configuration +{ + Interval y; + Real demerits; + +#if DEBUG_BEAM_SCORING + string score_card_; +#endif + + int next_scorer_todo; + + Beam_configuration (); + bool done () const; + void add (Real demerit, const string &reason); + static Beam_configuration* new_config(Interval start, + Interval offset); +}; + +// Comparator for a queue of Beam_configuration*. +class Beam_configuration_less +{ + bool operator() (Beam_configuration* const& l, Beam_configuration* const& r) + { + return l->demerits < r->demerits; + } +}; + + +struct Beam_quant_parameters +{ + Real SECONDARY_BEAM_DEMERIT; + Real STEM_LENGTH_DEMERIT_FACTOR; + Real REGION_SIZE; + + /* + threshold to combat rounding errors. + */ + Real BEAM_EPS; + + // possibly ridiculous, but too short stems just won't do + Real STEM_LENGTH_LIMIT_PENALTY; + Real DAMPING_DIRECTION_PENALTY; + Real MUSICAL_DIRECTION_FACTOR; + Real HINT_DIRECTION_PENALTY; + Real IDEAL_SLOPE_FACTOR; + Real ROUND_TO_ZERO_SLOPE; + + void fill (Grob *him); +}; + + + +/* + Parameters for a single beam. Precomputed to save time in + scoring individual configurations. + + TODO - use trailing _ on data members. + + */ +class Beam_scoring_problem +{ +public: + Beam_scoring_problem (Grob *me, Drul_array ys); + Drul_array solve() const; + +private: + Grob *beam; + + Interval unquanted_y; + + Real staff_space; + Real beam_thickness; + Real line_thickness; + Real musical_dy; + + Interval x_span; + + vector stem_infos; + + /* + Do stem computations. These depend on YL and YR linearly, so we can + precompute for every stem 2 factors. + + We store some info to quickly interpolate. The stemlengths are + affine linear in YL and YR. If YL == YR == 0, then we might have + stem_y != 0.0, when we're cross staff. + */ + vector base_lengths; + vector stem_xpositions; + + Grob *common[2]; + bool is_xstaff; + bool is_knee; + + Beam_quant_parameters parameters; + + Real staff_radius; + Drul_array edge_beam_counts; + Drul_array edge_dirs; + Real beam_translation; + + void init_stems (); + + // Scoring functions: + void score_forbidden_quants (Beam_configuration *config) const; + void score_slopes_dy (Beam_configuration *config) const; + void score_stem_lengths (Beam_configuration* config) const; + void generate_quants(vector* scores) const; +}; + +#endif /* BEAM_SCORING_PROBLEM_HH */ diff --git a/lily/include/beam.hh b/lily/include/beam.hh index 1fbb74f981..52be4c4447 100644 --- a/lily/include/beam.hh +++ b/lily/include/beam.hh @@ -26,31 +26,6 @@ #include "lily-proto.hh" #include "stem-info.hh" -/* - TODO: move quanting in separate file. -*/ -struct Beam_quant_parameters -{ - Real SECONDARY_BEAM_DEMERIT; - Real STEM_LENGTH_DEMERIT_FACTOR; - Real REGION_SIZE; - - /* - threshold to combat rounding errors. - */ - Real BEAM_EPS; - - // possibly ridiculous, but too short stems just won't do - Real STEM_LENGTH_LIMIT_PENALTY; - Real DAMPING_DIRECTION_PENALTY; - Real MUSICAL_DIRECTION_FACTOR; - Real HINT_DIRECTION_PENALTY; - Real IDEAL_SLOPE_FACTOR; - Real ROUND_TO_ZERO_SLOPE; - - void fill (Grob *him); -}; - struct Beam_segment { int vertical_count_; @@ -70,7 +45,6 @@ struct Beam_stem_segment bool gapped_; Direction dir_; int max_connect_; - }; @@ -112,30 +86,19 @@ public: DECLARE_SCHEME_CALLBACK (slope_damping, (SCM, SCM)); DECLARE_SCHEME_CALLBACK (quanting, (SCM, SCM)); -static Real score_slopes_dy (Real, Real, Real, Real, Real, bool, Beam_quant_parameters const *); - - static Real 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, Beam_quant_parameters const *); - static Real score_forbidden_quants (Real, Real, - Real, Real, Real, Real, - Drul_array, Direction, Direction, - Beam_quant_parameters const *); - static int get_direction_beam_count (Grob *me, Direction d); + private: + friend class Beam_scoring_problem; + static Direction get_default_dir (Grob *); static void set_stem_directions (Grob *, Direction); static void consider_auto_knees (Grob *); static void set_stem_shorten (Grob *); + static int forced_stem_count (Grob *); static Real calc_stem_y (Grob *, Grob *s, Grob **c, Real, Real, Direction, Drul_array pos, bool french); - static int forced_stem_count (Grob *); }; -- 2.39.2