# [Lilypond-auto] [LilyIssues-auto] [testlilyissues:issues] #1493 Problem with horizontal beams

```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.```
```

# 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

---

** [issues:#1493] Problem with horizontal beams**

**Status:** Started
**Created:** Wed Jan 26, 2011 12:21 PM UTC by Anonymous
**Last Updated:** Mon Jan 15, 2018 04:36 PM UTC
**Owner:** Torsten Hämmerle
**Attachments:**

-
[beams.ly](https://sourceforge.net/p/testlilyissues/issues/1493/attachment/beams.ly)
(807 Bytes; application/octet-stream)
-
[screenshot.png](https://sourceforge.net/p/testlilyissues/issues/1493/attachment/screenshot.png)
(18.9 kB; image/png)

*Originally created by:* *anonymous

*Originally created by:*

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"?

---

Sent from sourceforge.net because testlilyissues-a...@lists.sourceforge.net is
subscribed to https://sourceforge.net/p/testlilyissues/issues/

To unsubscribe from further messages, a project admin can change settings at
a mailing list, you can unsubscribe from the mailing list.```
```------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
```_______________________________________________
Testlilyissues-auto mailing list
testlilyissues-a...@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/testlilyissues-auto
```
• [Lilypond-... Auto mailings of changes to Lily Issues via Testlilyissues-auto
• [Lily... Auto mailings of changes to Lily Issues via Testlilyissues-auto
• [Lily... Auto mailings of changes to Lily Issues via Testlilyissues-auto
• [Lily... Auto mailings of changes to Lily Issues via Testlilyissues-auto
• [Lily... Auto mailings of changes to Lily Issues via Testlilyissues-auto
• [Lily... Auto mailings of changes to Lily Issues via Testlilyissues-auto
• [Lily... Auto mailings of changes to Lily Issues via Testlilyissues-auto
• [Lily... Auto mailings of changes to Lily Issues via Testlilyissues-auto
• [Lily... Auto mailings of changes to Lily Issues via Testlilyissues-auto
• [Lily... Auto mailings of changes to Lily Issues via Testlilyissues-auto