lilypond-user
[Top][All Lists]
Advanced

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

Re: How to get bounding NoteColumns of the first of consecutive TextSpan


From: Thomas Morley
Subject: Re: How to get bounding NoteColumns of the first of consecutive TextSpanners
Date: Sat, 6 Mar 2021 16:30:30 +0100

Hi David,

I don't understand the advantage of this coding compared with the one
from your post before.
Nevertheless I took the idea to separate a coding to find the wished
bound-objects, see below.

Am Do., 4. März 2021 um 14:08 Uhr schrieb David Nalesnik
<david.nalesnik@gmail.com>:

>
> Right, the bounds of the broken pieces would be NonMusicalPaperColumn
> grobs.  I've adapted Harm's code to deal with this.  I don't know how
> you'd deal with this in an engraver--if using ly:item-break-dir would
> be an option, for example.  (@Harm: I don't know of course what the
> ultimate goal of your experiment is, so I hope I haven't taken this
> too far, or removed code you still want in place!)

The goal is to center a text between two notes.
Though, I made the experience wanting to calculate said centering with
different values depending on the combinations of the Stem directions.
Going for TextSpanner seemed to be an easy method. Well, until I
realised right-bound may be PaperColumn for consecutive TextSpanners.
Other possibilities for bounds not being a NoteColumn are more or less
trivial to filter or circumvent.
Also, TextSpanner seemed to be a nice grob to set different behaviour
for up/down-stemmed NoteColumns: simply adjust the relevant
'attach-dir

I'd be open open for other suggestions, though :)

For now I did:

\version "2.22.0"

#(define (get-bound-stem-dir spanner dir)
  "Get @code{Stem.direction} of the bounding @code{NoteColumn}. If @var{spanner}
is bound to @code{NonMusicalPaperColumn} or @code{PaperColumn} try to find a
a @code{Stem} grob in the same @code{Staff} at the same musical moment."
  (let* ((bound (ly:spanner-bound spanner dir)))
    (if (grob::has-interface bound 'note-column-interface)
        ;; If bound is a NoteColumn, then it will surely be in the same Staff
        ;; as the `spanner'.
        ;; If it contains NoteHeads get Stem.direction
        (let ((stem (ly:grob-object bound 'stem)))
          (and (ly:grob? stem)
               (ly:grob-array? (ly:grob-object bound 'note-heads))
               (ly:grob-property stem 'direction)))
        ;; Select Stems by comparing the 'staff-symbol of `spanner' with
        ;; 'staff-symbol of Stems, found in (NonMusical)PaperColumn at same
        ;; musical moment
        (let* ((spanner-staff-symbol
                (ly:grob-object spanner 'staff-symbol))
               (bound-elts (ly:grob-object bound 'elements))
               (stems-from-bound
                 (filter
                   (lambda (elt)
                     (grob::has-interface elt 'stem-interface))
                   (if (ly:grob-array? bound-elts)
                       (ly:grob-array->list bound-elts)
                       '())))
               (stems
                 (filter
                   (lambda (stem)
                     (equal? spanner-staff-symbol
                             (ly:grob-object stem 'staff-symbol)))
                   stems-from-bound)))
          ;; TODO In polyphonic situations there may be more than one Stem
          ;;      How to select?
          (if (pair? stems)
              (ly:grob-property (car stems) 'direction)
              #f)))))

#(define (text-stencil txt)
  "Hack @code{TextSpanner.stencil} to center a text between the bounds.
If bounds are @code{NoteColumn} grobs, we take @code{Stem.direction} into
account in order to get a nice output."
  (lambda (grob)
    (let* (;; For consecutive TextSpanner or TextSpanner started/ended at
           ;; spacers or at line break left/right bound may be PaperColumn or
           ;; NonMusicalPaperColumn.
           ;; Though, we are interested in the Stems, if there's a bounding
           ;; NoteColumn or a NoteColumn at the same musical moment as
           ;; PaperColumn.
           ;; Thus we look up Stem in the bound's elements-array, in order
           ;; to specify bound-details.right.attach-dir depending on
           ;; Stem.direction.
           (stem-left-dir
             (get-bound-stem-dir grob LEFT))
           (stem-right-dir
             (get-bound-stem-dir grob RIGHT)))
      ;; adjust left/right padding and left/right attach-dir
      (if (and (number? stem-left-dir) (number? stem-right-dir))
          (begin
            ;; compensate Stem.thickness
            ;; IR says it's 1.3
            (ly:grob-set-nested-property! grob
              '(bound-details right padding) 0.13)
            (ly:grob-set-nested-property! grob
              '(bound-details left padding) 0)
            ;; always start at right edge of left bound
            (ly:grob-set-nested-property! grob
              '(bound-details left attach-dir) 1)

            (cond ((and (positive? stem-left-dir) (positive? stem-right-dir))
                    (ly:grob-set-nested-property! grob
                      '(bound-details right attach-dir) 1))
                  ((and (positive? stem-left-dir) (negative? stem-right-dir))
                    (ly:grob-set-nested-property! grob
                      '(bound-details right attach-dir) 0))
                  (else
                    (ly:grob-set-nested-property! grob
                      '(bound-details right attach-dir) -1)))))

      (let* ((stil (ly:line-spanner::print grob))
             (stil-center (interval-center (ly:stencil-extent stil X)))
             (text-stil
               (grob-interpret-markup grob
                 (if (not-first-broken-spanner? grob)
                     (make-parenthesize-markup txt)
                     txt)))
             (text-center (interval-center (ly:stencil-extent text-stil X))))

       (ly:stencil-add
        ;; for reference or visual debugging add:
        ;stil
        (ly:stencil-translate-axis text-stil (- stil-center text-center) X)
        )
        ))))

%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% EXAMPLEs
%%%%%%%%%%%%%%%%%%%%%%%%%%%

\layout {
  \override TextSpanner.font-shape = #'upright
  \override TextSpanner.style = #'solid
}

mus = <<
  { \repeat unfold 8 b4 }

  {
    g'-\tweak stencil #(text-stencil "1") \startTextSpan
    a'\stopTextSpan
      -\tweak stencil #(text-stencil "1½") \startTextSpan
    c''\stopTextSpan
       -\tweak stencil #(text-stencil "½") \startTextSpan
    b'\stopTextSpan
      -\tweak stencil #(text-stencil "0") \startTextSpan
    b'\stopTextSpan
      -\tweak stencil #(text-stencil "1") \startTextSpan
    a'\stopTextSpan
      -\tweak stencil #(text-stencil "0") \startTextSpan
    a'\stopTextSpan
      -\tweak stencil #(text-stencil "full") \startTextSpan
    b'\stopTextSpan
  }

  { \repeat unfold 8 b }
>>

\score {
  \mus
  \layout {}
  \layout {
      line-width = 120
      ragged-right = ##f
  }
  \layout {
      ragged-right = ##f
  }
}


Thanks,
  Harm



reply via email to

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