X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=lily%2Fbeam-quanting.cc;h=c5cafd5cfa9acb63e93723eaa7aa76347b06cc8a;hb=a4668131900363364bb2454700c40183cedf4c9b;hp=4b822cbac48a6289112b740d6b03172d701fecc3;hpb=17a931f59656dce7d0979520ea5e6ff60bbab698;p=lilypond.git diff --git a/lily/beam-quanting.cc b/lily/beam-quanting.cc index 4b822cbac4..c5cafd5cfa 100644 --- a/lily/beam-quanting.cc +++ b/lily/beam-quanting.cc @@ -20,18 +20,23 @@ #include "beam-scoring-problem.hh" -#include #include +#include +#include using namespace std; #include "align-interface.hh" #include "beam.hh" +#include "direction.hh" +#include "directional-element-interface.hh" #include "grob.hh" #include "international.hh" +#include "libc-extension.hh" #include "main.hh" #include "output-def.hh" #include "pointer-group-interface.hh" #include "staff-symbol-referencer.hh" +#include "stencil.hh" #include "stem.hh" #include "warn.hh" @@ -50,18 +55,29 @@ Beam_quant_parameters::fill (Grob *him) { SCM details = him->get_property ("details"); + // General + BEAM_EPS = get_detail (details, ly_symbol2scm ("beam-eps"), 1e-3); + REGION_SIZE = get_detail (details, ly_symbol2scm ("region-size"), 2); + + // forbidden quants 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); + HORIZONTAL_INTER_QUANT_PENALTY = get_detail (details, ly_symbol2scm ("horizontal-inter-quant"), 500); + 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); + + // Collisions + COLLISION_PENALTY = get_detail (details, ly_symbol2scm ("collision-penalty"), 500); + COLLISION_PADDING = get_detail (details, ly_symbol2scm ("collision-padding"), 0.5); + STEM_COLLISION_FACTOR = get_detail (details, ly_symbol2scm ("stem-collision-factor"), 0.1); } +// Add x if x is positive, add |x|*fac if x is negative. static Real shrink_extra_weight (Real x, Real fac) { @@ -108,6 +124,11 @@ Beam_configuration* Beam_configuration::new_config (Interval start, return qs; } +Real +Beam_scoring_problem::y_at (Real x, Beam_configuration const* p) const { + return p->y[LEFT] + (x - x_span[LEFT]) * p->y.delta() / x_span.delta(); +} + /****************************************************************/ /* @@ -115,27 +136,9 @@ Beam_configuration* Beam_configuration::new_config (Interval start, - Make all demerits customisable - - One sensible check per demerit (what's this --hwn) - - Add demerits for quants per se, as to forbid a specific quant entirely */ -int -best_quant_score_idx (vector const &configs) -{ - Real best = 1e6; - int best_idx = -1; - for (vsize i = configs.size (); i--;) - { - if (configs[i]->demerits < best) - { - best = configs [i]->demerits; - best_idx = i; - } - } - - return best_idx; -} // This is a temporary hack to see how much we can gain by using a // priority queue on the beams to score. @@ -146,12 +149,101 @@ LY_DEFINE (ly_beam_score_count, "ly:beam-score-count", 0, 0, 0, return scm_from_int (score_count); } +void Beam_scoring_problem::add_collision (Real x, Interval y, + Real score_factor) +{ + if (edge_dirs[LEFT] == edge_dirs[RIGHT]) { + Direction d = edge_dirs[LEFT]; + + Real quant_range_y = quant_range[LEFT][-d] + + (x - x_span[LEFT]) * (quant_range[RIGHT][-d] - quant_range[LEFT][-d]) / x_span.delta(); + + if (d*(quant_range_y - minmax(d, y[UP], y[DOWN])) > 0) { + return; + } + } + + Beam_collision c; + c.beam_y_.set_empty (); + + for (vsize j = 0; j < segments_.size (); j++) + { + if (segments_[j].horizontal_.contains(x)) + c.beam_y_.add_point (segments_[j].vertical_count_ * beam_translation); + if (segments_[j].horizontal_[LEFT] > x) + break; + } + c.beam_y_.widen (0.5 * beam_thickness); + + c.x_ = x; + + y *= 1/staff_space; + c.y_ = y; + c.base_penalty_ = score_factor; + collisions_.push_back (c); +} + +void Beam_scoring_problem::init_collisions (vector grobs) +{ + Grob* common_x = NULL; + segments_ = Beam::get_beam_segments (beam, &common_x); + vector_sort (segments_, beam_segment_less); + if (common[X_AXIS] != common_x) + { + programming_error ("Disagree on common x. Skipping collisions in beam scoring."); + return; + } + + set stems; + for (vsize i = 0; i < grobs.size (); i++) { + Box b; + for (Axis a = X_AXIS; a < NO_AXES; incr (a)) + b[a] = grobs[i]->extent(common[a], a); + + Real width = b[X_AXIS].length (); + Real width_factor = sqrt (width / staff_space); + + Direction d = LEFT; + do + add_collision (b[X_AXIS][d], b[Y_AXIS], width_factor); + while (flip (&d) != LEFT); + + Grob* stem = unsmob_grob (grobs[i]->get_object ("stem")); + if (stem && Stem::has_interface (stem) && Stem::is_normal_stem (stem)) + { + stems.insert (stem); + } + } + + for (set::const_iterator it(stems.begin ()); it != stems.end (); it++) + { + Grob *s = *it; + Real x = s->extent (common[X_AXIS], X_AXIS).center(); + + Direction stem_dir = get_grob_direction (*it); + Interval y; + y.set_full (); + y[-stem_dir] = Stem::chord_start_y (*it) + (*it)->relative_coordinate (common[Y_AXIS], Y_AXIS) + - beam->relative_coordinate (common[Y_AXIS], Y_AXIS); + + Real factor = parameters.STEM_COLLISION_FACTOR; + if (!unsmob_grob (s->get_object ("beam")) + && !Stem::flag (s).is_empty ()) + factor = 1.0; + add_collision (x, y, factor); + } +} + void Beam_scoring_problem::init_stems () { + extract_grob_set (beam, "covered-grobs", collisions); extract_grob_set (beam, "stems", stems); for (int a = 2; a--;) - common[a] = common_refpoint_of_array (stems, beam, Axis (a)); - + { + common[a] = common_refpoint_of_array (stems, beam, Axis (a)); + common[a] = common_refpoint_of_array (collisions, common[a], Axis (a)); + } + Drul_array edge_stems(Beam::first_normal_stem (beam), Beam::last_normal_stem (beam)); Direction d = LEFT; @@ -201,6 +293,10 @@ void Beam_scoring_problem::init_stems () d = LEFT; do { + quant_range[d].set_full (); + if (!edge_stems[d]) + continue; + Real stem_offset = edge_stems[d]->relative_coordinate (common[Y_AXIS], Y_AXIS) - beam->relative_coordinate (common[Y_AXIS], Y_AXIS); Interval heads = Stem::head_positions(edge_stems[d]) * 0.5 * staff_space; @@ -208,10 +304,11 @@ void Beam_scoring_problem::init_stems () Direction ed = edge_dirs[d]; heads.widen(0.5 * staff_space + (edge_beam_counts[d] - 1) * beam_translation + beam_thickness * .5); - quant_range[d][ed] = ed * infinity_f; quant_range[d][-ed] = heads[ed] + stem_offset; } while (flip (&d) != LEFT); + + init_collisions (collisions); } Beam_scoring_problem::Beam_scoring_problem (Grob *me, Drul_array ys) @@ -239,11 +336,11 @@ Beam_scoring_problem::generate_quants (vector *scores) cons { int region_size = (int) parameters.REGION_SIZE; - /* - Knees are harder, lets try some more possibilities for knees. - */ + // Knees and collisions are harder, lets try some more possibilities if (is_knee) region_size += 2; + if (collisions_.size ()) + region_size += 2; Real straddle = 0.0; Real sit = (beam_thickness - line_thickness) / 2; @@ -292,8 +389,14 @@ void Beam_scoring_problem::one_scorer (Beam_configuration* config) const { score_count ++; switch (config->next_scorer_todo) { - case SLOPES: - score_slopes_dy (config); + case SLOPE_IDEAL: + score_slope_ideal (config); + break; + case SLOPE_DIRECTION: + score_slope_direction (config); + break; + case SLOPE_MUSICAL: + score_slope_musical (config); break; case FORBIDDEN: score_forbidden_quants (config); @@ -301,7 +404,13 @@ void Beam_scoring_problem::one_scorer (Beam_configuration* config) const case STEM_LENGTHS: score_stem_lengths (config); break; - + case COLLISIONS: + score_collisions (config); + break; + case HORIZONTAL_INTER: + score_horizontal_inter_quants (config); + break; + case NUM_SCORERS: case ORIGINAL_DISTANCE: default: @@ -329,6 +438,9 @@ Beam_scoring_problem::force_score (SCM inspect_quants, const vector 1e5) programming_error ("cannot find quant"); + while (!best->done ()) + one_scorer (best); + return best; } @@ -337,12 +449,20 @@ Beam_scoring_problem::solve () const { vector configs; generate_quants (&configs); + if (configs.empty ()) + { + programming_error ("No viable beam quanting found. Using unquanted y value."); + return unquanted_y; + } + Beam_configuration *best = NULL; + bool debug = + to_boolean (beam->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)) + if (scm_is_pair (inspect_quants)) { + debug = true; best = force_score (inspect_quants, configs); } else @@ -352,7 +472,6 @@ Beam_scoring_problem::solve () const { for (vsize i = 0; i < configs.size(); i++) queue.push(configs[i]); - /* TODO @@ -383,7 +502,7 @@ Beam_scoring_problem::solve () const { Interval final_positions = best->y; #if DEBUG_BEAM_SCORING - if (to_boolean (beam->layout ()->lookup_variable (ly_symbol2scm ("debug-beam-scoring")))) + if (debug) { // debug quanting int completed = 0; @@ -394,7 +513,7 @@ Beam_scoring_problem::solve () const { } string card = best->score_card_ + to_string (" c%d/%d", completed, configs.size()); - beam->set_property ("quant-score", ly_string2scm (card)); + beam->set_property ("annotation", ly_string2scm (card)); } #endif @@ -447,12 +566,11 @@ Beam_scoring_problem::score_stem_lengths (Beam_configuration* config) const } void -Beam_scoring_problem::score_slopes_dy (Beam_configuration *config) const +Beam_scoring_problem::score_slope_direction (Beam_configuration *config) const { 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. @@ -472,10 +590,28 @@ Beam_scoring_problem::score_slopes_dy (Beam_configuration *config) const else dem += parameters.DAMPING_DIRECTION_PENALTY; } - - dem += parameters.MUSICAL_DIRECTION_FACTOR + + config->add (dem, "Sd"); +} + +// Score for going against the direction of the musical pattern +void +Beam_scoring_problem::score_slope_musical (Beam_configuration *config) const +{ + Real dy = config->y.delta (); + Real dem = parameters.MUSICAL_DIRECTION_FACTOR * max (0.0, (fabs (dy) - fabs (musical_dy))); + config->add (dem, "Sm"); +} +// Score deviation from calculated ideal slope. +void +Beam_scoring_problem::score_slope_ideal (Beam_configuration *config) const +{ + Real dy = config->y.delta (); + Real damped_dy = unquanted_y.delta(); + Real dem = 0.0; + Real slope_penalty = parameters.IDEAL_SLOPE_FACTOR; /* Xstaff beams tend to use extreme slopes to get short stems. We @@ -487,7 +623,7 @@ Beam_scoring_problem::score_slopes_dy (Beam_configuration *config) const dem += shrink_extra_weight (fabs (damped_dy) - fabs (dy), 1.5) * slope_penalty; - config->add (dem, "S"); + config->add (dem, "Si"); } static Real @@ -496,6 +632,21 @@ my_modf (Real x) return x - floor (x); } +// TODO - there is some overlap with forbidden quants, but for +// horizontal beams, it is much more serious to have stafflines +// appearing in the wrong place, so we have a separate scorer. +void +Beam_scoring_problem::score_horizontal_inter_quants (Beam_configuration *config) const +{ + if (config->y.delta () == 0.0 + && abs (config->y[LEFT]) < staff_radius * staff_space) + { + Real yshift = config->y[LEFT] - 0.5 * staff_space; + if (fabs (my_round (yshift) - yshift) < 0.01 * staff_space) + config->add (parameters.HORIZONTAL_INTER_QUANT_PENALTY, "H"); + } +} + /* TODO: The fixed value SECONDARY_BEAM_DEMERIT is probably flawed: because for 32nd and 64th beams the forbidden quants are relatively @@ -512,7 +663,7 @@ Beam_scoring_problem::score_forbidden_quants (Beam_configuration *config) const Direction d = LEFT; Real dem = 0.0; Real eps = parameters.BEAM_EPS; - + do { for (int j = 1; j <= edge_beam_counts[d]; j++) @@ -593,3 +744,32 @@ Beam_scoring_problem::score_forbidden_quants (Beam_configuration *config) const config->add (dem, "F"); } +void +Beam_scoring_problem::score_collisions (Beam_configuration *config) const +{ + Real demerits = 0.0; + for (vsize i = 0; i < collisions_.size (); i++) + { + Interval collision_y = collisions_[i].y_; + Real x = collisions_[i].x_; + + Real center_beam_y = y_at (x, config); + Interval beam_y = center_beam_y + collisions_[i].beam_y_; + + Real dist = infinity_f; + if (!intersection (beam_y, collision_y).is_empty ()) + dist = 0.0; + else + dist = min (beam_y.distance (collision_y[DOWN]), + beam_y.distance (collision_y[UP])); + + Real scale_free = + max (parameters.COLLISION_PADDING - dist, 0.0)/ + parameters.COLLISION_PADDING; + demerits += + collisions_[i].base_penalty_ * + pow (scale_free, 3) * parameters.COLLISION_PENALTY; + } + + config->add (demerits, "C"); +}