\version "2.18" \include "shapeII-definition.ily" \paper { ragged-right = ##t indent = 0 top-markup-spacing #'basic-distance = #5 } \header { title = "Shaping slurs: The penalty approach" author = "Valentin Petzel" } \markup \vspace #1 \markup {\italic "All examples and snippets are taken from this post:"} \markup { \typewriter "https://lists.gnu.org/archive/html/lilypond-user/2013-11/msg00832.html" } \markup \vspace #1 \markup { \column { \justify { A slur is, from a mathematical point of view naught but a planar curve, that is, a function from an Interval into the plane, i.e. some function γ(t)=(x(t), y(t)), where the parameter t comes from an interval. By reparametrisation we can assume this interval to be [0, 1]. Now, a computer cannot handle arbitrary curves just like that, that’s why we use very specific functions for the coordinates x and y, that is to say, polynomials of limited degree n (usually n = 3 for slurs). } \vspace #0.5 \justify { A polynomial of degree n can be expressed as a combination of 1=X^0, X^1, ..., X^n. The problem with using this so called basis is that one can hardly predict the outcome of this combination a0 + a1·X^1 + ... + an·X^n by just looking at the coefficients a0, ..., an. } \vspace #0.5 \justify { This is the reason we use so called \italic "Bézier curves" instead. This means that we use a different basis for our polynomials. Instead of monoms 1, X^1, ..., X^n one uses the so called \italic "Bernstein polynomials" of degree n. These are n+1 polynomials that form a basis, so we can express any polynomial of degree n as a combination of these basis polynomials. } \vspace #0.5 \justify { The Bernstein polynomials B0, ..., Bn have interesting properties (as can be see on this image https://commons.wikimedia.org/wiki/File:Bernstein_Polynomials.svg) on the Interval [0, 1]: The outer polynomials B0 and Bn are 1 on one side and 0 on the other, while the other polynomials B1, ..., B[n-1] are 0 on both edges and have their mass concentrated on different parts of the interval. This means, that B0 and B1 are directly specifying the starting and the ending value of the polynomial, while the other polynomials will specify the extent on different parts of the interval. } \vspace #0.5 \justify { If γ(t)=(x(t), y(t)), and both x and y are polynomials of degree n, that are parametrised as a combination of the Bernstein polynomials of degree b, this gives us an interesting geometric property: } \vspace #0.5 \justify { If y = y0·B0 + ... + yn·Bn and x = x0·B0 + ... + xn·Bn, then the so called control points (x0, y0), ..., (xn, yn) form a polygon, that somehow outlines the curve γ. This makes drawing Bézier curves on graphical systems very intuitive (and it’s basically what you get, when drawing slurs in graphical notation programs). } \vspace #0.8 \justify { What Lilypond attempts to do is to find these control points automatically for an optimal slur. This is done in a typical approach: For a given curve, we calculate penalty points for everything that is bad about this curve, and thus get a general badness value of it. Then we try to find a curve that somewhat minimizes this badness. This is an optimisation problem, that usually takes some effort to solve, therefore we need an algorithm, that yields a sufficiently good solution in acceptable time. And we need a good choice of penalties, so that the calculated badness somehow reflects the actual badness of the slur. And this is hard, and depends strongly on the situation. } \vspace #0.8 \justify { Lilypond uses parametrised penalty functions with a set of standard parameters that work in the most common situations, but might mess up in less common situations. See here an example by Janek Warchoł and David Nalesnik taken from aforementioned post: } \vspace #0.8 } } SUp = \change Staff = "up" SDn = \change Staff = "down" \new PianoStaff << \new Staff = up \relative d { \clef G \key e \major \time 3/16 \voiceTwo \slurUp \SDn \times 2/3 { b32( g' b } \SUp \times 2/3 { d g e' } \times 2/3 { d b g') } | \SDn \times 2/3 { b,,,32( g' b } \SUp \times 2/3 { dis g e' } \times 2/3 { d b g') } | \newSpacingSection \override Score.SpacingSpanner #'common-shortest-duration = #(ly:make-moment 1 70) \SDn \times 2/3 { b,,,32( g' b } \SUp \times 2/3 { dis g e' } \times 2/3 { d b g') } } \new Staff = down { \clef F \key e \major \time 3/16 s16*9 } >> \markup \column { \justify { As you can see, in this example the slurs minimizing the calculated badness are not in fact good solutions from the point of visual badness. The standard approach to solving these problems is manually overriding the slurs control points. Thus we might get a better slur, but we loose flexibility and might not be able to acheive an optimal slur. } } \pageBreak \markup \column { \justify { Thus, Janek Warchoł and David Nalesnik created a command, that changes control points basically by offsetting them, retaining some aspects of automatic placement, potentially gaining robustness to layout changes, while also providing heavily increased control over the slur appearance. Here you can see their manually corrected version of the example score: } \vspace #0.5 } \new PianoStaff << \new Staff = up \relative d { \clef G \key e \major \time 3/16 \voiceTwo \slurUp \shapeII #'((h)(p 55 0.5)(p 50 0.2)(h 0 1.5)) Slur \SDn \times 2/3 { b32( g' b } \SUp \times 2/3 { d g e' } \times 2/3 { d b g') } | \SDn \times 2/3 { b,,,32( g' b } \SUp \times 2/3 { dis g e' } \times 2/3 { d b g') } | \newSpacingSection \override Score.SpacingSpanner #'common-shortest-duration = #(ly:make-moment 1 70) \SDn \times 2/3 { b,,,32( g' b } \SUp \times 2/3 { dis g e' } \times 2/3 { d b g') } } \new Staff = down { \clef F \key e \major \time 3/16 s16*9 } >> \markup \column { \justify { This slur placement is obviously much better than the automatic placement, but it would require some additional work, to refine these rough placement into an optimal shape. } \vspace #0.9 \justify { I want to explain a totally different, but potentially more intuitive way of improving the output. Instead of overriding the control points themselves, one can override the parameter to the penalty functionals, so we can taylor Lilypond’s sense of badness to the situation. Lilypond does offer access to these parameter as part of the \typewriter Slur.detail property. The most important ones probably are: } \vspace #0.5 \justify {\typewriter head-encompass-penalty (1000) The penalty for a slur colliding with a note head} \vspace #0.3 \justify {\typewriter stem-encompass-penalty (30) Penalty for a slur colliding with a stem} \vspace #0.3 \justify {\typewriter edge-attraction-factor (4) Penalty for distance between start and end points to attachment points} \vspace #0.3 \justify {\typewriter max-slope (1.1) Specifies the maximal slope allowed} \vspace #0.3 \justify {\typewriter max-slope-factor (10) Multiplier for the penalty applied if the slope is larger than \typewriter max-slope} \vspace #0.3 \justify {\typewriter free-head-distance (0.3) The minimal distance between heads and the slur} \vspace #0.3 \justify {\typewriter free-slur-distance (0.8) The minimal distance between adjacent slurs} \vspace #0.3 \justify {\typewriter gap-to-staffline-inside (0.2) The minimal distance between the extremal point of the slur (where slur and stafflines are parallel) and stafflines inside the curve} \vspace #0.3 \justify {\typewriter gap-to-staffline-outside (0.1) The same for stafflines outside the curve} \vspace #0.3 \justify {\typewriter extra-object-collision-penalty (50) penalty for collision with extra objects like articulation marks} \vspace #0.3 \justify {\typewriter accidental-collision (3) Factor for penalty for collisions with accidentals} \vspace #0.3 \justify {\typewriter extra-encompass-free-distance (0.3) minimal distance to all kinds of stuff around the slur} \vspace #0.3 \justify {\typewriter head-slur-distance-max-ratio (3) The maximal allowed ratio between distances between heads and slur (trying to have slur somewhat equally close to all heads)} \vspace #0.3 \justify {\typewriter head-slur-distance-factor (10) Factor for penalty if the ratio is to high} \vspace #0.3 \justify {\typewriter absolute-closeness-measure (0.3) penalty for high total distance to heads} \vspace #0.3 \justify {\typewriter edge-slope-exponent (1.7) factor for penalty for big slopes near the edge points} \vspace #0.3 \vspace #0.5 } \markup {For a complete list see:}\markup \typewriter "http://lilypond.org/doc/v2.19/Documentation/internals/slur_002dinterface" \markup\vspace #0.5 \markup "Now, let’s apply this to the example:" \new PianoStaff << \new Staff = up \relative d { \clef G \key e \major \time 3/16 \voiceTwo \slurUp \SDn \times 2/3 { b32( g' b } \SUp \times 2/3 { d g e' } \times 2/3 { d b g') } | \SDn \times 2/3 { b,,,32( g' b } \SUp \times 2/3 { dis g e' } \times 2/3 { d b g') } | \newSpacingSection \override Score.SpacingSpanner #'common-shortest-duration = #(ly:make-moment 1 70) \SDn \times 2/3 { b,,,32( g' b } \SUp \times 2/3 { dis g e' } \times 2/3 { d b g') } } \new Staff = down { \clef F \key e \major \time 3/16 s16*9 } >> \pageBreak \markup\justify {The most obvious problem is, that the distance between the edge points and the slur is too small. So let’s increase the edge-attraction-factor:} \markup \typewriter "\override Slur.details.edge-attraction-factor = #6" \new PianoStaff << \new Staff = up \relative d { \override Slur.details.edge-attraction-factor = #6 \clef G \key e \major \time 3/16 \voiceTwo \slurUp \SDn \times 2/3 { b32( g' b } \SUp \times 2/3 { d g e' } \times 2/3 { d b g') } | \SDn \times 2/3 { b,,,32( g' b } \SUp \times 2/3 { dis g e' } \times 2/3 { d b g') } | \newSpacingSection \override Score.SpacingSpanner #'common-shortest-duration = #(ly:make-moment 1 70) \SDn \times 2/3 { b,,,32( g' b } \SUp \times 2/3 { dis g e' } \times 2/3 { d b g') } } \new Staff = down { \clef F \key e \major \time 3/16 s16*9 } >> \markup \justify {For reasons of legibility we might want to increase the distance to the note heads:} \markup \typewriter "\override Slur.details.free-head-distance = #1.2" \new PianoStaff << \new Staff = up \relative d { \override Slur.details.edge-attraction-factor = #6 \override Slur.details.free-head-distance = #1.2 \clef G \key e \major \time 3/16 \voiceTwo \slurUp \SDn \times 2/3 { b32( g' b } \SUp \times 2/3 { d g e' } \times 2/3 { d b g') } | \SDn \times 2/3 { b,,,32( g' b } \SUp \times 2/3 { dis g e' } \times 2/3 { d b g') } | \newSpacingSection \override Score.SpacingSpanner #'common-shortest-duration = #(ly:make-moment 1 70) \SDn \times 2/3 { b,,,32( g' b } \SUp \times 2/3 { dis g e' } \times 2/3 { d b g') } } \new Staff = down { \clef F \key e \major \time 3/16 s16*9 } >> \markup\justify { As you can see, we managed to get beautyful slurs, just by intuitively overriding two simple properties. } \pageBreak \markup "For comparisation:" \markup "Unmodified:" \new PianoStaff << \new Staff = up \relative d { \clef G \key e \major \time 3/16 \voiceTwo \slurUp \SDn \times 2/3 { b32( g' b } \SUp \times 2/3 { d g e' } \times 2/3 { d b g') } | \SDn \times 2/3 { b,,,32( g' b } \SUp \times 2/3 { dis g e' } \times 2/3 { d b g') } | \newSpacingSection \override Score.SpacingSpanner #'common-shortest-duration = #(ly:make-moment 1 70) \SDn \times 2/3 { b,,,32( g' b } \SUp \times 2/3 { dis g e' } \times 2/3 { d b g') } } \new Staff = down { \clef F \key e \major \time 3/16 s16*9 } >> \markup "Manually shaped:" \new PianoStaff << \new Staff = up \relative d { \clef G \key e \major \time 3/16 \voiceTwo \slurUp \shapeII #'((h)(p 55 0.5)(p 50 0.2)(h 0 1.5)) Slur \SDn \times 2/3 { b32( g' b } \SUp \times 2/3 { d g e' } \times 2/3 { d b g') } | \SDn \times 2/3 { b,,,32( g' b } \SUp \times 2/3 { dis g e' } \times 2/3 { d b g') } | \newSpacingSection \override Score.SpacingSpanner #'common-shortest-duration = #(ly:make-moment 1 70) \SDn \times 2/3 { b,,,32( g' b } \SUp \times 2/3 { dis g e' } \times 2/3 { d b g') } } \new Staff = down { \clef F \key e \major \time 3/16 s16*9 } >> \markup "Overridden penalty for edge distance:" \new PianoStaff << \new Staff = up \relative d { \override Slur.details.edge-attraction-factor = #6 \clef G \key e \major \time 3/16 \voiceTwo \slurUp \SDn \times 2/3 { b32( g' b } \SUp \times 2/3 { d g e' } \times 2/3 { d b g') } | \SDn \times 2/3 { b,,,32( g' b } \SUp \times 2/3 { dis g e' } \times 2/3 { d b g') } | \newSpacingSection \override Score.SpacingSpanner #'common-shortest-duration = #(ly:make-moment 1 70) \SDn \times 2/3 { b,,,32( g' b } \SUp \times 2/3 { dis g e' } \times 2/3 { d b g') } } \new Staff = down { \clef F \key e \major \time 3/16 s16*9 } >> \markup "+Overriden slur distance" \new PianoStaff << \new Staff = up \relative d { \override Slur.details.edge-attraction-factor = #6 \override Slur.details.free-head-distance = #1.2 \clef G \key e \major \time 3/16 \voiceTwo \slurUp \SDn \times 2/3 { b32( g' b } \SUp \times 2/3 { d g e' } \times 2/3 { d b g') } | \SDn \times 2/3 { b,,,32( g' b } \SUp \times 2/3 { dis g e' } \times 2/3 { d b g') } | \newSpacingSection \override Score.SpacingSpanner #'common-shortest-duration = #(ly:make-moment 1 70) \SDn \times 2/3 { b,,,32( g' b } \SUp \times 2/3 { dis g e' } \times 2/3 { d b g') } } \new Staff = down { \clef F \key e \major \time 3/16 s16*9 } >>