From f20551647f4502bf0c7d0dd1fdc1831e3493e24e Mon Sep 17 00:00:00 2001 From: Mike Solomon Date: Wed, 5 Oct 2011 09:56:40 +0200 Subject: [PATCH] Moves all functions related to the `position' property of beam to beam-quanting.cc. This cosmetic fix is the second step towards consistent slopes after the renaming of variables in beam-quanting.cc, allowing reviewers to see the diffs between functions in this file alone. --- lily/beam-quanting.cc | 497 ++++++++++++++++++++++++++++++++++++++++++ lily/beam.cc | 493 ----------------------------------------- 2 files changed, 497 insertions(+), 493 deletions(-) diff --git a/lily/beam-quanting.cc b/lily/beam-quanting.cc index 9b065bbd53..8baf0161b4 100644 --- a/lily/beam-quanting.cc +++ b/lily/beam-quanting.cc @@ -30,11 +30,16 @@ using namespace std; #include "direction.hh" #include "directional-element-interface.hh" #include "grob.hh" +#include "grob-array.hh" +#include "item.hh" #include "international.hh" +#include "least-squares.hh" #include "libc-extension.hh" #include "main.hh" +#include "note-head.hh" #include "output-def.hh" #include "pointer-group-interface.hh" +#include "rhythmic-head.hh" #include "staff-symbol-referencer.hh" #include "stencil.hh" #include "stem.hh" @@ -335,6 +340,498 @@ Beam_scoring_problem::Beam_scoring_problem (Grob *me, Drul_array ys) init_stems (); } +// 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 (); +} + +/* 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)); + } +} + +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); +} + +/* 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); +} + +/* + 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); +} + void Beam_scoring_problem::generate_quants (vector *scores) const { 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) -- 2.39.5