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