lilypond-user
[Top][All Lists]
Advanced

[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



reply via email to

[Prev in Thread] Current Thread [Next in Thread]