[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