commit aa271b4aaf5bb48bdeea41da6e44a883de1ed0de Author: Han-Wen Nienhuys Date: Mon Jan 24 12:15:48 2011 -0200 Proof of concept: use scoring for beam collisions. diff --git a/input/regression/beam-collision.ly b/input/regression/beam-collision.ly index b73ce8a..b91d060 100644 --- a/input/regression/beam-collision.ly +++ b/input/regression/beam-collision.ly @@ -1,19 +1,46 @@ \version "2.13.47" \header { - texidoc = "When set manually, beams do not collide in polyphonic textures. -" + texidoc = "Manual beams do not collide with notes, accidentals, etc." + tagline = "" } -\relative c'' { - \time 4/4 - << { s128 e[ s cis,] } \\ { b''[ s b] } >> r8.. - << { s8 d,,[ s d] } \\ { f'8[ s fis] } >> - << { s16 c,16[ s c''] } \\ { b16[ s d,,] } >> | - \time 3/4 - << { \times 4/5 { s32 e[ s g s g, s e'' s b'] } } \\ - { \times 4/5 { c,,32[ s c'' s g, s b s d s] } } >> - \clef bass g,,,8[ \clef treble d''8] - b8[ c | - \time 2/4 - d e] r4 | +\relative \new Staff { + + << + \new Voice { + \voiceOne + \repeat unfold 8 { c8[ c] } + } + + \new Voice \relative c'' { + \voiceThree + \autoBeamOff + f f e e + d d c c + b b a a + g g f f + \break + } + >> + << + \new Voice { + \repeat unfold 8 \relative { + \voiceOne + c8[ + \voiceTwo + c''] + } + } + \new Voice \relative { + \voiceFour + s8 f + s8 g + s8 a + s8 b + s8 c + s8 d + s8 e + } + >> } + diff --git a/lily/beam-quanting.cc b/lily/beam-quanting.cc index 2ecf070..f6338e7 100644 --- a/lily/beam-quanting.cc +++ b/lily/beam-quanting.cc @@ -58,6 +58,8 @@ Beam_quant_parameters::fill (Grob *him) 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); + COLLISION_PENALTY = get_detail (details, ly_symbol2scm ("collision-penalty"), 500); + COLLISION_DISTANCE = get_detail (details, ly_symbol2scm ("collision-distance"), 0.5); } static Real @@ -150,6 +152,9 @@ Beam::quanting (SCM smob, SCM posns) */ vector stems = extract_grob_array (me, "stems"); + + extract_grob_set (me, "covered-grobs", covered_grobs); + vector stem_infos; vector base_lengths; vector stem_xposns; @@ -157,8 +162,11 @@ Beam::quanting (SCM smob, SCM posns) Drul_array dirs_found (0, 0); Grob *common[2]; for (int a = 2; a--;) - common[a] = common_refpoint_of_array (stems, me, Axis (a)); - + { + common[a] = common_refpoint_of_array (stems, me, Axis (a)); + common[a] = common_refpoint_of_array (covered_grobs, me, 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; @@ -194,6 +202,18 @@ Beam::quanting (SCM smob, SCM posns) stem_xposns.push_back (s->relative_coordinate (common[X_AXIS], X_AXIS)); } + + /* + Get dimensions of possible collisions. + */ + vector covered; + for (vsize i = 0; i < covered_grobs.size(); i++) + { + Grob* g = covered_grobs[i]; + covered.push_back (Box(g->extent (common[X_AXIS], X_AXIS), + g->extent (common[Y_AXIS], Y_AXIS))); + } + bool xstaff = Align_interface::has_interface (common[Y_AXIS]); Direction ldir = Direction (stem_infos[0].dir_); @@ -237,6 +257,20 @@ Beam::quanting (SCM smob, SCM posns) parameters outside of the loop, we can save a lot of time. */ for (vsize i = qscores.size (); i--;) { + Real d = score_collisions (qscores[i].yl, qscores[i].yr, + covered, + beam_thickness, + xl, xr, + ¶meters); + qscores[i].demerits += d; + +#if DEBUG_BEAM_SCORING + qscores[i].score_card_ += to_string (" C %.2f", d); +#endif + } + + for (vsize i = qscores.size (); i--;) + { Real d = score_slopes_dy (qscores[i].yl, qscores[i].yr, dy_mus, yr- yl, xr - xl, @@ -308,6 +342,7 @@ Beam::quanting (SCM smob, SCM posns) } #endif + Interval final_positions; if (best_idx < 0) { @@ -546,3 +581,41 @@ Beam::score_forbidden_quants (Real yl, Real yr, return dem; } + +/* + TODO + + - handle beam multiplicity correctly + - tune parameters + - feathered beams? (probably overkill?) +*/ +Real +Beam::score_collisions (Real yl, Real yr, + vector grob_dimensions, + Real beam_thickness, + Real xl, Real xr, + Beam_quant_parameters const *parameters) +{ + Real demerits = 0.0; + for (vsize i = 0; i < grob_dimensions.size (); i++) + { + Interval y_dims = grob_dimensions[i][Y_AXIS]; + Direction d = LEFT; + do + { + Real x = grob_dimensions[i][X_AXIS][d]; + Real beam_y = yl + (yr - yl) / (xr - xl) * (x - xl); + Interval beam_y_extent = Interval(-.5, .5) * beam_thickness + beam_y; + + Real dist = min(beam_y_extent.distance(y_dims[DOWN]), + beam_y_extent.distance(y_dims[UP])); + Real scale_free = + max(parameters->COLLISION_DISTANCE - dist, 0.0)/ + parameters->COLLISION_DISTANCE; + demerits += + pow(scale_free, 3) * parameters->COLLISION_PENALTY; + } + while ((flip (&d)) != LEFT); + } + return demerits; +} diff --git a/lily/beam.cc b/lily/beam.cc index 1f70dfb..89fbac2 100644 --- a/lily/beam.cc +++ b/lily/beam.cc @@ -928,134 +928,6 @@ Beam::no_visible_stem_positions (Grob *me, Interval default_value) } /* - Raise the quanted beam for collisions. -*/ -MAKE_SCHEME_CALLBACK (Beam, move_to_avoid_collisions, 2); -SCM -Beam::move_to_avoid_collisions (SCM smob, SCM posns) -{ - Grob *me = unsmob_grob (smob); - extract_grob_set (me, "normal-stems", stems); - - if (!is_knee (me) && !is_cross_staff (me) && first_normal_stem (me) && last_normal_stem (me)) - { - Direction dir = to_dir (first_normal_stem (me)->get_property ("direction")); - Real offset = 0.0; - Interval pos = ly_scm2interval (posns); - extract_grob_set (me, "covered-grobs", covered_grobs); - Grob *common_x = common_refpoint_of_array (stems, me, X_AXIS); - Grob *common_y = common_refpoint_of_array (stems, me, Y_AXIS); - vector segments = get_beam_segments (me, &common_x); - - - Real beam_translation = get_beam_translation (me); - Real beam_thickness = get_beam_thickness (me); - - Interval span; - span[LEFT] = first_normal_stem (me)->relative_coordinate (common_x, X_AXIS); - span[RIGHT] = last_normal_stem (me)->relative_coordinate (common_x, X_AXIS); - - /* blech...kludge - for some reason, the leftmost segments[i].horizontal_[LEFT] is not equal to - span[LEFT] above. I believe that span[LEFT] is the correct X coordinate, - and thus, I use this corrective to do the calculations (usually, it is a very - small number, but sometimes matters. - */ - Real left_guess = 100000.0; - for (vsize i = 0; i < segments.size(); i++) - left_guess = min(segments[i].horizontal_[LEFT], left_guess); - Real left_corrective = span[LEFT] - left_guess; - /* end kludge */ - - Direction feather_dir = to_dir (me->get_property ("grow-direction")); - - Real slope = (pos[LEFT] - pos[RIGHT]) / (span[LEFT] - span[RIGHT]); - - for (vsize i = 0; i < segments.size(); i++) - { - Real temp_offset = 0.0; - - /* ugh...code dup from the print function */ - Real local_slope = slope; - if (feather_dir) - local_slope += feather_dir * segments[i].vertical_count_ * beam_translation / span.length (); - - Interval local_span; - local_span[LEFT] = segments[i].horizontal_[LEFT] + left_corrective; - local_span[RIGHT] = segments[i].horizontal_[RIGHT] + left_corrective; - Real local_down = local_slope - * (local_span[LEFT] - span.linear_combination (feather_dir)) - + pos.linear_combination (feather_dir) - + beam_translation * segments[i].vertical_count_; - Real local_width = local_span[RIGHT] - local_span[LEFT]; - - for (vsize j = 0; j < covered_grobs.size (); j++) - { - Grob *covered_grob = covered_grobs.at (j); - Real left = 0.0; - Real down = 0.0; - Real width = 0.0; - Real height = 0.0; - - if (covered_grob->is_live ()) - { - left = covered_grob->extent(common_x, X_AXIS)[LEFT] - local_span[LEFT]; - down = covered_grob->extent(common_y, Y_AXIS)[DOWN] - local_down; - width = covered_grob->extent(common_x, X_AXIS).length (); - - if (left > local_width) - continue; - if (left + width < 0) - continue; - height = covered_grob->extent(common_y, Y_AXIS).length (); - if (height <= 0.0) - continue; - } - else - continue; - - /* seems to be the correct amount of breathing room */ - Real y_val = beam_thickness; - if (dir == DOWN) - y_val *= -1.0; - if (dir == UP) - y_val += down + height; - else - y_val += down; - - Real beam_y1 = local_slope * left; - Real beam_y2 = local_slope * (left + width); - - if (dir == UP) - { - if (y_val > beam_y1) - temp_offset = max (y_val - beam_y1, temp_offset); - if (y_val > beam_y2) - temp_offset = max (y_val - beam_y2, temp_offset); - } - else - { - if (y_val < beam_y1) - temp_offset = min(y_val - beam_y1, temp_offset); - if (y_val < beam_y2) - temp_offset = min(y_val - beam_y2, temp_offset); - } - } - - if (dir == UP) - offset = max (offset, temp_offset); - else - offset = min (offset, temp_offset); - } - - pos[LEFT] += offset; - pos[RIGHT] += offset; - return ly_interval2scm (pos); - } - else - return posns; -} -/* Compute a first approximation to the beam slope. */ MAKE_SCHEME_CALLBACK (Beam, calc_least_squares_positions, 2); diff --git a/lily/include/beam.hh b/lily/include/beam.hh index 2263752..47cbf34 100644 --- a/lily/include/beam.hh +++ b/lily/include/beam.hh @@ -47,6 +47,8 @@ struct Beam_quant_parameters Real HINT_DIRECTION_PENALTY; Real IDEAL_SLOPE_FACTOR; Real ROUND_TO_ZERO_SLOPE; + Real COLLISION_PENALTY; + Real COLLISION_DISTANCE; void fill (Grob *him); }; @@ -113,8 +115,14 @@ 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_slopes_dy (Real, Real, Real, Real, Real, bool, Beam_quant_parameters const *); + static Real score_collisions (Real yl, Real yr, + vector grob_dimensions, + Real beam_thickness, + Real xl, Real xr, + Beam_quant_parameters const *parameters); + static Real score_stem_lengths (vector const &stems, vector const &stem_infos, vector const &base_stem_ys, diff --git a/scm/define-grobs.scm b/scm/define-grobs.scm index 18d9378..02160ee 100644 --- a/scm/define-grobs.scm +++ b/scm/define-grobs.scm @@ -377,7 +377,6 @@ ly:beam::slope-damping ly:beam::shift-region-to-valid ly:beam::quanting - ly:beam::move-to-avoid-collisions )))) ;; this is a hack to set stem lengths, if positions is set.