Re: bug in magnetic snapping lyrics engraver

2022-04-06 Thread Werner LEMBERG

> Here is an example where it fails to position lyrics correctly (see
> last system in the image).

And while we are at it: Here is another bug, already reported in

  https://lists.gnu.org/archive/html/lilypond-user/2020-03/msg00289.html

I've slightly sharpened the test to exclude the possibility of kerning
being the cause of the (buggy) result.


Werner
\version "2.20.0"

\include "magnetic-lyrics.ily"

\new Lyrics \lyricmode {
  "|foobar|" "|foobar|" \break
  foo -- "|bar|" "|foobar|"
}

\layout {
  \context {
\Lyrics
\override LyricWord.after-line-breaking = #(lyric-word-compressor 0.5)
  }

  \context {
\Score
\remove "Bar_number_engraver"
  }
}

\paper {
  indent = 0
  ragged-right = ##t
  system-system-spacing.basic-distance = 0
  system-system-spacing.minimum-distance = 0
  system-system-spacing.padding = 0
}


Re: bug in magnetic snapping lyrics engraver

2022-04-06 Thread Jean Abou Samra

Le 06/04/2022 à 14:33, Werner LEMBERG a écrit :

Is someone taking care of bugs in the magnetic snapping lyrics
engraver?  Here is an example where it fails to position lyrics
correctly (see last system in the image).


 Werner


PS: I've attached a version of `magnetic-lyrics.ily` that actually
 works with the current development version (using an updated
 `add-grob-definition` routine).




To be honest, I don't know why the snippet is so complicated.
I'd just have done:

\version "2.23.7"

#(define (Left_hyphen_pointer_engraver context)
   (let ((hyphen #f)
 (text #f))
 (make-engraver
  (acknowledgers
   ((lyric-syllable-interface engraver grob source-engraver)
  (set! text grob)))
  (end-acknowledgers
   ((lyric-hyphen-interface engraver grob source-engraver)
  (when (not (grob::has-interface grob 'lyric-space-interface))
(set! hyphen grob
  ((stop-translation-timestep engraver)
 (when (and text hyphen)
   (ly:grob-set-object! text 'left-hyphen hyphen))
 (set! text #f)
 (set! hyphen #f)

#(define (lyric-text::apply-magnetic-offset! grob)
   (let ((hyphen (ly:grob-object grob 'left-hyphen #f)))
 (when hyphen
   (let* ((left-text (ly:spanner-bound hyphen LEFT))
  (common (ly:grob-common-refpoint grob left-text X))
  (my-x-ext (ly:grob-extent grob common X))
  (left-x-ext (begin
;; Trigger magnetism for left-text
(ly:grob-property left-text 'after-line-breaking)
(ly:grob-extent left-text common X)))
  (delta (- (interval-start my-x-ext)
(interval-end left-x-ext)))
  (details (ly:grob-property grob 'details))
  (threshold (assoc-get 'squash-threshold details 0.2)))
 (when (< delta threshold)
   (ly:grob-translate-axis! grob
(- delta)
X))

#(define (lyric-hyphen::displace-bounds-first grob)
   ;; Make very sure this callback isn't triggered too early.
   (let ((left (ly:spanner-bound grob LEFT))
 (right (ly:spanner-bound grob RIGHT)))
 (ly:grob-property left 'after-line-breaking)
 (ly:grob-property right 'after-line-breaking)
 (ly:lyric-hyphen::print grob)))

\layout {
  \context {
\Lyrics
\consists #Left_hyphen_pointer_engraver
\override LyricText.after-line-breaking = 
#lyric-text::apply-magnetic-offset!
\override LyricHyphen.stencil = #lyric-hyphen::displace-bounds-first
\override LyricText.details.squash-threshold = 0.7
\override LyricHyphen.minimum-distance = 0
\override LyricHyphen.minimum-length = 0
\override LyricSpace.minimum-distance = 1
  }
}
   


<<
  \new Voice = "foo" \relative c' {
\repeat unfold 16 { a8 b a2 a8 b }
  }
  \new Lyrics \lyricsto "foo" {
\repeat unfold 10 { foo }
\repeat unfold 10 { foo -- \markup \caps bar }
\repeat unfold 10 { \markup \bold syl -- la -- ble }
a \markup \with-color #red ran -- \markup \box dom string of mo -- no -- 
syl -- la -- bic
and mul -- ti -- \markup \fontsize #5 syl -- la -- bic
\markup \bold \underline ver -- \markup \italic bi -- age
\markup {
  \stencil #(make-circle-stencil 0.5 0 #f)
}
  }





Which seems to work, and doesn't have your first bug.

Did I miss something?

The second bug is super hard to fix: when you tell LilyPond about
spacing constraints between lyrics, before line breaking, you need
to assume a lyric syllable won't be moved to the left by 'magnetism',
because if you do, the spacer might think it can space its right neighbor
more to the left due to the space left by moving the syllable to the
left, and if the compression cannot actually be done, it will collide.
But when the compression does get done, and the room is left empty,
horizontal spacing has already been done and it is too late to tell
the right neighbor that it would have room on its left. Am I making
sense?


Jean




Re: bug in magnetic snapping lyrics engraver

2022-04-06 Thread Kieren MacMillan
Hi Jean,

> To be honest, I don't know why the snippet is so complicated.

Perhaps a corollary to Clarke's Law?
“Every sufficiently primitive technology is indistinguishable from garbage.”  ;)
[No offence to the wonderful coders who wrote the existing snippet! Just a 
funny observation re: Clarke.]

> I'd just have done:

I was the main squeaky wheel for whom this grease was originally created.
I'll test out your version and see if it solves all the use-cases I had/have.

Cheers,
Kieren.


Re: bug in magnetic snapping lyrics engraver

2022-04-06 Thread Jean Abou Samra

Le 06/04/2022 à 20:17, Jean Abou Samra a écrit :

Did I miss something?


Whoops, big problem at line breaks ...

\version "2.23.7"

#(define (Left_hyphen_pointer_engraver context)
   (let ((hyphen #f)
 (text #f))
 (make-engraver
  (acknowledgers
   ((lyric-syllable-interface engraver grob source-engraver)
  (set! text grob)))
  (end-acknowledgers
   ((lyric-hyphen-interface engraver grob source-engraver)
  (when (not (grob::has-interface grob 'lyric-space-interface))
    (set! hyphen grob
  ((stop-translation-timestep engraver)
 (when (and text hyphen)
   (ly:grob-set-object! text 'left-hyphen hyphen))
 (set! text #f)
 (set! hyphen #f)

#(define (lyric-text::apply-magnetic-offset! grob)
   (let ((hyphen (ly:grob-object grob 'left-hyphen #f)))
 (when hyphen
   (let ((left-text (ly:spanner-bound hyphen LEFT)))
 (when (grob::has-interface left-text 'lyric-syllable-interface)
   (let* ((common (ly:grob-common-refpoint grob left-text X))
  (my-x-ext (ly:grob-extent grob common X))
  (left-x-ext (begin
    ;; Trigger magnetism for left-text
    (ly:grob-property left-text 
'after-line-breaking)

    (ly:grob-extent left-text common X)))
  (delta (- (interval-start my-x-ext)
    (interval-end left-x-ext)))
  (details (ly:grob-property grob 'details))
  (threshold (assoc-get 'squash-threshold details 0.2)))
 (when (< delta threshold)
   (ly:grob-translate-axis! grob
    (- delta)
    X

#(define (lyric-hyphen::displace-bounds-first grob)
   ;; Make very sure this callback isn't triggered too early.
   (let ((left (ly:spanner-bound grob LEFT))
 (right (ly:spanner-bound grob RIGHT)))
 (ly:grob-property left 'after-line-breaking)
 (ly:grob-property right 'after-line-breaking)
 (ly:lyric-hyphen::print grob)))

\layout {
  \context {
    \Lyrics
    \consists #Left_hyphen_pointer_engraver
    \override LyricText.after-line-breaking = 
#lyric-text::apply-magnetic-offset!

    \override LyricHyphen.stencil = #lyric-hyphen::displace-bounds-first
    \override LyricText.details.squash-threshold = 10
    \override LyricHyphen.minimum-distance = 0
    \override LyricHyphen.minimum-length = 0.2
    \override LyricSpace.minimum-distance = 1
  }
}




Re: bug in magnetic snapping lyrics engraver

2022-04-07 Thread Werner LEMBERG


> The second bug is super hard to fix: when you tell LilyPond about
> spacing constraints between lyrics, before line breaking, you need
> to assume a lyric syllable won't be moved to the left by
> 'magnetism', because if you do, the spacer might think it can space
> its right neighbor more to the left due to the space left by moving
> the syllable to the left, and if the compression cannot actually be
> done, it will collide.  But when the compression does get done, and
> the room is left empty, horizontal spacing has already been done and
> it is too late to tell the right neighbor that it would have room on
> its left.  Am I making sense?

This makes sense, thanks!


Werner



Re: bug in magnetic snapping lyrics engraver

2022-04-07 Thread Werner LEMBERG
>> Did I miss something?
> 
> Whoops, big problem at line breaks ...

This is ingenious :-) Thanks *a lot* for this solution.  In my tests
it seems to work fine.

Two comments.

* I *strongly* vote for polishing and documenting this so that it can
  be added to LilyPond.  It's an invaluable feature IMHO, greatly
  enhancing Lyrics.

* You have redefined the `LyricHyphen` stencil.  AFAICS, this change
  could be directly integrated into `ly:lyric-hyphen::print`, right?
  Doing so would allow users to change the stencil by themselves if
  necessary.  Otherwise I vote for introducing a property
  `hyphen-formatter` for this job.


Werner



Re: bug in magnetic snapping lyrics engraver

2022-04-07 Thread Jean Abou Samra

Le 07/04/2022 à 11:44, Werner LEMBERG a écrit :

Did I miss something?

Whoops, big problem at line breaks ...

This is ingenious :-) Thanks *a lot* for this solution.  In my tests
it seems to work fine



You're welcome.



Two comments.

* I *strongly* vote for polishing and documenting this so that it can
   be added to LilyPond.  It's an invaluable feature IMHO, greatly
   enhancing Lyrics.




Well, I'm afraid you can't 'vote' for someone else doing volunteer
work :-) Right now, I'm struggling to get my work on break substitution
out, which has been brewing for four months. After that ... I have
a list of LilyPond TODO items. Currently the length of that list is 24 :-)

So I can enqueue it on the list, but don't expect me to work on it
anytime soon. On the other hand, by all means feel free to work yourself
on polishing and integrating it :-)



* You have redefined the `LyricHyphen` stencil.  AFAICS, this change
   could be directly integrated into `ly:lyric-hyphen::print`, right?
   Doing so would allow users to change the stencil by themselves if
   necessary.  Otherwise I vote for introducing a property
   `hyphen-formatter` for this job.



Yes, it can. That said, it remains sort of a crutch. Although there are
not many solutions in current LilyPond (and my approach is not an outlier
in the code base either), I'm not very happy with this use of 
after-line-breaking.

Without trying right now, I think I can easily find edge cases involving
ly:side-position-interface::move-to-extremal-staff (with bar numbers /
rehearsal marks / tempo marks / volta brackets) that would break the snippet
without this stencil override. Having it means catering for edge cases in
drawing the stencil at least, but I have not investigated whether there are
currently any X-extent callbacks that could manage to trigger 
outside-staff positioning
for example, which could make for odd spacing. In short: I think it 
should work
OK in non-esoteric cases, although I would have to look more closely to 
be sure.



Jean



Re: bug in magnetic snapping lyrics engraver

2022-04-07 Thread Kieren MacMillan
Hi Werner (et al.),

> This is ingenious :-) Thanks *a lot* for this solution.

+1 (x2)

> * I *strongly* vote for polishing and documenting this so that it can
>  be added to LilyPond.  It's an invaluable feature IMHO, greatly
>  enhancing Lyrics.

YES PLEASE!!

If anyone is interested in discussing one potentially related request — also 
having to do with spacing — I'd appreciate it. It may not have any impact on 
this solution, but my instinct is it might, so I wanted to throw it out there.

Consider the following snippet:

%%%  SNIPPET BEGINS
\version "2.23.4"

\layout {
  indent = 0
}

music = {
  \tuplet 3/2 { c'8 8 8 }
}

words = \lyricmode {
  \tag #'fix { \once \override LyricText.self-alignment-X = #0.25 } Loong! 
Not long.
}

\score {
  <<
\new Staff \music
\addlyrics \words
  >>
  \layout {
ragged-right = ##f
line-width = 2.5\in
  }
}

\score {
  <<
\new Staff \removeWithTag #'fix { \music }
\addlyrics \words
  >>
}

\score {
  <<
\new Staff \keepWithTag #'fix { \music }
\addlyrics \words
  >>
  \layout {
ragged-right = ##f
line-width = 2\in
  }
}
%%%  SNIPPET ENDS

A *large* percentage of my tweaking time is spent adjusting the alignment of 
lyrics so that the note spacing is as natural as possible: aligning the first 
syllable of a triplet more to the right and the last syllable more to the left, 
etc. I would love to be able to identify a block of notes (anything from a pair 
to a whole measure or more!) so that the spacing engine would privilege the 
note spacing in that group over the spacing of the lyrics under it, compressing 
or expanding LyricWords [hence the intersection with this feature!], changing 
self-alignment-X or X-offset, etc., in order to keep the notes the most natural.

Janek started to work on this as part of the GSoC Lyric work (10 years ago, 
maybe?!?!), but never finished. It's obviously a non-trivial feature to 
implement… but I wanted to bring it up just in case the potential 
implementation of it in the future might change the way that LyricWord is 
implemented right now.

Thoughts?
Kieren




Re: bug in magnetic snapping lyrics engraver

2022-04-07 Thread Carl Sorensen
On Thu, Apr 7, 2022 at 10:04 AM Kieren MacMillan <
kie...@kierenmacmillan.info> wrote:

> Hi Werner (et al.),
>
> > This is ingenious :-) Thanks *a lot* for this solution.
>
> +1 (x2)
>
> > * I *strongly* vote for polishing and documenting this so that it can
> >  be added to LilyPond.  It's an invaluable feature IMHO, greatly
> >  enhancing Lyrics.
>
> YES PLEASE!!
>
>
Kieren, this might be a place for you to make your first commit, rather
than trying to get Charles's work in.  This is a much smaller piece of code
to polish and document.

Of course, I can't decide for you, but I thought it might be worth your
consideration.

Thanks,

Carl


Re: bug in magnetic snapping lyrics engraver

2022-04-13 Thread Werner LEMBERG


>> This is ingenious :-) Thanks *a lot* for this solution.  In my
>> tests it seems to work fine
>
> You're welcome.

I spoke too soon :-) There is a problem with ligatures at the syllable
boundaries (and kerning).  While this can be considered an ugliness in
languages based on the latin script, it might be a real showstopper
for non-latin scripts like Devanagari.

In other words, it is not sufficient to shift the right syllable in a
syllable pair to the left.  Instead, the following should be done.

* Concatenate the text/markup of the left syllable with the right
  text/markup *on the input level* (for example, by using
  `make-concat-markup`) so that Pango/Cairo can handle all affected
  syllables together.

* Store the result in the right syllable and adjust the horizontal
  position.  This allows accumulation for multi-syllable words.

* Set the text/markup in the left syllable to empty.

I tried to implement this, but I failed.  In this replacement code

```
(when (< delta threshold)
  (let ((lt (ly:grob-property left-text 'text))
(rt (ly:grob-property grob 'text)))
(begin
  (ly:grob-set-property!
   left-text 'text empty-markup)
  (ly:grob-set-property!
   grob 'text (make-concat-markup (list lt rt)))
```

the `ly:grob-set-property!` calls have zero effect.  I guess that at
this point of time it is too late for modifying properties, and the
grobs have already been converted to stencils.

Maybe this is the reason for the more complicated approach in David's
original approach?  Do you know a quick fix for your code?


Werner



Re: bug in magnetic snapping lyrics engraver

2022-04-14 Thread Werner LEMBERG


> There is a problem with ligatures at the syllable boundaries (and
> kerning).  [...]  it is not sufficient to shift the right syllable
> in a syllable pair to the left.  Instead, the following should be
> done.  [...]

Meanwhile I could implement this :-)

I will submit a MR soon.


Werner



Re: bug in magnetic snapping lyrics engraver

2022-04-14 Thread Jean Abou Samra

Le 14/04/2022 à 17:39, Werner LEMBERG a écrit :

There is a problem with ligatures at the syllable boundaries (and
kerning).  [...]  it is not sufficient to shift the right syllable
in a syllable pair to the left.  Instead, the following should be
done.  [...]

Meanwhile I could implement this :-)

I will submit a MR soon.



How did you do that? Sorry to rain on your parade, but
I would not want you to put a lot of work in it if it
will not be of mergeable quality.

Jean





Re: bug in magnetic snapping lyrics engraver

2022-04-14 Thread Werner LEMBERG

> How did you do that?  Sorry to rain on your parade, but I would not
> want you to put a lot of work in it if it will not be of mergeable
> quality.

Attached.  In the end the necessary modifications were surprisingly
minor.  Please comment, there is certainly room for improvements.


Werner

diff --git a/lily/lyric-hyphen.cc b/lily/lyric-hyphen.cc
index 23cd76bd94..897de7d7f7 100644
--- a/lily/lyric-hyphen.cc
+++ b/lily/lyric-hyphen.cc
@@ -48,6 +48,10 @@ Lyric_hyphen::print (SCM smob)
   && !from_scm (get_property (me, "after-line-breaking"
 return SCM_EOL;
 
+  // Ensure that bounds are displaced first.
+  (void) get_property (bounds[LEFT], "after-line-breaking");
+  (void) get_property (bounds[RIGHT], "after-line-breaking");
+
   Grob *common = bounds[LEFT]->common_refpoint (bounds[RIGHT], X_AXIS);
 
   Interval span_points;
diff --git a/scm/scheme-engravers.scm b/scm/scheme-engravers.scm
index 07e3be3691..5131beef1f 100644
--- a/scm/scheme-engravers.scm
+++ b/scm/scheme-engravers.scm
@@ -943,6 +943,101 @@ Engraver to print a line between two @code{Fingering} grobs.")))
(description . "Create repeat counts within lyrics for modern
 transcriptions of Gregorian chant.")))
 
+(define (Left_hyphen_pointer_engraver context)
+  (let ((hyphen #f)
+(text #f))
+(make-engraver
+ (acknowledgers
+  ((lyric-syllable-interface engraver grob source-engraver)
+   (set! text grob)))
+ (end-acknowledgers
+  ((lyric-hyphen-interface engraver grob source-engraver)
+   (when (not (grob::has-interface grob 'lyric-space-interface))
+ (set! hyphen grob
+ ((stop-translation-timestep engraver)
+  (when (and text hyphen)
+(ly:grob-set-object! text 'left-hyphen hyphen))
+  (set! text #f)
+  (set! hyphen #f)
+
+(define-public (lyric-text::apply-magnetic-offset! grob)
+  "If the space between two syllables is less than the value in
+property @code{LyricText@/.details@/.squash-threshold}, move the right
+syllable to the left so that it gets concatenated with the left
+syllable.
+
+Use this function as a hook for
+@code{LyricText@/.after-@/line-@/breaking} if the
+@code{Left_@/hyphen_@/pointer_@/engraver} is active."
+  (let ((hyphen (ly:grob-object grob 'left-hyphen #f)))
+(when hyphen
+  (let ((left-text (ly:spanner-bound hyphen LEFT)))
+(when (grob::has-interface left-text 'lyric-syllable-interface)
+  (let* ((common (ly:grob-common-refpoint grob left-text X))
+ (this-x-ext (ly:grob-extent grob common X))
+ (left-x-ext
+  (begin
+;; Trigger magnetism for left-text.
+(ly:grob-property left-text 'after-line-breaking)
+(ly:grob-extent left-text common X)))
+ ;; `delta` is the gap width between two syllables.
+ (delta (- (interval-start this-x-ext)
+   (interval-end left-x-ext)))
+ (details (ly:grob-property grob 'details))
+ (threshold (assoc-get 'squash-threshold details 0.2)))
+(when (< delta threshold)
+  (let* (;; We have to manipulate the input text so that
+ ;; ligatures crossing syllable boundaries are not
+ ;; disabled.  For languages based on the Latin
+ ;; script this is essentially a beautification.
+ ;; However, for non-Western scripts it can be a
+ ;; necessity.
+ (lt (ly:grob-property left-text 'text))
+ (rt (ly:grob-property grob 'text))
+ ;; Append new syllable.
+ (ltrt (if (and (string? lt) (string? rt))
+   (string-append lt rt)
+   (make-concat-markup (list lt rt
+ ;; Right-align `ltrt` to the right side.
+ (markup (grob-interpret-markup
+  grob
+  (make-translate-markup
+   (cons (interval-length this-x-ext) 0)
+   (make-right-align-markup ltrt)
+(begin
+  ;; Don't print `left-text`.
+  (ly:grob-set-property! left-text 'stencil #f)
+  ;; Set text and stencil (which holds all collected
+  ;; syllables so far) and shift it to the left.
+  (ly:grob-set-property! grob 'text ltrt)
+  (ly:grob-set-property! grob 'stencil markup)
+  (ly:grob-translate-axis! grob (- delta) X))
+
+(ly:register-translator
+ Left_hyphen_pointer_engraver 'Left_hyphen_pointer_engraver
+ '((grobs-created . ())
+   (events-accepted . ())
+   (properties-read . ())
+   (properties-written . ())
+   (description . "\
+Collect syllable-hyphen-syllable occurrences in lyrics and store them

Re: bug in magnetic snapping lyrics engraver

2022-04-14 Thread Alexander Kobel
Hi all,

On 4/6/22 20:17, Jean Abou Samra wrote:
> Le 06/04/2022 à 14:33, Werner LEMBERG a écrit :
>> Is someone taking care of bugs in the magnetic snapping lyrics
>> engraver?  Here is an example where it fails to position lyrics
>> correctly (see last system in the image).
>>
>>
>>  Werner
>>
>>
>> PS: I've attached a version of `magnetic-lyrics.ily` that actually
>>  works with the current development version (using an updated
>>  `add-grob-definition` routine).
> 
> 
> 
> To be honest, I don't know why the snippet is so complicated.
> I'd just have done:

One of the reasons might be that all participants have been 8 years younger and 
less experienced back then, and *some* later touches (at least mine) have been 
made by the trusted techniques of shotgun debugging and interpreter 
error-driven (rather than test-driven) development. And folks like Harm only 
had so much chance to tidy up my mess. ;-)

My literally last exposure to Lilypond was meeting you folks in Jan 2020 in 
Salzburg, and I'll need to dig out my old tests, but I'll give it a shot; 
having this integrated would be such an amazing improvement.


When it comes to LyricHyphen handling, some pieces in this thread might be 
relevant as well:
https://mail.gnu.org/archive/html/lilypond-user/2016-12/msg00361.html

Unfortunately IMHO, Knut's work on automatic LyricExtenders has never reached 
its prime time...


Cheers,
Alex


smime.p7s
Description: S/MIME Cryptographic Signature


Re: bug in magnetic snapping lyrics engraver

2022-04-14 Thread Jean Abou Samra

Le 14/04/2022 à 18:18, Werner LEMBERG a écrit :

How did you do that?  Sorry to rain on your parade, but I would not
want you to put a lot of work in it if it will not be of mergeable
quality.

Attached.  In the end the necessary modifications were surprisingly
minor.  Please comment, there is certainly room for improvements.



Thanks. This is a bit what I feared: in this state, there is
potential for things going wrong with other after-line-breaking
callbacks. Those are the kind of thing that can be buggy in obscure
circumstances. I'm a little nervous on having my "off-the-top" /
"first thing that seemed to work" solution in the code base :-)

OTOH, it's *probably* not too difficult to refactor this to use
a separate 'original-text property and a 'text property computed
by a callback, with X-offset computed only after-line-breaking,
X-extent left empty and springs-and-rods doing the magic that
would otherwise be done by Separation_item::set_distance, at the
condition of letting Grob::extent skip the X-offset calculation
in case the extent is found to be empty (which could be a good
idea regardless actually). Or something like that. Too bad that
we don't have pure widths (we could if I finally managed to finish
a big patch that has been pending for about a year), but it should
be possible to do without I think. I think. But right now, I'm a
bit swamped due to a somewhat important exam next Thursday.


[Alex; glad to meet you BTW]

My literally last exposure to Lilypond was meeting you folks in Jan 2020 in 
Salzburg,



Well, not me ;-) (I knew little about LilyPond at that time
and had not started to contribute.)



and I'll need to dig out my old tests, but I'll give it a shot; having this 
integrated would be such an amazing improvement.



If you guys are ready to clean up this thing, that would be a relief
for me given the amount of other contributions that I am trying to
get around to. I know I was a bit elliptic above (too tired ATM),
but I am happy to provide guidance and discuss solutions.


Jean




Re: bug in magnetic snapping lyrics engraver

2022-04-14 Thread Werner LEMBERG

>> Attached.  In the end the necessary modifications were surprisingly
>> minor.  Please comment, there is certainly room for improvements.
> 
> Thanks. This is a bit what I feared: in this state, there is
> potential for things going wrong with other after-line-breaking
> callbacks.  Those are the kind of thing that can be buggy in obscure
> circumstances.  I'm a little nervous on having my "off-the-top" /
> "first thing that seemed to work" solution in the code base :-)

I think your code is very clear and easy to understand.  My extensions
aren't complicated either.  It also works nicely and flawless in
combination with the new `VaticanaLyrics` context.  So please be more
specific about your fears.  You told us that you are working on the
breaking algorithm since a few months, so I guess you are the best
person to voice objections and give counterexamples.

Kieren, can you test this, too?

We should probably continue the discussion on lilypond-devel, since it
seems to get bloody technical. :-)

> OTOH, it's *probably* not too difficult to refactor this to use a
> separate 'original-text property and a 'text property computed by a
> callback, with X-offset computed only after-line-breaking, X-extent
> left empty and springs-and-rods doing the magic that would otherwise
> be done by Separation_item::set_distance, at the condition of
> letting Grob::extent skip the X-offset calculation in case the
> extent is found to be empty (which could be a good idea regardless
> actually).

This is way too complicated for me, sorry.

> Or something like that. Too bad that we don't have pure widths (we
> could if I finally managed to finish a big patch that has been
> pending for about a year), but it should be possible to do without I
> think. I think.

Well, I fear I can't help you with the coding if you think this is the
best route to go.  But as usual: The better is the enemy of the
good :-)

> But right now, I'm a bit swamped due to a somewhat important exam
> next Thursday.

I wish you success!

> If you guys are ready to clean up this thing, that would be a relief
> for me given the amount of other contributions that I am trying to
> get around to.

Define 'clean up'.  AFAICS, what you have outlined above is not a
cleanup but a complete reimplementation based on a different
algorithm...


Werner


Re: bug in magnetic snapping lyrics engraver

2022-04-14 Thread Werner LEMBERG

>> How did you do that?  Sorry to rain on your parade, but I would not
>> want you to put a lot of work in it if it will not be of mergeable
>> quality.
> 
> Attached.  In the end the necessary modifications were surprisingly
> minor.  Please comment, there is certainly room for improvements.

Attached is another version for testing without the need to patch
LilyPond.


Werner
% magnetic-lyrics.ily
%
%   written by
% Jean Abou Samra 
% Werner Lemberg 
%
% Version 2022-Apr-15

\version "2.23.7"

#(define (Left_hyphen_pointer_engraver context)
   "Collect syllable-hyphen-syllable occurrences in lyrics and store
them in properties.  This engraver only looks to the left.  For
example, if the lyrics input is @code{foo -- bar}, it does the
following.

@itemize @bullet
@item
Set the @code{text} property of the @code{LyricHyphen} grob between
@q{foo} and @q{bar} to @code{foo}.

@item
Set the @code{left-hyphen} property of the @code{LyricText} grob with
text @q{foo} to the @code{LyricHyphen} grob between @q{foo} and
@q{bar}.
@end itemize

Use this auxiliary engraver in combination with the
@code{lyric-@/text::@/apply-@/magnetic-@/offset!} hook."
   (let ((hyphen #f)
 (text #f))
 (make-engraver
  (acknowledgers
   ((lyric-syllable-interface engraver grob source-engraver)
(set! text grob)))
  (end-acknowledgers
   ((lyric-hyphen-interface engraver grob source-engraver)
(when (not (grob::has-interface grob 'lyric-space-interface))
  (set! hyphen grob
  ((stop-translation-timestep engraver)
   (when (and text hyphen)
 (ly:grob-set-object! text 'left-hyphen hyphen))
   (set! text #f)
   (set! hyphen #f)

#(define (lyric-text::apply-magnetic-offset! grob)
   "If the space between two syllables is less than the value in
property @code{LyricText@/.details@/.squash-threshold}, move the right
syllable to the left so that it gets concatenated with the left
syllable.

Use this function as a hook for
@code{LyricText@/.after-@/line-@/breaking} if the
@code{Left_@/hyphen_@/pointer_@/engraver} is active."
   (let ((hyphen (ly:grob-object grob 'left-hyphen #f)))
 (when hyphen
   (let ((left-text (ly:spanner-bound hyphen LEFT)))
 (when (grob::has-interface left-text 'lyric-syllable-interface)
   (let* ((common (ly:grob-common-refpoint grob left-text X))
  (this-x-ext (ly:grob-extent grob common X))
  (left-x-ext
   (begin
 ;; Trigger magnetism for left-text.
 (ly:grob-property left-text 'after-line-breaking)
 (ly:grob-extent left-text common X)))
  ;; `delta` is the gap width between two syllables.
  (delta (- (interval-start this-x-ext)
(interval-end left-x-ext)))
  (details (ly:grob-property grob 'details))
  (threshold (assoc-get 'squash-threshold details 0.2)))
 (when (< delta threshold)
   (let* (;; We have to manipulate the input text so that
  ;; ligatures crossing syllable boundaries are not
  ;; disabled.  For languages based on the Latin
  ;; script this is essentially a beautification.
  ;; However, for non-Western scripts it can be a
  ;; necessity.
  (lt (ly:grob-property left-text 'text))
  (rt (ly:grob-property grob 'text))
  ;; Append new syllable.
  (ltrt (if (and (string? lt) (string? rt))
(string-append lt rt)
(make-concat-markup (list lt rt
  ;; Right-align `ltrt` to the right side.
  (markup (grob-interpret-markup
   grob
   (make-translate-markup
(cons (interval-length this-x-ext) 0)
(make-right-align-markup ltrt)
 (begin
   ;; Don't print `left-text`.
   (ly:grob-set-property! left-text 'stencil #f)
   ;; Set text and stencil (which holds all collected
   ;; syllables so far) and shift it to the left.
   (ly:grob-set-property! grob 'text ltrt)
   (ly:grob-set-property! grob 'stencil markup)
   (ly:grob-translate-axis! grob (- delta) X))


#(define (lyric-hyphen::displace-bounds-first grob)
   ;; Make very sure this callback isn't triggered too early.
   (let ((left (ly:spanner-bound grob LEFT))
 (right (ly:spanner-bound grob RIGHT)))
 (ly:grob-property left 'after-line-breaking)
 (ly:grob-property right 'after-line-breaking)
 (ly:lyric-hyphen::print grob)))


\layout {
  \context {
\Lyrics
\consists #Left_h