From c5f3e046a33c78a0505bb30cdb9a2bb938bd7346 Mon Sep 17 00:00:00 2001 From: Han-Wen Nienhuys Date: Sat, 11 Sep 2004 23:46:07 +0000 Subject: [PATCH] new file, with new routines for concave decisions. There are now two types of concaveness: 1. a sharp logic decision forcing a beam horizontal, and 2. a number that measures how concave beams are that are not caught by 1. This fixes: morgenlied.ly and input/regression/beam-concave.ly --- ChangeLog | 9 ++ input/regression/beam-concave.ly | 16 +-- lily/beam-concave.cc | 142 +++++++++++++++++++++++++++ lily/beam-quanting.cc | 16 +-- lily/beam.cc | 163 +++++-------------------------- 5 files changed, 183 insertions(+), 163 deletions(-) create mode 100644 lily/beam-concave.cc diff --git a/ChangeLog b/ChangeLog index 9e885e83df..45be1a4b75 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,14 @@ 2004-09-12 Han-Wen Nienhuys + * input/regression/beam-concave-damped.ly: new file. + + * lily/beam-concave.cc: new file, with new routines for concave + decisions. There are now two types of concaveness: 1. a sharp + logic decision forcing a beam horizontal, and 2. a number that + measures how concave beams are that are not caught by 1. + + This fixes: morgenlied.ly and input/regression/beam-concave.ly + * lily/new-fingering-engraver.cc (stop_translation_timestep): new property: add-stem-support diff --git a/input/regression/beam-concave.ly b/input/regression/beam-concave.ly index 38d7ef0aa4..ed1db5b553 100644 --- a/input/regression/beam-concave.ly +++ b/input/regression/beam-concave.ly @@ -2,13 +2,14 @@ \version "2.3.4" \header{ -texidoc = "Concave beams should be horizontal. Informally spoken, +texidoc = "Fully concave beams should be horizontal. Informally spoken, concave refers to the shape of the notes that are opposite a beam. If an up-beam has high notes on its center stems, then we call - it concave. If a beam is fails a test, the desired slope is printed - next to it." + it concave. + If a beam is fails a test, the desired slope is printed +next to it." } @@ -25,8 +26,7 @@ rossFourBeams =\relative c'' { a[ d, e g] a[ f f g] c,[ b f' e] b[ e g, e'] - g[ d a' b] - c[ c, c c] + c'[ c, c c] c[ c c c'] f,,[ b a g] f[g g e] @@ -49,12 +49,6 @@ rossBeams = \relative c'' { \rossThreeBeams } -nonHorizBeams = \relative c'' { - \time 3/4 - f[ e d c g b] - b,16[ f' g a] - \stemUp b,8[ \stemDown d'8 bes8] -} diff --git a/lily/beam-concave.cc b/lily/beam-concave.cc new file mode 100644 index 0000000000..ac2d3a758e --- /dev/null +++ b/lily/beam-concave.cc @@ -0,0 +1,142 @@ +/* + Determine whether a beam is concave. + */ +#include + + +#include "group-interface.hh" +#include "array.hh" +#include "grob.hh" +#include "stem.hh" +#include "interval.hh" +#include "beam.hh" +#include "staff-symbol-referencer.hh" + +bool +is_concave_single_notes (Array positions, Direction beam_dir) +{ + Interval covering; + covering.add_point (positions[0]); + covering.add_point (positions.top ()); + + bool above = false; + bool below = false; + bool concave = false; + + /* + notes above and below the interval covered by 1st and last note. + */ + for (int i = 1; i < positions.size () - 1; i++) + { + above = above || (positions[i] > covering[UP]); + below = below || (positions[i] < covering[DOWN]); + } + + + concave = concave || (above && below); + /* + A note as close or closer to the beam than begin and end, but the + note is reached in the opposite direction as the last-first dy + */ + int dy = positions.top() - positions[0]; + int closest = (beam_dir * positions.top()) >? (beam_dir *positions[0]); + for (int i = 2; !concave && i < positions.size () - 1; i++) + { + int inner_dy = positions[i] - positions[i-1]; + if (sign (inner_dy) != sign (dy) + && (beam_dir * positions[i] >= closest + || beam_dir * positions[i-1] >= closest)) + concave = true; + } + + bool all_closer = true; + for (int i = 1; all_closer && i < positions.size ()-1; i++) + { + all_closer = all_closer && + (beam_dir * positions[i] > closest); + } + + concave = concave || all_closer; + return concave; +} + + +MAKE_SCHEME_CALLBACK (Beam, check_concave, 1); +SCM +Beam::check_concave (SCM smob) +{ + Grob *me = unsmob_grob (smob); + + Link_array stems = + Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems"); + + if (is_knee (me)) + return SCM_UNSPECIFIED; + + Direction beam_dir = CENTER; + for (int i = stems.size (); i--; ) + { + if (Stem::is_invisible (stems[i])) + stems.del (i); + else + { + if (Direction dir = Stem::get_direction (stems[i])) + beam_dir = dir; + } + } + + if (stems.size () <= 2) + return SCM_UNSPECIFIED; + + + Array positions; + for (int i= 0; i < stems.size (); i++) + { + /* + For chords, we take the note head that is closest to the beam. + + Hmmm.. wait, for the beams in the last measure of morgenlied, + this doesn't look so good. Let's try the heads farthest from + the beam. + + */ + Real pos = Stem::head_positions (stems[i])[-beam_dir]; + + positions.push ((int) rint (pos)); + } + + if (is_concave_single_notes (positions, beam_dir)) + { + Drul_array pos = ly_scm2interval (me->get_property ("positions")); + Real r = linear_combination (pos, 0.0); + + r /= Staff_symbol_referencer::staff_space (me); + me->set_property ("positions", ly_interval2scm (Drul_array (r, r))); + me->set_property ("least-squares-dy", scm_make_real (0)); + } + else + { + Real dy = positions.top () - positions[0]; + Real slope = dy / Real (positions.size() - 1); + Real concaveness = 0.0; + for (int i = 1; i < positions.size() - 1; i++) + { + Real line_y = slope * i + positions[0]; + + concaveness += (beam_dir * (positions[i] - line_y)) >? 0.0; + } + + concaveness /= positions.size () ; + + /* + Normalize. For dy = 0, the slopes ends up as 0 anyway, so + the scaling of concaveness doesn't matter much. + */ + if (dy) + concaveness /= dy; + + me->set_property ("concaveness", scm_from_double (concaveness)); + } + + return SCM_UNSPECIFIED; +} diff --git a/lily/beam-quanting.cc b/lily/beam-quanting.cc index 5dd9da6e75..6a8721fdca 100644 --- a/lily/beam-quanting.cc +++ b/lily/beam-quanting.cc @@ -38,7 +38,6 @@ const int DAMPING_DIRECTION_PENALTY = 800; const int MUSICAL_DIRECTION_FACTOR = 400; const int IDEAL_SLOPE_FACTOR = 10; const Real ROUND_TO_ZERO_SLOPE = 0.02; -const int ROUND_TO_ZERO_POINTS = 4; static Real shrink_extra_weight (Real x, Real fac) @@ -408,23 +407,10 @@ Beam::score_slopes_dy (Real yl, Real yr, dem += shrink_extra_weight (fabs (dy_damp) - fabs (dy), 1.5) * slope_penalty; -#if 0 - /* - almost zero slopes look like errors in horizontal beams. - */ - /* - This causes too much problems, because horizontal depends on - horizontal spacing details. These errors should be dealt with - through concaveness. --hwn. - */ - if (fabs (dy) > 1e-3 - && fabs (dy / dx) < ROUND_TO_ZERO_SLOPE) - dem += ROUND_TO_ZERO_POINTS; -#endif - return dem; } + static Real my_modf (Real x) { diff --git a/lily/beam.cc b/lily/beam.cc index 65e1aafe38..d8648dc26c 100644 --- a/lily/beam.cc +++ b/lily/beam.cc @@ -340,7 +340,7 @@ Beam::print (SCM grob) scale_drul (&pos, Staff_symbol_referencer::staff_space (me)); Real dy = pos[RIGHT] - pos[LEFT]; - Real dydx = (dy && dx) ? dy/dx : 0; + Real slope = (dy && dx) ? dy/dx : 0; Real thick = get_thickness (me); Real bdy = get_beam_translation (me); @@ -415,14 +415,14 @@ Beam::print (SCM grob) Real blot = me->get_paper ()->get_dimension (ly_symbol2scm ("blotdiameter")); - Stencil whole = Lookup::beam (dydx, w, thick, blot); + Stencil whole = Lookup::beam (slope, w, thick, blot); Stencil gapped; int gap_count = 0; if (scm_is_number (me->get_property ("gap-count"))) { gap_count = scm_to_int (me->get_property ("gap-count")); - gapped = Lookup::beam (dydx, w - 2 * gap_length, thick, blot); + gapped = Lookup::beam (slope, w - 2 * gap_length, thick, blot); full_beams.sort (default_compare); if (stem_dir == UP) @@ -440,7 +440,7 @@ Beam::print (SCM grob) b.translate_axis (gap_length, X_AXIS); } b.translate_axis (last_xposn - x0 + stem_offset, X_AXIS); - b.translate_axis (dydx * (last_xposn - x0) + bdy * full_beams[j], Y_AXIS); + b.translate_axis (slope * (last_xposn - x0) + bdy * full_beams[j], Y_AXIS); the_beam.add_stencil (b); } @@ -479,20 +479,20 @@ Beam::print (SCM grob) lw = me->get_bound (RIGHT)->relative_coordinate (xcommon, X_AXIS) - last_xposn; - Stencil rhalf = Lookup::beam (dydx, rw, thick, blot); - Stencil lhalf = Lookup::beam (dydx, lw, thick, blot); + Stencil rhalf = Lookup::beam (slope, rw, thick, blot); + Stencil lhalf = Lookup::beam (slope, lw, thick, blot); for (int j = lfliebertjes.size (); j--;) { Stencil b (lhalf); b.translate_axis (last_xposn - x0, X_AXIS); - b.translate_axis (dydx * (last_xposn-x0) + bdy * lfliebertjes[j], Y_AXIS); + b.translate_axis (slope * (last_xposn-x0) + bdy * lfliebertjes[j], Y_AXIS); the_beam.add_stencil (b); } for (int j = rfliebertjes.size (); j--;) { Stencil b (rhalf); b.translate_axis (xposn - x0 - rw , X_AXIS); - b.translate_axis (dydx * (xposn-x0 -rw) + bdy * rfliebertjes[j], Y_AXIS); + b.translate_axis (slope * (xposn-x0 -rw) + bdy * rfliebertjes[j], Y_AXIS); the_beam.add_stencil (b); } } @@ -883,7 +883,7 @@ Beam::least_squares (SCM smob) Real y =0; - Real dydx = 0; + Real slope = 0; Real dy = 0; if (!ideal.delta ()) @@ -932,9 +932,9 @@ Beam::least_squares (SCM smob) - my_y)); } - minimise_least_squares (&dydx, &y, ideals); + minimise_least_squares (&slope, &y, ideals); - dy = dydx * dx; + dy = slope * dx; me->set_property ("least-squares-dy", scm_make_real (dy)); pos = Interval (y, (y+dy)); } @@ -998,7 +998,7 @@ Beam::shift_region_to_valid (SCM grob) Real dy = pos[RIGHT] - pos[LEFT]; Real y = pos[LEFT]; - Real dydx =dy/dx; + Real slope =dy/dx; /* @@ -1017,7 +1017,7 @@ Beam::shift_region_to_valid (SCM grob) Real left_y = Stem::get_stem_info (s).shortest_y_ - - dydx * x_posns [i]; + - slope * x_posns [i]; /* left_y is now relative to the stem S. We want relative to @@ -1053,121 +1053,6 @@ Beam::shift_region_to_valid (SCM grob) return SCM_UNSPECIFIED; } -MAKE_SCHEME_CALLBACK (Beam, check_concave, 1); -SCM -Beam::check_concave (SCM smob) -{ - Grob *me = unsmob_grob (smob); - - Link_array stems = - Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems"); - - Direction beam_dir = CENTER; - for (int i = 0; i < stems.size ();) - { - if (Stem::is_invisible (stems[i])) - stems.del (i); - else - { - if (Direction sd = Stem::get_direction (stems[i])) - { - /* - Don't do knee beams. - */ - if (beam_dir && sd && sd != beam_dir) - return SCM_UNSPECIFIED; - - beam_dir = sd; - } - i++; - } - } - - if (stems.size () < 3) - return SCM_UNSPECIFIED; - - - /* Concaveness #1: If distance of an inner notehead to line between - two outer noteheads is bigger than CONCAVENESS-GAP (2.0ss), - beam is concave (Heinz Stolba). - - In the case of knees, the line connecting outer heads is often - not related to the beam slope (it may even go in the other - direction). Skip the check when the outer stems point in - different directions. --hwn - - */ - bool is_concave1 = false; - SCM gap = me->get_property ("concaveness-gap"); - if (scm_is_number (gap)) - { - Real r1 = scm_to_double (gap); - Real dy = Stem::chord_start_y (stems.top ()) - - Stem::chord_start_y (stems[0]); - - - Real slope = dy / (stems.size () - 1); - - Real y0 = Stem::chord_start_y (stems[0]); - for (int i = 1; i < stems.size () - 1; i++) - { - Real c = - beam_dir *((Stem::chord_start_y (stems[i]) - y0) - i * slope); - if (c - r1 > 0) - { - is_concave1 = true; - break; - } - } - } - - - /* Concaveness #2: Sum distances of inner noteheads that fall - outside the interval of the two outer noteheads. - - We only do this for beams where first and last stem have the same - direction. --hwn. - - - Note that "convex" stems compensate for "concave" stems. - (is that intentional?) --hwn. - */ - - Real concaveness2 = 0; - SCM thresh = me->get_property ("concaveness-threshold"); - Real r2 = infinity_f; - if (!is_concave1 && scm_is_number (thresh)) - { - r2 = scm_to_double (thresh); - - Interval iv; - iv.add_point (Stem::chord_start_y (stems[0])); - iv.add_point (Stem::chord_start_y (stems.top ())); - - for (int i = 1; i < stems.size () - 1; i++) - { - Real f = Stem::chord_start_y (stems[i]); - concaveness2 += ( (f - iv[MAX] ) >? 0) + - ( (f - iv[MIN] ) plain horizontal */ - if (is_concave1 || concaveness2 > r2) - { - Drul_array pos = ly_scm2interval (me->get_property ("positions")); - Real r = linear_combination (pos, 0.0); - - r /= Staff_symbol_referencer::staff_space (me); - me->set_property ("positions", ly_interval2scm (Drul_array (r, r))); - me->set_property ("least-squares-dy", scm_make_real (0)); - } - - return SCM_UNSPECIFIED; -} - /* This neat trick is by Werner Lemberg, damped = tanh (slope) corresponds with some tables in [Wanske] CHECKME */ @@ -1181,11 +1066,11 @@ Beam::slope_damping (SCM smob) return SCM_UNSPECIFIED; SCM s = me->get_property ("damping"); - int damping = scm_to_int (s); + Real damping = scm_to_double (s); if (damping) { - Drul_array pos = ly_scm2interval (me->get_property ("positions")); + Drul_array pos = ly_scm2interval (me->get_property ("positions")); scale_drul (&pos, Staff_symbol_referencer::staff_space (me)); Real dy = pos[RIGHT] - pos[LEFT]; @@ -1198,10 +1083,14 @@ Beam::slope_damping (SCM smob) Real dx = last_visible_stem (me)->relative_coordinate (commonx, X_AXIS) - first_visible_stem (me)->relative_coordinate (commonx, X_AXIS); - Real dydx = dy && dx ? dy/dx : 0; - dydx = 0.6 * tanh (dydx) / damping; - Real damped_dy = dydx * dx; + Real slope = dy && dx ? dy/dx : 0; + + Real concaveness = robust_scm2double (me->get_property ("concaveness"), 0.0); + + slope = 0.6 * tanh (slope) / (damping + concaveness); + + Real damped_dy = slope * dx; pos[LEFT] += (dy - damped_dy) / 2; pos[RIGHT] -= (dy - damped_dy) / 2; @@ -1482,10 +1371,10 @@ Beam::rest_collision_callback (SCM element_smob, SCM axis) // ugh -> use commonx Real x0 = first_visible_stem (beam)->relative_coordinate (0, X_AXIS); Real dx = last_visible_stem (beam)->relative_coordinate (0, X_AXIS) - x0; - Real dydx = dy && dx ? dy/dx : 0; + Real slope = dy && dx ? dy/dx : 0; Direction d = Stem::get_direction (stem); - Real stem_y = pos[LEFT] + (stem->relative_coordinate (0, X_AXIS) - x0) * dydx; + Real stem_y = pos[LEFT] + (stem->relative_coordinate (0, X_AXIS) - x0) * slope; Real beam_translation = get_beam_translation (beam); Real beam_thickness = Beam::get_thickness (beam); @@ -1569,8 +1458,8 @@ ADD_INTERFACE (Beam, "beam-interface", "The @code{thickness} property is the weight of beams, and is measured " "in staffspace" , - "knee positioning-done position-callbacks concaveness-gap " - "concaveness-threshold dir-function quant-score auto-knee-gap gap " + "knee positioning-done position-callbacks " + "concaveness dir-function quant-score auto-knee-gap gap " "gap-count chord-tremolo beamed-stem-shorten shorten least-squares-dy " "damping inspect-quants flag-width-function neutral-direction positions space-function " "thickness"); -- 2.39.2