X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=lily%2Fbeam.cc;h=030aa5d531e1f897a88a35c5c45817e269b05971;hb=f20551647f4502bf0c7d0dd1fdc1831e3493e24e;hp=1ecd71a88ff5a87027c7aa987c67292a5954196d;hpb=f875ef39c544bd3499dae5360e9e24f69933575f;p=lilypond.git diff --git a/lily/beam.cc b/lily/beam.cc index 1ecd71a88f..030aa5d531 100644 --- a/lily/beam.cc +++ b/lily/beam.cc @@ -45,7 +45,6 @@ #include "international.hh" #include "interval-set.hh" #include "item.hh" -#include "least-squares.hh" #include "lookup.hh" #include "main.hh" #include "misc.hh" @@ -884,38 +883,6 @@ Beam::consider_auto_knees (Grob *me) } } -/* Set stem's shorten property if unset. - -TODO: -take some y-position (chord/beam/nearest?) into account -scmify forced-fraction - -This is done in beam because the shorten has to be uniform over the -entire beam. -*/ - -void -set_minimum_dy (Grob *me, Real *dy) -{ - if (*dy) - { - /* - If dy is smaller than the smallest quant, we - get absurd direction-sign penalties. - */ - - 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 sit = (beam_thickness - slt) / 2; - Real inter = 0.5; - Real hang = 1.0 - (beam_thickness - slt) / 2; - - *dy = sign (*dy) * max (fabs (*dy), - min (min (sit, inter), hang)); - } -} - MAKE_SCHEME_CALLBACK (Beam, calc_stem_shorten, 1) SCM Beam::calc_stem_shorten (SCM smob) @@ -951,466 +918,6 @@ Beam::calc_stem_shorten (SCM smob) return scm_from_double (0.0); } -Interval -Beam::no_visible_stem_positions (Grob *me, Interval default_value) -{ - extract_grob_set (me, "stems", stems); - if (stems.empty ()) - return default_value; - - Interval head_positions; - Slice multiplicity; - for (vsize i = 0; i < stems.size (); i++) - { - head_positions.unite (Stem::head_positions (stems[i])); - multiplicity.unite (Stem::beam_multiplicity (stems[i])); - } - - Direction dir = get_grob_direction (me); - - 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); - - y /= Staff_symbol_referencer::staff_space (me); - return Interval (y, y); -} - -/* - Compute a first approximation to the beam slope. -*/ -MAKE_SCHEME_CALLBACK (Beam, calc_least_squares_positions, 2); -SCM -Beam::calc_least_squares_positions (SCM smob, SCM /* posns */) -{ - Grob *me = unsmob_grob (smob); - - int count = normal_stem_count (me); - Interval pos (0, 0); - if (count < 1) - return ly_interval2scm (no_visible_stem_positions (me, pos)); - - vector x_posns; - extract_grob_set (me, "normal-stems", stems); - Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS); - Grob *commony = common_refpoint_of_array (stems, me, Y_AXIS); - - Real my_y = me->relative_coordinate (commony, Y_AXIS); - - Grob *fvs = first_normal_stem (me); - Grob *lvs = last_normal_stem (me); - - Interval ideal (Stem::get_stem_info (fvs).ideal_y_ - + fvs->relative_coordinate (commony, Y_AXIS) - my_y, - Stem::get_stem_info (lvs).ideal_y_ - + lvs->relative_coordinate (commony, Y_AXIS) - my_y); - - Real x0 = first_normal_stem (me)->relative_coordinate (commonx, X_AXIS); - for (vsize i = 0; i < stems.size (); i++) - { - Grob *s = stems[i]; - - Real x = s->relative_coordinate (commonx, X_AXIS) - x0; - x_posns.push_back (x); - } - Real dx = last_normal_stem (me)->relative_coordinate (commonx, X_AXIS) - x0; - - Real y = 0; - Real slope = 0; - Real dy = 0; - Real ldy = 0.0; - if (!ideal.delta ()) - { - Interval chord (Stem::chord_start_y (stems[0]), - Stem::chord_start_y (stems.back ())); - - /* Simple beams (2 stems) on middle line should be allowed to be - slightly sloped. - - However, if both stems reach middle line, - ideal[LEFT] == ideal[RIGHT] and ideal.delta () == 0. - - For that case, we apply artificial slope */ - if (!ideal[LEFT] && chord.delta () && count == 2) - { - /* FIXME. -> UP */ - Direction d = (Direction) (sign (chord.delta ()) * UP); - pos[d] = get_beam_thickness (me) / 2; - pos[-d] = -pos[d]; - } - else - pos = ideal; - - /* - For broken beams this doesn't work well. In this case, the - slope esp. of the first part of a broken beam should predict - where the second part goes. - */ - ldy = pos[RIGHT] - pos[LEFT]; - } - else - { - vector ideals; - for (vsize i = 0; i < stems.size (); i++) - { - Grob *s = stems[i]; - ideals.push_back (Offset (x_posns[i], - Stem::get_stem_info (s).ideal_y_ - + s->relative_coordinate (commony, Y_AXIS) - - my_y)); - } - - minimise_least_squares (&slope, &y, ideals); - - dy = slope * dx; - - set_minimum_dy (me, &dy); - - ldy = dy; - pos = Interval (y, (y + dy)); - } - - /* - "position" is relative to the staff. - */ - scale_drul (&pos, 1 / Staff_symbol_referencer::staff_space (me)); - - me->set_property ("least-squares-dy", scm_from_double (ldy)); - return ly_interval2scm (pos); -} - -// Assuming V is not empty, pick a 'reasonable' point inside V. -static Real -point_in_interval (Interval v, Real dist) -{ - if (isinf (v[DOWN])) - return v[UP] - dist; - else if (isinf (v[UP])) - return v[DOWN] + dist; - else - return v.center (); -} - -/* - We can't combine with previous function, since check concave and - slope damping comes first. - - TODO: we should use the concaveness to control the amount of damping - applied. -*/ -MAKE_SCHEME_CALLBACK (Beam, shift_region_to_valid, 2); -SCM -Beam::shift_region_to_valid (SCM grob, SCM posns) -{ - Grob *me = unsmob_grob (grob); - - /* - Code dup. - */ - vector x_posns; - extract_grob_set (me, "stems", stems); - extract_grob_set (me, "covered-grobs", covered); - - Grob *common[NO_AXES] = { me, me }; - for (Axis a = X_AXIS; a < NO_AXES; incr (a)) - { - common[a] = common_refpoint_of_array (stems, me, a); - common[a] = common_refpoint_of_array (covered, common[a], a); - } - Grob *fvs = first_normal_stem (me); - - if (!fvs) - return posns; - Interval x_span; - x_span[LEFT] = fvs->relative_coordinate (common[X_AXIS], X_AXIS); - for (vsize i = 0; i < stems.size (); i++) - { - Grob *s = stems[i]; - - 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); - - Drul_array pos = ly_scm2interval (posns); - - scale_drul (&pos, Staff_symbol_referencer::staff_space (me)); - - Real beam_dy = pos[RIGHT] - pos[LEFT]; - Real beam_left_y = pos[LEFT]; - Real slope = x_span.delta () ? (beam_dy / x_span.delta ()) : 0.0; - - /* - Shift the positions so that we have a chance of finding good - quants (i.e. no short stem failures.) - */ - Interval feasible_left_point; - feasible_left_point.set_full (); - - for (vsize i = 0; i < stems.size (); i++) - { - Grob *s = stems[i]; - if (Stem::is_invisible (s)) - continue; - - Direction d = get_grob_direction (s); - Real left_y - = Stem::get_stem_info (s).shortest_y_ - - slope * x_posns [i]; - - /* - left_y is now relative to the stem S. We want relative to - ourselves, so translate: - */ - left_y - += + s->relative_coordinate (common[Y_AXIS], Y_AXIS) - - me->relative_coordinate (common[Y_AXIS], Y_AXIS); - - Interval flp; - flp.set_full (); - flp[-d] = left_y; - - feasible_left_point.intersect (flp); - } - - vector filtered; - /* - We only update these for objects that are too large for quanting - to find a workaround. Typically, these are notes with - stems, and timesig/keysig/clef, which take out the entire area - inside the staff as feasible. - - The code below disregards the thickness and multiplicity of the - beam. This should not be a problem, as the beam quanting will - 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); - - if (b[X_AXIS].is_empty () || b[Y_AXIS].is_empty ()) - continue; - - if (intersection (b[X_AXIS], x_span).is_empty ()) - continue; - - filtered.push_back (covered[i]); - Grob *head_stem = Rhythmic_head::get_stem (covered[i]); - if (head_stem && Stem::is_normal_stem (head_stem) - && Note_head::has_interface (covered[i])) - { - if (Stem::get_beam (head_stem)) - { - /* - We must assume that stems are infinitely long in this - case, as asking for the length of the stem typically - leads to circular dependencies. - - This strategy assumes that we don't want to handle the - collision of beams in opposite non-forced directions - with this code, where shortening the stems of both - would resolve the problem, eg. - - x x - | | - ===== - - ===== - | | - x x - - Such beams would need a coordinating grob to resolve - the collision, since both will likely want to occupy - the centerline. - */ - Direction stemdir = get_grob_direction (head_stem); - b[Y_AXIS][stemdir] = stemdir * infinity_f; - } - else - { - // TODO - should we include the extent of the stem here? - } - } - - if (b[Y_AXIS].length () < min_y_size) - continue; - - Direction d = LEFT; - do - { - Real x = b[X_AXIS][d] - x_span[LEFT]; - Real dy = slope * x; - - Direction yd = DOWN; - Interval disallowed; - do - { - Real left_y = b[Y_AXIS][yd]; - - left_y -= dy; - - // Translate back to beam as ref point. - left_y -= me->relative_coordinate (common[Y_AXIS], Y_AXIS); - - disallowed[yd] = left_y; - } - while (flip (&yd) != DOWN); - - forbidden_intervals.push_back (disallowed); - } - while (flip (&d) != LEFT); - } - - Grob_array *arr - = Pointer_group_interface::get_grob_array (me, - ly_symbol2scm ("covered-grobs")); - arr->set_array (filtered); - - 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 - { - 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); - } - } - while (dirty); - - // 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; - } - 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. - 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)); - - return ly_interval2scm (pos); -} - -/* This neat trick is by Werner Lemberg, - damped = tanh (slope) - corresponds with some tables in [Wanske] CHECKME */ -MAKE_SCHEME_CALLBACK (Beam, slope_damping, 2); -SCM -Beam::slope_damping (SCM smob, SCM posns) -{ - Grob *me = unsmob_grob (smob); - Drul_array pos = ly_scm2interval (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); - if (concaveness >= 10000) - { - pos[LEFT] = pos[RIGHT]; - me->set_property ("least-squares-dy", scm_from_double (0)); - damping = 0; - } - - if (damping) - { - scale_drul (&pos, Staff_symbol_referencer::staff_space (me)); - - Real dy = pos[RIGHT] - pos[LEFT]; - - Grob *fvs = first_normal_stem (me); - Grob *lvs = last_normal_stem (me); - - Grob *commonx = fvs->common_refpoint (lvs, X_AXIS); - - Real dx = last_normal_stem (me)->relative_coordinate (commonx, X_AXIS) - - first_normal_stem (me)->relative_coordinate (commonx, X_AXIS); - - Real slope = dy && dx ? dy / dx : 0; - - slope = 0.6 * tanh (slope) / (damping + concaveness); - - Real damped_dy = slope * dx; - - set_minimum_dy (me, &damped_dy); - - pos[LEFT] += (dy - damped_dy) / 2; - pos[RIGHT] -= (dy - damped_dy) / 2; - - scale_drul (&pos, 1 / Staff_symbol_referencer::staff_space (me)); - } - - return ly_interval2scm (pos); -} - MAKE_SCHEME_CALLBACK (Beam, quanting, 2); SCM Beam::quanting (SCM smob, SCM posns)