X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=lily%2Fbeam.cc;h=1bb0a209a859666c271dfed1c955d556723ecfa0;hb=c1c919567da299663c9d92e919e682df4be95602;hp=53fda8174319205db9cf61ebf5151765ea674577;hpb=cccce5cadfae74d26c56ab3f22cb1c7c81093249;p=lilypond.git diff --git a/lily/beam.cc b/lily/beam.cc index 53fda81743..1bb0a209a8 100644 --- a/lily/beam.cc +++ b/lily/beam.cc @@ -37,6 +37,7 @@ #include "beam.hh" +#include "align-interface.hh" #include "beam-scoring-problem.hh" #include "beaming-pattern.hh" #include "directional-element-interface.hh" @@ -554,6 +555,8 @@ Beam::print (SCM grob) Spanner *me = unsmob_spanner (grob); Grob *commonx = 0; vector segments = get_beam_segments (me, &commonx); + if (!segments.size ()) + return SCM_EOL; Interval span; if (normal_stem_count (me)) @@ -590,24 +593,72 @@ Beam::print (SCM grob) Direction feather_dir = to_dir (me->get_property ("grow-direction")); + Interval placements = robust_scm2interval (me->get_property ("normalized-endpoints"), Interval (0.0, 0.0)); + Stencil the_beam; + + int extreme = (segments[0].vertical_count_ == 0 + ? segments[0].vertical_count_ + : segments.back ().vertical_count_); + for (vsize i = 0; i < segments.size (); i ++) { Real local_slope = slope; + /* + Makes local slope proportional to the ratio of the length of this beam + to the total length. + */ if (feather_dir) - { - local_slope += feather_dir * segments[i].vertical_count_ * beam_dy / span.length (); - } + local_slope += (feather_dir * segments[i].vertical_count_ + * beam_dy + * placements.length () + / span.length ()); Stencil b = Lookup::beam (local_slope, segments[i].horizontal_.length (), beam_thickness, blot); b.translate_axis (segments[i].horizontal_[LEFT], X_AXIS); + Real multiplier = feather_dir ? placements[LEFT] : 1.0; + + Interval weights (1 - multiplier, multiplier); + + if (feather_dir != LEFT) + weights.swap (); + + // we need two translations: the normal one and + // the one of the lowest segment + int idx[] = {i, extreme}; + Real translations[2]; + + for (int j = 0; j < 2; j++) + translations[j] = slope + * (segments[idx[j]].horizontal_[LEFT] - span.linear_combination (CENTER)) + + pos.linear_combination (CENTER) + + beam_dy * segments[idx[j]].vertical_count_; + + Real weighted_average = translations[0] * weights[LEFT] + translations[1] * weights[RIGHT]; + + /* + Tricky. The manipulation of the variable `weighted_average' below ensures + that beams with a RIGHT grow direction will start from the position of the + lowest segment at 0, and this error will decrease and decrease over the + course of the beam. Something with a LEFT grow direction, on the other + hand, will always start in the correct place but progressively accrue + error at broken places. This code shifts beams up given where they are + in the total span length (controlled by the variable `multiplier'). To + better understand what it does, try commenting it out: you'll see that + all of the RIGHT growing beams immediately start too low and get better + over line breaks, whereas all of the LEFT growing beams start just right + and get worse over line breaks. + */ + Real factor = Interval (multiplier, 1 - multiplier).linear_combination (feather_dir); + + if (segments[0].vertical_count_ < 0 && feather_dir) + weighted_average += beam_dy * (segments.size () - 1) * factor; + + b.translate_axis (weighted_average, Y_AXIS); - b.translate_axis (local_slope - * (segments[i].horizontal_[LEFT] - span.linear_combination (feather_dir)) - + pos.linear_combination (feather_dir) - + beam_dy * segments[i].vertical_count_, Y_AXIS); the_beam.add_stencil (b); + } #if (DEBUG_BEAM_SCORING) @@ -626,7 +677,7 @@ Beam::print (SCM grob) properties = scm_cons(scm_acons (ly_symbol2scm ("font-size"), scm_from_int (-5), SCM_EOL), properties); - + Direction stem_dir = stems.size () ? to_dir (stems[0]->get_property ("direction")) : UP; Stencil score = *unsmob_stencil (Text_interface::interpret_markup @@ -925,7 +976,11 @@ Beam::no_visible_stem_positions (Grob *me, Interval default_value) } Direction dir = get_grob_direction (me); - Real y = head_positions[dir] + + if (!dir) + programming_error ("The beam should have a direction by now."); + + Real y = head_positions.linear_combination (dir) * 0.5 * Staff_symbol_referencer::staff_space (me) + dir * get_beam_translation (me) * (multiplicity.length () + 1); @@ -1088,7 +1143,7 @@ Beam::shift_region_to_valid (SCM grob, SCM posns) Real x = s->relative_coordinate (common[X_AXIS], X_AXIS) - x_span[LEFT]; x_posns.push_back (x); } - + Grob *lvs = last_normal_stem (me); x_span[RIGHT] = lvs->relative_coordinate (common[X_AXIS], X_AXIS); @@ -1133,13 +1188,6 @@ Beam::shift_region_to_valid (SCM grob, SCM posns) feasible_left_point.intersect (flp); } - /* - We have two intervals here, one for the up variant (beams goes - over the collision) one for the down. - */ - Drul_array collision_free (feasible_left_point, - feasible_left_point); - vector filtered; /* We only update these for objects that are too large for quanting @@ -1152,11 +1200,18 @@ Beam::shift_region_to_valid (SCM grob, SCM posns) take care of computing the impact those exactly. */ Real min_y_size = 2.0; + + // A list of intervals into which beams may not fall + vector forbidden_intervals; + for (vsize i = 0; i < covered.size(); i++) { if (!covered[i]->is_live()) continue; - + + if (Beam::has_interface (covered[i]) && is_cross_staff (covered[i])) + continue; + Box b; for (Axis a = X_AXIS; a < NO_AXES; incr (a)) b[a] = covered[i]->extent (common[a], a); @@ -1197,7 +1252,7 @@ Beam::shift_region_to_valid (SCM grob, SCM posns) the centerline. */ Direction stemdir = get_grob_direction (head_stem); - b[Y_AXIS][stemdir] = stemdir * infinity_f; + b[Y_AXIS][stemdir] = stemdir * infinity_f; } else { @@ -1215,65 +1270,97 @@ Beam::shift_region_to_valid (SCM grob, SCM posns) Real dy = slope * x; Direction yd = DOWN; + Interval disallowed; do { Real left_y = b[Y_AXIS][yd]; - if (left_y == yd * infinity_f) - { - collision_free[yd].set_empty (); - continue; - } - left_y -= dy; // Translate back to beam as ref point. - left_y -= -me->relative_coordinate (common[Y_AXIS], Y_AXIS); - - Interval allowed; - allowed.set_full (); + left_y -= me->relative_coordinate (common[Y_AXIS], Y_AXIS); - allowed[-yd] = left_y; - collision_free[yd].intersect (allowed); + disallowed[yd] = left_y; } while (flip (&yd) != DOWN); + + forbidden_intervals.push_back (disallowed); } while (flip (&d) != LEFT); } - Grob_array *arr = + Grob_array *arr = Pointer_group_interface::get_grob_array (me, ly_symbol2scm ("covered-grobs")); arr->set_array (filtered); - if (collision_free[DOWN].contains (beam_left_y) - || collision_free[UP].contains (beam_left_y)) + vector_sort (forbidden_intervals, Interval::left_less); + Real epsilon = 1.0e-10; + Interval feasible_beam_placements (beam_left_y, beam_left_y); + + /* + forbidden_intervals contains a vector of intervals in which + the beam cannot start. it iterates through these intervals, + pushing feasible_beam_placements epsilon over or epsilon under a + collision. when this type of change happens, the loop is marked + as "dirty" and re-iterated. + + TODO: figure out a faster ways that this loop can happen via + a better search algorithm and/or OOP. + */ + + bool dirty = false; + do { - // We're good to go. Do nothing. + dirty = false; + for (vsize i = 0; i < forbidden_intervals.size (); i++) + { + Direction d = DOWN; + do + { + if (forbidden_intervals[i][d] == d * infinity_f) + feasible_beam_placements[d] = d * infinity_f; + else if (forbidden_intervals[i].contains (feasible_beam_placements[d])) + { + feasible_beam_placements[d] = d * epsilon + forbidden_intervals[i][d]; + dirty = true; + } + } + while (flip (&d) != DOWN); + } } - else if (!collision_free[DOWN].is_empty () - || !collision_free[UP].is_empty ()) - { - // We have space above or below collisions (or, no collisions at - // all). - Interval best = - (collision_free[DOWN].length () > collision_free[UP].length ()) ? - collision_free[DOWN] : collision_free[UP]; + while (dirty); - beam_left_y = point_in_interval (best, 2.0); + // if the beam placement falls out of the feasible region, we push it + // to infinity so that it can never be a feasible candidate below + Direction d = DOWN; + do + { + if (!feasible_left_point.contains (feasible_beam_placements[d])) + feasible_beam_placements[d] = d*infinity_f; } - else if (!feasible_left_point.is_empty ()) + while (flip (&d) != DOWN); + + if ((feasible_beam_placements[UP] == infinity_f && feasible_beam_placements[DOWN] == -infinity_f) && !feasible_left_point.is_empty ()) { // We are somewhat screwed: we have a collision, but at least // there is a way to satisfy stem length constraints. beam_left_y = point_in_interval (feasible_left_point, 2.0); } + else if (!feasible_left_point.is_empty ()) + { + // Only one of them offers is feasible solution. Pick that one. + if (abs (beam_left_y - feasible_beam_placements[DOWN]) > abs (beam_left_y - feasible_beam_placements[UP])) + beam_left_y = feasible_beam_placements[UP]; + else + beam_left_y = feasible_beam_placements[DOWN]; + } else { // We are completely screwed. - warning (_ ("no viable initial configuration found: may not find good beam slope")); + me->warning (_ ("no viable initial configuration found: may not find good beam slope")); } - + pos = Drul_array (beam_left_y, (beam_left_y + beam_dy)); scale_drul (&pos, 1 / Staff_symbol_referencer::staff_space (me)); @@ -1770,6 +1857,8 @@ ADD_INTERFACE (Beam, "break-overshoot " "clip-edges " "concaveness " + "collision-interfaces " + "collision-voice-only " "covered-grobs " "damping " "details "