lilypond-user
[Top][All Lists]
Advanced

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

Re: Code pointer from end-BarLine to previous NoteHead.?


From: Jean Abou Samra
Subject: Re: Code pointer from end-BarLine to previous NoteHead.?
Date: Sat, 16 Jul 2022 23:12:18 +0200
User-agent: Mozilla/5.0 (X11; Linux x86_64; rv:91.0) Gecko/20100101 Thunderbird/91.11.0

Le 16/07/2022 à 20:47, Thomas Morley a écrit :
Hi Jean,
thanks for the hint.
Alas, the code below _ensures_ the NoteHead _before_ the BarLine is
taken (if I'm wrong here, I am completely lost), and again the
override for BarLine.Y-offset fails.


Ah, yes, sigh. This is caused by prebreak substitution.
You likely know about break substitution, and even if you
didn't know that term, you definitely know about the concept.

<https://extending-lilypond.readthedocs.io/en/latest/backend.html#grob-pointers>

(What I call) "prebreak substitution" is exactly the same,
but happening right after translation, _before_ line breaking.
It's done one items only, and uses break directions as the
criterion for replacing grobs instead of systems. Concretely,
the grob pointers in each item are scanned for other items;
let's assume a pointer to J is found in I. Then J is replaced
by its broken piece having the same break direction of I.
If it doesn't exist, the value is removed if it's in a grob
array, or otherwise replaced with *unspecified*.

I've never been a fan of this, TBH. For example, it doesn't
play so well with the actual break substitution. If item I
with break direction LEFT refers to J which is on the non-musical
column at a line break, then prebreak substitution will replace
J with its LEFT broken piece, but if I ends up on the system
just after the line break where J is located, then break
substitution will again replace that pointer, this time with
the RIGHT broken piece.

                                       End of line

                                       ||  <- NonMusicalPaperColumn
                                       ||
                                       J   <- Item J, LEFT version

-- line break

Beginning of line                      End of line
||                                     ||
||                                     ||
J, RIGHT version                       Item I, LEFT break dir, with pointer to J                                        => with prebreak substitution, pointer replaced                                           with LEFT J above (same break dir)                                        => with break substitution, replaced with
                                          RIGHT J (same system)




Maybe this is something to raise on the bug tracker.
Meanwhile, you can work around it by using a regular
property (ly:grob-property etc) instead of a pointer
(ly:grob-object etc).


Some code comments:

tst =
#(lambda (ctx)
   (let ((nhds '())
         (bar #f))
   (make-engraver
     (acknowledgers
       ((bar-line-interface engraver grob source-engraver)
         (set! bar grob))
       ((note-head-interface engraver grob source-engraver)
         (set! nhds (cons (cons (ly:context-current-moment ctx) grob) nhds))))
     ((stop-translation-timestep engraver)
       (let* ((curr (ly:context-current-moment ctx))
              (nhds-to-consider
                (remove
                  (lambda (x)
                    (equal? (car x) curr))
                  nhds))
              (sorted-nhds
                (reverse
                  (sort
                    nhds-to-consider
                    (lambda (x y)
                      (ly:moment<? (car x) (car y)))))))
     (if (and (ly:grob? bar) (pair? nhds))


(pair? sorted-nhds) rather. Otherwise, you have a bug where
you take the car of the empty list if nhds only contains note
heads from the current moment.


         (begin
           (ly:grob-set-property! (cdr (car sorted-nhds)) 'color red)
           (ly:grob-set-object! bar 'element (car sorted-nhds))
           (set! bar #f))))))))



The last line has a bug, the (set! ...) should be outside
the (if ...), or the bar line will take note heads after it
if there are only skips before it.

Furthermore, you have quadratic behavior because you sort the
whole list of note heads at every timestep. Overall, I'd write
this engraver more simply -- and efficiently -- as this:

\version "2.23.11"

tst =
#(lambda (ctx)
  (let ((nhd #f)
        (previous-nhd #f)
        (bar #f))
  (make-engraver
    (acknowledgers
      ((bar-line-interface engraver grob source-engraver)
        (set! bar grob))
      ((note-head-interface engraver grob source-engraver)
        (set! nhd grob)))
    ((stop-translation-timestep engraver)
     (when (and bar previous-nhd)
       ;; for debugging
       (ly:grob-set-property! previous-nhd 'color red)
       (set! (ly:grob-property bar 'details)
             (acons 'previous-note-head
                    previous-nhd
                    (ly:grob-property bar 'details))))
       (when nhd
       ;; Move the next line out of (when ...) if you only want the
       ;; note head from the time step right before, and not the last
       ;; note head seen before the bar line.
       (set! previous-nhd nhd)
       (set! nhd #f))
     (set! bar #f)))))

moveBarLineToPrevHead = {
  \override Staff.BarLine.Y-offset =
    #(lambda (grob)
      (let* ((prev-head (assq-ref (ly:grob-property grob 'details)
                                  'previous-note-head))
             (staff-pos (and prev-head (ly:grob-property prev-head 'staff-position))))
       (/ (or staff-pos 0) 2)))
}

\layout {
  \context {
      \Staff
      \consists \tst
      \moveBarLineToPrevHead
  }
}

\new Staff { s4 s \bar "." b s c' d' e' f' g' a' b' c'' \bar "|." }


As you can see, using regular properties cures the problem.

Of course, you'll need to adapt if you want to account for chords.

Best,
Jean




reply via email to

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