On 2019-12-21 9:15 am, Kieren MacMillan wrote:
Here is my attempt at hacking the BarLine stencil to add overlapping
ties:
This is so cool. Is there an easy way to make the function determine
the "current" set of LVs (i.e., the LV configuration from the most
recent LV-ed chord) and automagically apply it to the subsequent
barlines (e.g., with a \temporary, to be \reverted manually)?
Things get trickier if you need to keep track of past events. But here
is another approach that uses a Scheme engraver to do the bulk of the
work:
%%%%
\version "2.19.83"
#(begin
(set-object-property! 'activeLaissezVibrer 'translation-type? list?)
(set-object-property! 'activeLaissezVibrer 'translation-doc
"A list of LaissezVibrerTies which should span BarLines.")
(set! all-translation-properties
(cons 'activeLaissezVibrer all-translation-properties)))
#(define (SpanLaissezVibrer_engraver context)
(define (spanLaissezVibrer? grob)
(let* ((cause (ly:grob-property grob 'cause))
(class (ly:event-property cause 'class '()))
(span? (ly:event-property cause 'span #f)))
(and (memq 'laissez-vibrer-event class) span?)))
(define (past-events tie)
(ly:moment<? (car tie) (ly:context-now context)))
(define (rescale-control-points cp xex)
(let* ((xmin (apply min (map car cp)))
(xmax (apply max (map car cp)))
(xmid (/ (+ xmin xmax) 2))
(xhw (/ (- xmax xmin) 2)))
(map (lambda (pt) pt
(let ((t (/ (- (car pt) xmid) xhw)))
(cons (interval-index xex t) (cdr pt)))) cp)))
(define ((stencil-proc ties) grob)
(let* ((bd (ly:item-break-dir grob))
(orig (ly:bar-line::print grob))
(xex (ly:stencil-extent orig X))
(x (interval-index xex 0))
(w (+ 0.8 (/ (interval-length xex) 2)))
(tie-xex (cons (- x w) (+ x w))))
(ly:stencil-add (ly:bar-line::print grob)
(apply ly:stencil-add
(map (lambda (tie)
(let* ((cp-orig (ly:grob-property tie 'control-points))
(cp-temp (rescale-control-points cp-orig tie-xex))
(sten '()) (xex '()) (yex '()))
(ly:grob-set-property! tie 'control-points cp-temp)
(set! sten (ly:tie::print tie))
(ly:grob-set-property! tie 'control-points cp-orig)
(set! xex (ly:stencil-extent sten X))
(set! yex (ly:stencil-extent sten Y))
(ly:make-stencil (ly:stencil-expr sten)
(cons (car xex) (if (> 0 bd) x (cdr xex))) yex)))
(map cdr ties))))))
(make-engraver
(acknowledgers
((bar-line-interface engraver grob source-engraver)
(let ((ties (ly:context-property context
'activeLaissezVibrer '())))
(set! ties (filter past-events ties))
(if (not (null? ties)) (ly:grob-set-property!
grob 'stencil (stencil-proc ties)))))
((semi-tie-interface engraver grob source-engraver)
(if (spanLaissezVibrer? grob)
(ly:context-set-property! context 'activeLaissezVibrer
(cons (cons (ly:context-now context) grob)
(ly:context-property context
'activeLaissezVibrer '()))))))))
spanLaissezVibrer = #(make-music 'LaissezVibrerEvent 'span #t)
stopSpanLaissezVibrers = \applyContext #(lambda (context)
(ly:context-set-property! (ly:context-find context 'Staff)
'activeLaissezVibrer '()))
Upper = {
| r2 r8 g'8 b' c''
| <g' b' c''>1\spanLaissezVibrer
| R1 \stopSpanLaissezVibrers \bar "||"
| r8 a' g' e' c'2 \bar "|."
}
Lower = {
| <a, d>1\spanLaissezVibrer
| R1
| g8 b <g b>2.^\spanLaissezVibrer
| R1 \stopSpanLaissezVibrers
}
\score {
\new PianoStaff <<
\new Staff { \clef "treble" \Upper }
\new Staff { \clef "bass" \Lower }
>>
\layout { \context { \Staff
\consists \SpanLaissezVibrer_engraver } }
}
%%%%
This is quite different from my previous manual approach, but this has
advantages of exactly matching the vertical positioning of LV ties
without needing manual intervention.
-- Aaron Hill