[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: Custom Spanner with variable length sections
From: |
Jean Abou Samra |
Subject: |
Re: Custom Spanner with variable length sections |
Date: |
Fri, 15 Apr 2022 23:00:11 +0200 |
User-agent: |
Mozilla/5.0 (X11; Linux x86_64; rv:91.0) Gecko/20100101 Thunderbird/91.7.0 |
Le 15/04/2022 à 22:41, Dimitris Marinakis a écrit :
Thank you, much better now :)
The only thing to improve here would be to have separate paddings for
each system. For some reason the staff-padding currently behaves more
like a fixed offset. If you have very high notes they get clipped.
Well, that's exactly the meaning of staff-padding -- the padding
from the staff symbol. The setting for the distance from note heads
and such is padding. I just didn't implement distancing from note
heads because I didn't know if I had to. I thought you'd be putting
the grob on its own in some Dynamics-like context. See below for
an implementation that has it; it's just one added line of code.
What are you using this spanner for, by the way?
I'm using 2.23.0. I can't use the latest versions for all of my
projects yet because I'm using a font that doesn't have the new
microtonal accidentals. I hope the creator of the font finds the time
to update it or I'll need to do it myself.
Besides, I have over 12K lines of code with custom stuff that some of
it will most likely break with the version change.
Ok, ok, the code below works in 2.23.0. I am surprised
by the font issue though. Is LilyPond preventing you from
using a font because it lacks some accidentals even if
you are not using these accidentals?
Jean
\version "2.23.0"
#(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* ((sys (ly:grob-system sib))
(tr (- (interval-start (car sib-exts-to-fill))
(ly:grob-relative-coordinate sib sys 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)
(Y . 0)))
(right . ((padding . 0)
(Y . 0)))))
(direction . ,DOWN)
(left-bound-info . ,ly:line-spanner::calc-left-bound-info)
(normalized-endpoints . ,ly:spanner::calc-normalized-endpoints)
(padding . 0.5)
(right-bound-info . ,ly: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))
(col (ly:context-property context 'currentMusicalColumn)))
(if (eqv? d LEFT)
(begin
(set! filler (ly:engraver-make-grob engraver
'SymbolFiller ev))
(ly:spanner-set-bound! filler LEFT col))
(begin
(ly:spanner-set-bound! filler RIGHT col)
(ly:engraver-announce-end-grob engraver filler ev))))))
(acknowledgers
((note-column-interface engraver grob source-engraver)
(if filler
(begin
(ly:pointer-group-interface::add-grob filler
'note-columns grob)
(ly:pointer-group-interface::add-grob filler
'side-support-elements 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
g1\startFiller #'(10 20) \markuplist { a b } 1\stopFiller
\break
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
}