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: 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
}






reply via email to

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