Menu

#1493 Problem with horizontal beams

Fixed
Enhancement
2018-02-17
2011-01-26
Anonymous
No

Originally created by: *anonymous

Originally created by: RalphBug...@gmail.com

James Lowe :
There seems to be an inconsistency with setting horizontal beams.

We have a snippet where we state that

\override Beam #'damping = #+inf.0

Should generate horizontal beams in all cases.

However the simple example attached shows some odd inconsistencies.

   \version "2.13.40"

\relative c''  {
    \override Beam #'damping = #+inf.0
     f16 g a b    a c d g,    a b a c   d e f g, % all beams horizontal
     f16 g a b    a c d g,    a b a c   d e f g, % all beams horizontal
     f16 g a b    a c d g,    a b a c   d e f g, % 2nd and 4th group not Horizontal
     f16 g a b    a c d g,    a b a c   d e f g, % 2nd and 4th group not Horizontal
}

\relative c''  {
    \override Beam #'damping = #+inf.0
     f16 g a b    a c d g,    a b a c   d e f g, \break % 4th group not Horizontal
     f16 g a b    a c d g,    a b a c   d e f g, \break % 2nd and 4th group not Horizontal
     f16 g a b    a c d g,    a b a c   d e f g, \break % 2nd and 4th group not Horizontal
     f16 g a b    a c d g,    a b a c   d e f g, \break % 2nd and 4th group not Horizontal
}

Phil Holmes :

I'd suggest the snippet is worth sorting out, too.  What do you reckon - adding the commands

   \override Beam #'details #'damping-direction-penalty = #0
   \override Beam #'details #'round-to-zero-slope = #0

to the existing snippet, or a new one pointed to by the old one, that says "if  \override Beam #'damping = #+inf.0 doesn't do what you want, add the other commands too, as shown here"?

2 Attachments

Discussion

  • Torsten Hämmerle

    • labels: Frog -->
    • Description has changed:

    Diff:

    
    
    • status: Accepted --> Started
    • assigned_to: Torsten Hämmerle
    • Needs: -->
    • Patch: -->
    • Type: Enhancement --> Defect
     
  • Torsten Hämmerle

    Changed to Type "Defect" because the coding clearly isn't working as intended for extreme values of damping and even (seemingly) random results occur.

    More details to follow (about the LilyPond beam damping algorithm in general and unwanted side-effects in cases of extreme damping in particular).

    Cheerio,
    Torsten

     
  • Torsten Hämmerle

    I've spent a lot of time on thinking about what to write. On the one hand, I didn't want to write screeds and screeds, on the other hand I wanted to supply background information to underpin my favoured solution.

    For those not interested in the details, please skip to "Proposed solutions".

    Numerical precision: rounding problem

    Starting off with a completely undamped beam, damping is implemented by raising one end and lowering the other end of the beam.
    Let dy be the vertical distance between left and right end of the completely undamped beam. With infinite damping, LilyPond will raise the lower end by dy/2 and raise the lower end by dy/2. Theoretically, this will lead to two end points with the exact same y value, i.e. a horizontal beam.

    Problem: due to limited numerical precision there may be tiny rounding problems when dividing by 2 and adding.
    Even the slightest vertical distance between the end points, however, will later be quantized (i.e. increased) to valid sit/straddle/hang positions and thus yield noticeable slopes.
    The numeric rounding issue may even overcompensate the slope so that the beam's direction flips.
    That's what happened when we see a raising beam that really should be descending (or, ideally, be horizontal).

    All these calculations are very sensitive to the slightest deviations and highly depend on beam slope, beam span, stave size, line width, indent, etc.

    One solution would be to avoid the precision problem.

    In the following list, DELTA unquanted_y is the remaining vertical distance (should be 0) of both ends of the beam.
    As you can see, even the original dy sign may flip and the numeric "error" is so small I had to use exponential notation:

    original dy = -0.680277, DELTA unquanted_y =  5.5511e-17
    original dy = -0.431074, DELTA unquanted_y =  2.7756e-17
    original dy =  1.301622, DELTA unquanted_y =  0.0000e+00
    original dy = -0.680277, DELTA unquanted_y = -5.5511e-17
    original dy = -0.431074, DELTA unquanted_y =  2.7756e-17
    original dy =  1.301622, DELTA unquanted_y =  0.0000e+00
    original dy =  1.303842, DELTA unquanted_y =  0.0000e+00
    original dy = -0.428570, DELTA unquanted_y = -2.7756e-17
    original dy = -0.679738, DELTA unquanted_y =  5.5511e-17
    original dy =  1.303842, DELTA unquanted_y =  0.0000e+00
    original dy = -0.428570, DELTA unquanted_y =  2.7756e-17
    original dy = -0.679738, DELTA unquanted_y =  5.5511e-17
    

    The sligthest deviation between left and right beam end Y position will be drastically amplified by quanting to valid sit/straddle/hang positions later on.

    After avoiding the rounding problem, there are no unwanted DELTAs left in the case of infinite damping:

    original dy = -0.680277, DELTA unquanted_y =  0.0000e+00
    original dy = -0.431074, DELTA unquanted_y =  0.0000e+00
    original dy =  1.301622, DELTA unquanted_y =  0.0000e+00
    original dy = -0.680277, DELTA unquanted_y =  0.0000e+00
    original dy = -0.431074, DELTA unquanted_y =  0.0000e+00
    original dy =  1.301622, DELTA unquanted_y =  0.0000e+00
    original dy =  1.303842, DELTA unquanted_y =  0.0000e+00
    original dy = -0.428570, DELTA unquanted_y =  0.0000e+00
    original dy = -0.679738, DELTA unquanted_y =  0.0000e+00
    original dy =  1.303842, DELTA unquanted_y =  0.0000e+00
    original dy = -0.428570, DELTA unquanted_y =  0.0000e+00
    original dy = -0.679738, DELTA unquanted_y =  0.0000e+00
    

    Damping mechanism

    Beam damping is needed because "beam angles should not deviate far from the horizontal" (Gould).
    Starting off with the slope of a straight line that links the outer noteheads of the beam, LilyPond applies a rather simple damping formula to the slope:

    slope = 0.6 * tanh (slope) / (damping + concaveness);

    Let's have a look a the numerator first:

    Numerator
    One may wonder about the hyperbolic tangent (tanh), but it turns out to be very suitable as a damping factor:

    • It's symmetric to the origin and can be used both for positive and negative slopes
    • For small |x|, there is practically no damping at all
    • |tanh(x)| will never become greater than 1, however large x may be.
    • For values of |x| > 3, tanh(x) is practically 1.

    Consequently, the numerator of the damping term 0.6 * tanh(slope) an never exceed the value of 0.6 (i.e. an angle of 31°).

    Denominator
    damping has a default value of 1, concaveness is 0 for non-concave beams.
    Concave groups of notes should always have a horizontal beam - independent of damping.So S So that's a special case we'll set aside for the moment.

    By specifying damping values other than 1, we can increase damping (damping > 1) to get flatter beams

    From a purely mathematical point of view, damping and concaveness play a very similar role, because the tanh-damped numerator is divided by the sum of damping and concaveness.

    The idea of achieving horizontal beams by dividing a denominator (that can never becom greater than 0.6) by an infinite damping value is absolutely correct, but there are numeric problems (see below).

    "Rounding up"

    While damping may theoretically lead to a very flat slope, according to the well-known sit/straddle/hang rules even the faintest slope will be "rounded up" to a noticeable slant. That's why we need "infinite" damping to get actual 0 slopes.
    The smallest remaining slope after damping will inevitably be "rounded up" as described.

    Proposed solution (1)

    As concaveness and damping play the exact same role in the damping formula, it suggests itself to treat them in the same way:
    Why not setting the same threshold of 10000 for damping than it has already been done for concaveness?
    That way, a damping value >= 10000 will force the beams to be horizontal completely avoiding numeric calamities:

    -if (concaveness >= 10000)
    +if ((concaveness >= 10000) || (damping >= 10000))
    

    This would be, in my opinion, the preferable and consistent solution.
    A concaveness value >= 10000 will lead to horizontal beaming, and so will a damping value of >= 10000.

    Why should be a concavenss of 10000 be sufficient, but damping has to be inifinte if both play a similar role in the damping formula.

    Full regression test passed.

    Proposed solution (2)

    As we have seen, the odd inconsistencies are a side effect of numeric rounding inaccuracies that can be avoided by avoiding the division by 2.

    Original coding:

          unquanted_y_[LEFT] += (dy - damped_dy) / 2;
          unquanted_y_[RIGHT] -= (dy - damped_dy) / 2;
    

    Easiest solution:

          unquanted_y_[LEFT] += dy - damped_dy);
    

    That way, left end will be changed by the full amount, thus avoiding the malicious division while leaving the right end untouched.
    The absolute Y positions are not important, they will be adjusted anyway later on.

    Safest solution

          unquanted_y_[LEFT] = unquanted_y_[LEFT] + (dy - damped_dy) - ((dy - damped_dy) / 2);
          unquanted_y_[RIGHT] -= (dy - damped_dy) / 2;
    

    Sacrificing the += abbreviation is essential to avoid cumulation of rounding errors here.
    LEFT and RIGHT unquanted_y values now are identical to the original coding, apart from the slight rounding problem 17 places after the decimal point.

    Full regression test passed for both variants.

    Solution (1) or (2)?

    I'm well aware of the fact that conding discussions should take place in rietveld, but this time it's about deciding between two approaches.

    As soon as we've agreed on one of the possible solutions, I'll upload a patch.
    What do you think?
    I'll favour the threshold solution (1)

    Thanks
    Torsten

     
  • Werner LEMBERG

    Werner LEMBERG - 2018-02-08

    Because you asked: I introduced tanh many, many years ago based on analyzing stem damping data in Helene Wanske's book... This function fit best :-)

    I favour solution (1). This looks very TeX-like, and I trust Knuth that he had good reasons to design similar bounds for solving paragraph layout and the like.

     
  • Torsten Hämmerle

    issue #1493: Problem with horizontal beams

    file: lily/beam-quanting.cc

    In spite of infinite damping, due to numeric precision problems
    (rounding/adding) the resulting beams arbitrarily still had a
    remaining non-zero slope, sometimes even in the "wrong" direction.

    In good TeX manner and in line with concaveness (already implemented),
    we'll use a threshold value of 10000 for forcing horizontal beams.

    http://codereview.appspot.com/337560043

     
  • Anonymous

    Anonymous - 2018-02-10
    • Needs: -->
    • Patch: new --> review
    • Type: --> Enhancement
     
  • Anonymous

    Anonymous - 2018-02-10

    Passes make, make check and a full make doc.

     
  • Torsten Hämmerle

    off-topic check for division by 0 added

    http://codereview.appspot.com/337560043

     
  • Anonymous

    Anonymous - 2018-02-10
    • Needs: -->
    • Patch: new --> review
    • Type: --> Enhancement
     
  • Anonymous

    Anonymous - 2018-02-10

    Passes make, make check and a full make doc.

     
  • Anonymous

    Anonymous - 2018-02-12
    • Patch: review --> countdown
     
  • Anonymous

    Anonymous - 2018-02-12

    Patch on countdown for Feb 15th.

     
  • Anonymous

    Anonymous - 2018-02-15
    • Patch: countdown --> push
     
  • Anonymous

    Anonymous - 2018-02-15

    Patch counted down - please push. Torsten, if you do not have commit access please create a git formatted patch against current master and I can push it for you if you like.

     
    • Torsten Hämmerle

      Hi James,
      Patch file attached - please push it for me.
      Thanks,
      Torsten

       
  • Anonymous

    Anonymous - 2018-02-17
    • labels: --> Fixed_2_21_0
    • status: Started --> Fixed
    • Patch: push -->
     
  • Anonymous

    Anonymous - 2018-02-17
    issue #1493: Problem with horizontal beams staging
    author  Torsten Hämmerle <torsten.haemmerle@web.de> 
        Mon, 5 Feb 2018 20:20:16 +0000 (21:20 +0100)
    committer   James Lowe <pkx166h@gmail.com>  
        Sat, 17 Feb 2018 18:38:49 +0000 (18:38 +0000)
    commit  30a874a29b81dbb174a60d185fc3f28bba85604c
    

    Thank you Torsten.