lilypond-user
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Finding the objects at the start of the current measure


From: Urs Liska
Subject: Finding the objects at the start of the current measure
Date: Mon, 25 Feb 2019 13:13:09 +0100
User-agent: Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Thunderbird/60.4.0

Recently I managed (with considerable help from here) to write a function that can a) center some arbitrary stuff in a measure and b) print markup above/below that measure that pushes the surrounding barlines so the markup fits in the measure. However, before I can make this nice function available I have to resolve at least one further problem.

The function works by

- creating a markup stencil from the to-be-centered music
- creating the actual markup stencils and measuring their width
- inserting a MMR, setting its minimum-length to the determined width (plus some padding)
- replacing the MMR's stencil with the combined stencils created earlier.

As can be seen in the attached image this works smoothly in regular cases (first instance). However, it works *not* correctly when there is some stuff like clefs, key or time signatures at the beginning of the measure. The second and third instances in the image show cases where stuff of varying width is at the beginning of the measure, and in both cases the measure is not pushed wide enough. The same is true at the beginning of a system.

So what I need to do is determine if there's anything at that beginning, determine its width and calculate a value from there that I have to add to the minimum-length override.

I know that this is not linear. In the first instance (the 4/4 time sig) it would probably be ok to simply add the time signature's width, but in the other instance the to-be-added width is significantly less than the width of the combined time and the key signatures. I recall having to deal with that issue once, I think the calculation starts from a fixed width (2.0) and interpolates that up to a certain width when things work "normal" again.

First thing I need is to know: is it possible to know the width of these elements in a before-line-breaking callback? I have serious doubts because at that point we don't even know yet whether we're at a line break. However, in later callbacks I can't set minimum-length anymore.

If that is possible at all I'd need some help how to find the column where all these objects may be or some property of the current measure or the previous barline. From there I'd probably be able to get further on my own.

Thanks
Urs

\version "2.19.82"

\include "oll-core/package.ily"

% Center a (markup) stencil against an (original) MultiMeasureRest grob
#(define (center-stencil rest-grob markup-stencil)
   (let*
    ((rest-stencil (ly:multi-measure-rest::print rest-grob))
     (centered-markup-stencil (ly:stencil-aligned-to markup-stencil X 0))
     (rest-offset (interval-center (ly:stencil-extent rest-stencil X))))
    ;; return the self-centered time stencil offset by the rest's offset
    (ly:stencil-translate-axis centered-markup-stencil rest-offset X)))

#(define (annotate-centered rest-grob markup-stencil upper-padding upper lower-padding lower)
   (let*
    ((base-stencil (center-stencil rest-grob markup-stencil))
     (base-y-extent (ly:stencil-extent base-stencil Y))
     (upper-offset (- 2 (cdr base-y-extent)))
     (lower-offset (+ (car base-y-extent) 2))
     (upper-stencil
      (center-stencil rest-grob
        (grob-interpret-markup rest-grob
          (markup
           #:override '(baseline-skip . 2.5) upper))))
     (lower-stencil
      (center-stencil rest-grob
        (grob-interpret-markup rest-grob
          (markup
           #:override '(baseline-skip . 2.5) lower))))
     (combined-stencil
      (ly:stencil-combine-at-edge
       (ly:stencil-combine-at-edge
        base-stencil Y DOWN
        lower-stencil
        (+ lower-offset lower-padding))
       Y UP
       upper-stencil
       (+ upper-offset upper-padding))))
    combined-stencil))

% Center some (annotated) music in a measure

% Wrap the music in a bare \markup \score context
% and return its stencil
getBareScoreMarkupStencil =
#(define-scheme-function (grob music)(ly:grob? ly:music?)
   (grob-interpret-markup grob
     #{
       \markup \score {
         \new Staff = "centered" {
           % Necessary to remove some offset to the right
           % (caused by the regular system-start gap)
           \once \override NoteColumn.X-offset = -2
           $music
         }
         \layout {
           ragged-right = ##t
           \context {
             \Score
             \omit StaffSymbol
             \omit Clef
             \omit TimeSignature
             \omit KeySignature
             \omit BarLine
           }
         }
       }
     #}))

annotateCenteredMusic =
#(with-options define-music-function (music)(ly:music?)
   `(strict
     (? above ,markup? ,#{ \markup \null #})
     (? below ,markup? ,(markup #:null)))
   ;; Store data in a closure to drag it over from the music-function stage
   ;; to before-line-breaking and stencil
   (let ((upper (assq-ref props 'above))
         (lower (assq-ref props 'below))
         (music-stil #f)
         (upper-stil #f)
         (lower-stil #f)
         (upper-padding 2)
         (lower-padding 2))
     #{
       \tweak before-line-breaking
       #(lambda (grob)
          ;; Create the three markup stencils *now* and store it in the closure
          ;; so we can use its dimensions to affect the layout.
          (set! music-stil #{ \getBareScoreMarkupStencil #grob #music #})
          (set! upper-stil (grob-interpret-markup grob upper))
          (set! lower-stil (grob-interpret-markup grob lower))

          (ly:grob-set-property! grob 'Y-extent
            ;; Include the markups in the Y-extent of the MMR
            ;; so it won't get cut off the page
            (cons
             (- 0 2 lower-padding (interval-length (ly:stencil-extent lower-stil Y)))
             (+ 2 upper-padding (interval-length (ly:stencil-extent upper-stil Y)))))

          (ly:grob-set-property! grob 'minimum-length
            ;; widen the measure to encompass music content, upper, and lower markup
            ; TODO: This still is confused by leading Clef/Time/Key
            (+ 2
              (max
               (interval-length (ly:stencil-extent upper-stil X))
               (interval-length (ly:stencil-extent lower-stil X))
               (interval-length (ly:stencil-extent music-stil X)))))
          )
       \tweak stencil
       #(lambda (grob)
          ;; Replace the MMR stencil with the combined stencil created earlier
          (annotate-centered grob music-stil
            upper-padding upper lower-padding lower))
       % TODO: make this length variable/configurable
       % Ideally take it from the surrounding music/time signature
       R1
     #}))


\relative {
  R1
  \annotateCenteredMusic \with {
    above = "Upper text"
    below = "Lower"
  } { r8 }
  R1
  \numericTimeSignature
  \time 4/4
  \annotateCenteredMusic \with {
    above = "Upper text"
    below = "Lower"
  } { r16 }
  \time 3/4
  R2.
  \time 4/4
  \key b \major
  \annotateCenteredMusic \with {
    above = "Upper text"
    below = "Lower"
  } { r32 }
  R1
  
}

Attachment: document.png
Description: PNG image


reply via email to

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