|
From: | Lukas-Fabian Moser |
Subject: | Re: Breaking slurs at apex |
Date: | Mon, 26 Apr 2021 19:28:25 +0200 |
User-agent: | Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Thunderbird/78.7.1 |
Hi Michael,
Well, I only needed it for over a single break for a figure in a dissertation that I'm turning in today, so it's good enough for the moment.
Actually, "the math" is already in LilyPond, as part of Mike Solomon's bezier tools. Unfortunately, they are not defined as publicly available, so I copied them. (Is there a better way to make the definitions of bezier-tools.scm available? Of course I can #(load ...) them, but I'm not certain if this can be done independently of my path configuration.)
split-bezier enables us to take, for instance, the left half of a slur. Then it's just a matter of scaling it to its original width, which leads to:
\version "2.22.0"
% copied from bezier-tools.scm
#(define (coord- coord1 coord2)
"Subtract @var{coord2} from @var{coord1}."
(cons (- (car coord1) (car coord2))
(- (cdr coord1) (cdr coord2))))
#(define (coord* scalar coord)
"Multiply each component of @var{coord} by @var{scalar}."
(cons (* (car coord) scalar)
(* (cdr coord) scalar)))
#(define (coord+ coord1 coord2)
"Add @var{coord1} to @var{coord2}, returning a coordinate."
(cons (+ (car coord1) (car coord2))
(+ (cdr coord1) (cdr coord2))))
#(define (interpolated-control-points control-points split-value)
"Interpolate @var{control-points} at @var{split-value}. Return
a
set of control points that is one degree less than
@var{control-points}."
(if (null? (cdr control-points))
'()
(let ((first (car control-points))
(second (cadr control-points)))
(cons* (coord+ first (coord* split-value (coord- second
first)))
(interpolated-control-points
(cdr control-points)
split-value)))))
#(define (split-bezier bezier split-value)
"Split a cubic bezier defined by @var{bezier} at the value
@var{split-value}. @var{bezier} is a list of pairs; each pair is
is the coordinates of a control point. Returns a list of beziers.
The first element is the LHS spline; the second
element is the RHS spline."
(let* ((quad-points (interpolated-control-points
bezier
split-value))
(lin-points (interpolated-control-points
quad-points
split-value))
(const-point (interpolated-control-points
lin-points
split-value))
(left-side (list (car bezier)
(car quad-points)
(car lin-points)
(car const-point)))
(right-side (list (car const-point)
(list-ref lin-points 1)
(list-ref quad-points 2)
(list-ref bezier 3))))
(cons left-side right-side)))
% ------------------------------------------------------------ %
#(define (affine-x-transform factor offset)
; constructs a function applying x |-> factor * x + offset
; to the car of a pair
(lambda (pair)
(cons
(+ offset (* factor (car pair)))
(cdr pair))))
#(define (bezier-x-zoom-to-extent bezier x-extent)
; interpolates bezier such that:
; the first control point will have x-coordinate (car
x-extent),
; the last control point will have x-coordinate (cdr x-extent).
(let* ((old-span (- (car (fourth bezier)) (car (first
bezier))))
(new-span (- (cdr x-extent) (car x-extent)))
(factor (/ new-span old-span))
(offset (- (car x-extent) (* factor (car (first
bezier))))))
(map (affine-x-transform factor offset) bezier)))
#(define (horizontalise-broken-slurs grob)
(let*
((orig (ly:grob-original grob))
(siblings (if (ly:grob? orig)
(ly:spanner-broken-into orig)
'())))
(if (>= (length siblings) 2)
(let*
((control-points (ly:grob-property grob
'control-points))
(x-extent (cons (car (first control-points))
(car (fourth control-points))))
(half-slurs (split-bezier control-points 1/2))
(new-control-points
(cond
((eq? (first siblings) grob)
(bezier-x-zoom-to-extent (car half-slurs) x-extent))
((eq? (last siblings) grob)
(bezier-x-zoom-to-extent (cdr half-slurs) x-extent))
(else control-points))))
(ly:grob-set-property! grob 'control-points
new-control-points)))))
\relative {
\override Slur.after-line-breaking = #horizontalise-broken-slurs
c'2 c( e) c c c c'1 ( \break d, d2) 2 2 2 2 2( \break
\repeat unfold 8 c4 \break 2)
}
Of course this relies on the "standard" broken slurs being
symmetrical.
Cracking me up though, you and Jean with your "a little trig" and "just do the math."
:-) I guess a large percentage of people on this list have some sort of maths/computer science/... background - which in my case is a bit rusty, so I was glad that the hard part had already been done by Mike.
Granted, "a little trig" might scare away most musicians (or people in general, for that matter), but if you restrict to the select subset of musicians willing to delve into a text-based music typesetting system distantly related to LaTeX and using a dialect of Lisp as its natural extension language, that would seem to change the odds...
Lukas
[Prev in Thread] | Current Thread | [Next in Thread] |