|
From: | Dimitris Marinakis |
Subject: | Re: Custom Spanner with variable length sections |
Date: | Fri, 15 Apr 2022 23:41:46 +0300 |
Le 15/04/2022 à 16:35, Dimitris Marinakis a écrit :
> Thank you so much Jean. That looks amazing for a first try. Great work!
>
> Sorry it took me a while to test it.
Less than a day? I didn't find that long :-)
> Upon testing I found out that it only works when there are line breaks
> (not on a single system).
Right, I intended to add this code, but forgot it along the way.
> Also it would be useful to have control over the broken.left/right
> paddings. This is an issue especially if the spanner is used above the
> stave (clef collisions).
>
> Is it too hard to an option to align the broken sections on the first
> and last notes in the system instead of the system edges? The current
> behaviour isn't wrong. It's just not suitable for all use cases.
See the second attempt below. This SymbolFiller implements a subset
of the horizontal-line-spanner-interface. You can override padding and
end-on-note in bound-details.
I hope you don't mind that this now requires a development version.
Best,
Jean
\version "2.23.7"
#(define (define-grob! grob-name grob-entry)
(set! all-grob-descriptions
(cons ((@@ (lily) completize-grob-entry)
(cons grob-name grob-entry))
all-grob-descriptions)))
#(define (partial-sums lst)
(cdr (reverse! (fold
(lambda (new previous)
(cons (+ new (car previous))
previous))
(list 0)
lst))))
#(define (symbol-filler::print grob)
(let* ((widths (ly:grob-property grob 'widths))
(symbols (ly:grob-property grob 'symbols))
(orig (ly:grob-original grob))
(proto-siblings (ly:spanner-broken-into orig))
(siblings (if (null? proto-siblings)
(list grob)
proto-siblings))
(sib-exts-to-fill
(map
(lambda (sib)
(define (on-dir sym dir)
(let* ((details (ly:grob-property sib sym))
(x (assoc-get 'X details))
(pding (assoc-get 'padding details)))
(+ x (* -1 dir pding))))
(cons (on-dir 'left-bound-info LEFT)
(on-dir 'right-bound-info RIGHT)))
siblings))
(sib-widths (map interval-length sib-exts-to-fill))
(sib-changes (partial-sums sib-widths))
(total-spanner-width (apply + sib-widths))
(total-sym-width (apply + widths))
(normalized-syms (map (lambda (x)
(* x (/ total-spanner-width
total-sym-width)))
widths))
(sym-changes (partial-sums normalized-syms))
(sib-stil empty-stencil)
(len-so-far 0)
(retval #f))
;; Let's do an exception. This is easier written in imperative style.
(while (and (pair? sib-changes)
(pair? sym-changes))
(let* ((sib (car siblings))
(next-sym-maybe (car symbols))
(next-stil-maybe (grob-interpret-markup sib next-sym-maybe))
(len (interval-length (ly:stencil-extent next-stil-maybe X)))
(new-len-so-far (+ len len-so-far)))
(cond
((> new-len-so-far (car sib-changes))
;; Used full length of this broken piece. Set
;; its stencil and start using the next.
(let* ((tr (- (interval-start (car sib-exts-to-fill))
(ly:grob-relative-coordinate sib
(ly:grob-system sib)
X)))
(tr-stil (ly:stencil-translate-axis sib-stil tr X)))
(if (eq? grob (car siblings))
(set! retval tr-stil)
(ly:grob-set-property! (car siblings)
'stencil
tr-stil)))
(set! sib-changes (cdr sib-changes))
(set! siblings (cdr siblings))
(set! sib-stil empty-stencil)
(set! sib-exts-to-fill (cdr sib-exts-to-fill)))
((> new-len-so-far (car sym-changes))
;; Done with this symbol, start using the next.
(set! sym-changes (cdr sym-changes))
(set! symbols (cdr symbols)))
(else
(set! sib-stil (ly:stencil-stack sib-stil X RIGHT
next-stil-maybe 0))
(set! len-so-far new-len-so-far)))))
retval))
#(define-grob! 'SymbolFiller
`((bound-details . ((left . ((padding . 0)))
(right . ((padding . 0)))))
(direction . ,DOWN)
(left-bound-info . ,ly:horizontal-line-spanner::calc-left-bound-info)
(normalized-endpoints . ,ly:spanner::calc-normalized-endpoints)
(right-bound-info .
,ly:horizontal-line-spanner::calc-right-bound-info)
(stencil . ,symbol-filler::print)
(staff-padding . 3.0)
(symbols . ,(grob::calc-property-by-copy 'symbols))
(widths . ,(grob::calc-property-by-copy 'widths))
(Y-offset . ,ly:side-position-interface::y-aligned-side)
(meta . ((class . Spanner)
(interfaces . (side-position-interface
horizontal-line-spanner-interface))))))
#(define (Symbol_filler_engraver context)
(let ((filler #f)
(ev #f))
(make-engraver
(listeners
((symbol-filler-event engraver event)
(set! ev event)))
((process-music engraver)
(if ev
(let ((d (ly:event-property ev 'span-direction)))
(if (eqv? d LEFT)
(begin
(set! filler (ly:engraver-make-grob engraver
'SymbolFiller ev))
(ly:spanner-set-bound! filler
LEFT
(ly:context-property context
'currentMusicalColumn)))
(begin
(ly:spanner-set-bound! filler
RIGHT
(ly:context-property context
'currentMusicalColumn))
(ly:engraver-announce-end-grob engraver filler ev))))))
(acknowledgers
((note-column-interface engraver grob source-engraver)
(when filler
(ly:pointer-group-interface::add-grob filler 'note-columns
grob))))
((stop-translation-timestep engraver)
(set! ev #f)))))
\layout {
\context {
\Global
\grobdescriptions #all-grob-descriptions
}
\context {
\Voice
\consists #Symbol_filler_engraver
}
}
#(define (define-event! type properties)
(set! properties (assoc-set! properties 'name type))
(hashq-set! music-name-to-property-table type properties)
(set! music-descriptions
(sort (cons (cons type properties)
music-descriptions)
alist<?)))
#(define-event-class 'symbol-filler-event 'span-event)
#(define-event! 'SymbolFillerEvent
'((types . (symbol-filler-event span-event post-event event))))
startFiller =
#(define-music-function (widths symbols) (number-list? markup-list?)
(make-music 'SymbolFillerEvent
'span-direction LEFT
'widths widths
'symbols symbols))
stopFiller = #(make-music 'SymbolFillerEvent 'span-direction RIGHT)
{
% Demonstrate various settings.
\override SymbolFiller.bound-details.left.padding = 2
\override SymbolFiller.bound-details.left-broken.padding = 1
\override SymbolFiller.bound-details.left-broken.end-on-note = ##t
\override SymbolFiller.bound-details.right.padding = 2
\override SymbolFiller.bound-details.right-broken.padding = 1
\override SymbolFiller.bound-details.right-broken.end-on-note = ##t
g'1\startFiller #'(10 20) \markuplist { a b } 1\stopFiller
\break
g'1\startFiller #'(5 5 10 30 50) \markuplist { * \musicglyph
"scripts.trill_element" + × "#" }
1 1 1 1
\break
1 1 1 1 1
1 1 1 1 1
\break
1 1 1 1 1
\break
1 1 1 1 1
\break
1 1\stopFiller
}
[Prev in Thread] | Current Thread | [Next in Thread] |