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: Thomas Morley
Subject: Re: Code pointer from end-BarLine to previous NoteHead.?
Date: Sun, 17 Jul 2022 01:25:57 +0200

Am Sa., 16. Juli 2022 um 23:12 Uhr schrieb Jean Abou Samra <jean@abou-samra.fr>:
>
> 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)
>

Thanks for your explanations, right now I had only a quick glance over them.
I'll hopefully have some time soon to dive into it...

Nevertheless, before I posted the problem I searched for an
'original'-BarLine-grob, as we have for broken spanners.
For (maybe broken) spanners I sometimes switch to that
'original'-grob, would it improve the situation if we had some
'original' for items as well?


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

Thanks for them!
Though, the posted engraver was heavily simplied, I didn't care about
foreseeable bugs or efficiency, it should demonstrate the probelm
nothing else :)
>
> > 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)

Below is the most interesting part.
I never thought about putting a selected grob into a grob-property of
a different one.
I'm aware we do so with context-properties...

>         (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.

Actually I need the behaviour above for end repeat bars.
The position of an starting repeat bar will rely on the staff-position
of the following rhythmic event.
And yes, I need it for single notes, event-chords and rests.
And ofcourse I need to decide what to do for multiple voices in same Staff.


Many thanks!!

Best,
  Harm



reply via email to

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