lilypond-user
[Top][All Lists]
Advanced

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

Re: Custom Spanner with variable length sections


From: Dimitris Marinakis
Subject: Re: Custom Spanner with variable length sections
Date: Fri, 15 Apr 2022 17:35:25 +0300

Thank you so much Jean. That looks amazing for a first try. Great work!

Sorry it took me a while to test it.

Upon testing I found out that it only works when there are line breaks (not on a single system).

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.

Best,
Dimitris

On Thu, Apr 14, 2022 at 7:44 PM Jean Abou Samra <jean@abou-samra.fr> wrote:
Le 14/04/2022 à 16:09, Kieren MacMillan a écrit :
> Hi Dimitris,
>
>> I need a custom spanner that has x sections with different symbols. This is going to be tricky so any help is appreciated.
> https://github.com/davidnalesnik/lilypond-text-spanner-inner-texts


Ok, I'm not sure if this solves the problem fully, so I'll post what I had
started writing anyway:


\version "2.22.2"

#(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))
           (siblings (ly:spanner-broken-into orig))
           (sib-widths
            (map (lambda (sib)
                   (let ((sys (ly:grob-system sib))
                         (left (ly:spanner-bound sib LEFT))
                         (right (ly:spanner-bound sib RIGHT)))
                     (- (ly:grob-relative-coordinate right sys X)
                        (ly:grob-relative-coordinate left sys X))))
                 siblings))
           (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.
            (if (eq? grob (car siblings))
               (set! retval sib-stil)
               (ly:grob-set-property! (car siblings)
                                      'stencil
                                      sib-stil))
            (set! sib-changes (cdr sib-changes))
            (set! siblings (cdr siblings))
            (set! sib-stil empty-stencil))
           ((> 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
    `((direction . ,DOWN)
      (normalized-endpoints . ,ly:spanner::calc-normalized-endpoints)
      (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))))))

#(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))))))
       ((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)

{
   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 1\stopFiller
}


Jean



reply via email to

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