[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: Tweak beam slashes for small staves
From: |
Thomas Morley |
Subject: |
Re: Tweak beam slashes for small staves |
Date: |
Sun, 22 Jul 2018 12:04:32 +0200 |
2018-07-22 6:30 GMT+02:00 Edward Neeman <address@hidden>:
> Hello,
>
> This snippet produces very nice slashed grace note beams:
> http://lsr.di.unimi.it/LSR/Snippet?id=721
>
> However the slashes don’t adjust properly for smaller staves that are tweaked
> with the \magnifyStaff function. In the example below, the slash on the
> second (smaller) staff is too high and too long. How might I change the
> function to accommodate the \magnifyStaff value?
>
> Thanks,
> Edward
>
> \version "2.19.80"
>
> slash =
> #(define-music-function (ang stem-fraction protrusion)
> (number? number? number?)
> (remove-grace-property 'Voice 'Stem 'direction) ; necessary?
> #{
> \once \override Stem #'stencil =
> #(lambda (grob)
> (let* ((X-parent (ly:grob-parent grob X))
> (is-rest? (ly:grob? (ly:grob-object X-parent 'rest))))
> (if is-rest?
> empty-stencil
> (let* ((ang (degrees->radians ang))
> ; We need the beam and its slope so that slash will
> ; extend uniformly past the stem and the beam
> (beam (ly:grob-object grob 'beam))
> (beam-X-pos (ly:grob-property beam 'X-positions))
> (beam-Y-pos (ly:grob-property beam 'positions))
> (beam-slope (/ (- (cdr beam-Y-pos) (car beam-Y-pos))
> (- (cdr beam-X-pos) (car beam-X-pos))))
> (beam-angle (atan beam-slope))
> (stem-Y-ext (ly:grob-extent grob grob Y))
> ; Stem.length is expressed in half staff-spaces
> (stem-length (/ (ly:grob-property grob 'length) 2.0))
> (dir (ly:grob-property grob 'direction))
> ; if stem points up. car represents segment of stem
> ; closest to notehead; if down, cdr does
> (stem-ref (if (= dir 1) (car stem-Y-ext) (cdr
> stem-Y-ext)))
> (stem-segment (* stem-length stem-fraction))
> ; Where does slash cross the stem?
> (slash-stem-Y (+ stem-ref (* dir stem-segment)))
> ; These are values for the portion of the slash that
> ; intersects the beamed group.
> (dx (/ (- stem-length stem-segment)
> (- (tan ang) (* dir beam-slope))))
> (dy (* (tan ang) dx))
> ; Now, we add in the wings
> (protrusion-dx (* (cos ang) protrusion))
> (protrusion-dy (* (sin ang) protrusion))
> (x1 (- protrusion-dx))
> (y1 (- slash-stem-Y (* dir protrusion-dy)))
> (x2 (+ dx protrusion-dx))
> (y2 (+ slash-stem-Y
> (* dir (+ dy protrusion-dy))))
> (th (ly:staff-symbol-line-thickness grob))
> (stil (ly:stem::print grob)))
>
> (ly:stencil-add
> stil
> (make-line-stencil th x1 y1 x2 y2))))))
> #})
>
> slashI = {
> \slash 50 0.6 1.0
> }
>
> <<
> \new Staff { \grace { \slashI b''8[ c'''] } c'' }
> \new Staff \with { \magnifyStaff #2/3 } { \grace { \slashI b''8[ c'''] }
> c'' }
> >>
>
> ---
> Dr. Edward Neeman
> www.neemanpianoduo.com
Hi,
the LSR-snippet does not take the changed staff-space into account,
thus the calculations are wrong, if staff-space isn't default.
The used trigonomitrcs makes it even harder to debug: Simply applying
the changed staff-space to the found x1/2 y1/2 values will not
succeed.
Further more I was always dissatisfied, because no automatic slashed
beams were possible.
Over the years I repeatedly tried different approaches and erlier this
year I've finally found something more satisfying.
It overrides Beam not Stem.
No trogonometrics.
Several adjustments are possible by overriding sub-properties of
Beam.details. Here the defaults as overrides:
%% defaults:
%% \override Beam.details.attach-side = #'left
%% \override Beam.details.stem-part = 1
%% \override Beam.details.slash-gradient = 1/2
%% \override Beam.details.slash-x-y-over-shoot = #'(0.3 . 0.8)
%% \override Beam.details.slash-thickness = 0.1
They suited me well in an own project, though for Beams of 8th-notes
the slash may collide with the NoteHead.
I always found Stems of 8th-notes are too often at minimum size in
LilyPond in general. For adding a slash this means there is (too)
little space.
It's one of the TODOs: automatic adjust slash-placement depending on
the duration.
For the examples below I therefore used modified values.
The other TODO is 'attach-side 'right. Atm it's buggy, it is possible
to adjust the other details sub-properties to get a nice output, but
not out-of-the-box.
But here the code with your example, additionally I included the LSR-examples.
\version "2.19.82"
%% c/p from lily-library.scm (it is not public)
#(define (sign x)
(if (= x 0)
0
(if (< x 0) -1 1)))
#(define (slashed-beam beam-stil)
(lambda (grob)
(let* ((orig-grob (ly:grob-original grob))
(broken-beams (ly:spanner-broken-into orig-grob))
(beam-stil-x-ext (ly:stencil-extent beam-stil X))
(side
(assoc-get 'attach-side (ly:grob-property grob 'details) 'left))
;; which side?
(on-left? (eq? side 'left))
;; get beam direction, can be found by examining stem direction.
(stems (ly:grob-object grob 'stems))
;; on the left, use the first stem. on the right, use last stem.
;; this allows the function to work with kneed beams as well.
(stem
(if on-left?
(ly:grob-array-ref stems 0)
(ly:grob-array-ref stems (1- (ly:grob-array-length stems)))))
(stem-dir (ly:grob-property stem 'direction #f))
(beam-X-pos (ly:grob-property grob 'X-positions))
(beam-Y-pos (ly:grob-property grob 'positions))
(beam-start-y (car beam-Y-pos))
(beam-end-y (cdr beam-Y-pos))
(beam-gradient
(/ (- beam-end-y beam-start-y)
(interval-length beam-X-pos)))
(beam-slope
(* stem-dir (sign (- (abs beam-end-y) (abs beam-start-y)))))
(relevant-beam-y
(if on-left?
beam-start-y
beam-end-y))
(beam-thick (ly:grob-property grob 'beam-thickness))
(half-beam-thick (/ beam-thick 2))
(length-fraction (ly:grob-property grob 'length-fraction 1))
(stem-part
(assoc-get 'stem-part (ly:grob-property grob 'details) 1))
(slash-thick
(assoc-get
'slash-thickness
(ly:grob-property grob 'details)
(ly:output-def-lookup
(ly:grob-layout grob)
'line-thickness
0.1)))
(slash-gradient
(assoc-get
'slash-gradient
(ly:grob-property grob 'details)
(cons 1 2)))
(slash-gradient-fraction
(/ (cdr slash-gradient) (car slash-gradient)))
(slash-x-y-over-shoot
(assoc-get
'slash-x-y-over-shoot
(ly:grob-property grob 'details)
'(0.3 . 0.8)))
(stick-out-x (car slash-x-y-over-shoot))
(stick-out-y (cdr slash-x-y-over-shoot))
;; We first construct a line starting at the relevant stem, ending
;; in the middle of the beam, relying on an appropriate move in
;; y-direction, ensured by `stem-y'
(stem-y
(- relevant-beam-y (* length-fraction stem-part stem-dir)))
;; Get a x-value, where the slash will match the beam
(inner-x
(if on-left?
(/ (* -1 length-fraction stem-part stem-dir)
(- beam-gradient (* stem-dir slash-gradient-fraction)))
(/ (* length-fraction stem-part stem-dir)
(+ beam-gradient (* stem-dir slash-gradient-fraction)))))
;; We ensure a constant gap between the end-point of the slash and
;; the beam, by transforming the given `stick-out-y' into a value
;; which is added to `inner-x' later.
(add-x
(if (or (and (> stem-dir 0) on-left?)
(and (<= stem-dir 0) (not on-left?)))
(/ stick-out-y (- slash-gradient-fraction beam-gradient))
(/ stick-out-y (+ slash-gradient-fraction beam-gradient))))
;; To get the slash sticking out to the left, apply `stick-out-x'
;; to the starting x-value and apply `slash-gradient-fraction' to the
;; starting y-value.
(slash-start-x
(if on-left?
(- (car beam-stil-x-ext) stick-out-x)
(+ (cdr beam-stil-x-ext) stick-out-x)))
(slash-start-y
(+ stem-y
(*
stem-dir
(- (+ half-beam-thick stick-out-x))
slash-gradient-fraction)))
(slash-end-x
(if on-left?
(+ inner-x add-x)
(- (cdr beam-stil-x-ext) (+ inner-x add-x))))
(slash-end-y
(+ stem-y
(* stem-dir (+ inner-x add-x) slash-gradient-fraction)))
(staff-space (ly:staff-symbol-staff-space grob)))
(ly:stencil-add
beam-stil
(if (or (not (pair? broken-beams))
(equal? grob (car broken-beams)))
;; for debugging purposes we let the color in place, commented
;(stencil-with-color
(make-line-stencil
;; thick
slash-thick
;; start-coords
(* staff-space slash-start-x)
(* staff-space slash-start-y)
;; end-coords
(* staff-space slash-end-x)
(* staff-space slash-end-y))
; red)
empty-stencil)))))
slashedBeam =
\override Beam.stencil =
#(lambda (grob)
;; TODO
;; this will catch the default-stencil, to get an already tweaked
;; stencil call (ly:grob-property grob 'stencil) and apply it
;; and set the stencil 'after-line-breaking.
(slashed-beam (ly:beam::print grob)))
startAcciaccaturaMusic = {
<>\startGraceSlur
\temporary \override Flag.stroke-style = #"grace"
\temporary \slashedBeam
}
stopAcciaccaturaMusic = {
\revert Flag.stroke-style
\revert Beam.stencil
<>\stopGraceSlur
}
%%%%%%%%%%%%%%%%%%%%%%
%% EXAMPLES
%%%%%%%%%%%%%%%%%%%%%%
\layout {
\override Beam.details.stem-part = 0.8
\override Beam.details.slash-gradient = 2/3
\override Beam.details.slash-x-y-over-shoot = #'(0.3 . 0.6)
}
<<
\new Staff
{
\grace { \slashedBeam b''8[ c'''] } c''
}
\new Staff
\with { \magnifyStaff #2/3 }
{
\grace { \slashedBeam b''8[ c'''] } c''
}
>>
%% Examples from http://lsr.di.unimi.it/LSR/Snippet?id=721
%% \magnifyStaff #2/3 always applied
\new Staff \with { \magnifyStaff #2/3 } {
\relative c' {
\acciaccatura { d8[ e f g] } d4
\acciaccatura { g8[ a b c] } d4
\acciaccatura { g8[ a b c] } d4
\acciaccatura { g8[ a b c ] } d4
\clef bass
\acciaccatura { d,,,8[ c b a ] } g4
\acciaccatura { d8[ c b a ] } g4
\acciaccatura { d8[ c b a ] } g4
\acciaccatura { d8[ c b a ] } g4
}
}
\new Staff \with { \magnifyStaff #2/3 } {
\relative c'' {
\acciaccatura {
dis32[ e, a' bes, cis, d' ]
}
es,4
}
}
\new PianoStaff <<
\new Staff = "1" \with { \magnifyStaff #2/3 } {
s1*0
\grace {
\slashedBeam
\stemDown
a'''16[
\change Staff = "2"
\stemUp
bes,
\change Staff = "1"
\stemDown
fis''16
\change Staff = "2"
\stemUp
g]
}
\change Staff = "1"
es'4
}
\new Staff = "2" \with { \magnifyStaff #2/3 } {
\clef bass
\grace s4
s4
}
>>
I've used this in a large own project, so I think it's pretty robust.
HTH,
harm