Am Fr., 3. Okt. 2025 um 19:21 Uhr schrieb Thomas Morley
<[email protected]>:
>
> Am Fr., 3. Okt. 2025 um 19:08 Uhr schrieb Werner LEMBERG <[email protected]>:
> >
> >
> > > currently I'm trying to add crosses to the start and end of
> > > arbitrary Glissandi.  So far it works for unbroken and broken
> > > single-staff Glissando, as well as for unbroken cross-staff
> > > Glissando.  Alas, I'm stuck for broken cross-staff Glissando.
> >
> > I fear you are experiencing one of the many unresolved bugs related to
> > cross-staff grobs.  [...]
>
> I don't think so.
> Adding other stencils to Glissando.stencil (without modifying the
> original) works fine, even for broken cross-staff ones.
> Alas, I currently do not know a way to get the needed values for
> _placing_ those additional stencils, if the cross-staff Glissando is
> broken.
> In this case Glissando is bound at least on one side to
> NonMusicalPaperColumn. And, the different staves for start/end of the
> Glissando may come closer or not.
> There is some code in line-spanner.cc dealing with the problem.
> Though, with my lack of cc-knowledge I do not understand enough to
> redo it in scheme.
>
> Granted, there's https://gitlab.com/lilypond/lilypond/-/issues/6551
> but I'm not attempting page-breaks at the current state.
>
> Cheers,
>   Harm

After reading line-spanner.cc again and again I may have found the culprit.
See attached files. Previous attempts are still in there, but
commented. This may be of some use for others.

Cheers,
  Harm
\version "2.25.29"

\paper {
  ragged-right = ##f
  ragged-last = ##t
}

\layout {
  \context {
    \Voice
    %% For the sake of simplicity:
    \override Glissando.bound-details.left.padding = 0
    \override Glissando.bound-details.right.padding = 0
  }
}

%% cross stencil
#(define* (make-cross-stencil coords #:optional (thick 0.1) (sz 0.4))
  "Print a x-cross-stencil at @var{coords} supposed to be a number pair."
  (ly:stencil-add
    (make-line-stencil
      thick
      (- (car coords) sz)
      (- (cdr coords) sz)
      (+ (car coords) sz)
      (+ (cdr coords) sz))
    (make-line-stencil
      thick
      (- (car coords) sz)
      (+ (cdr coords) sz)
      (+ (car coords) sz)
      (- (cdr coords) sz))))

glissandoX =
#(define-music-function (proc)(procedure?)
#{
  \override Glissando.stencil =
    #(grob-transformer 'stencil
      (lambda (grob orig)
        (apply ly:stencil-add orig
          (map
            (lambda (pt) (stencil-with-color (make-cross-stencil pt) red))
            (proc grob)))))
#})

oneStaffMusic = {
  d''2\glissando c' r d''\glissando
  \break
  s c'
  \bar "|."
}

crossStaffMusic =
  \new PianoStaff
    <<
      \new Staff = "top" { f''2\glissando \change Staff = "bottom" c, }
      \new Staff = "bottom" { \clef "bass" s1 \bar "|." }
    >>

crossStaffMusicBroken =
  \new PianoStaff
    <<
      \new Staff = "top" {
         \clef alto
         a''2\glissando \change Staff = "bottom" c,
         \change Staff = "top"
         a''1\glissando
         \break
         \change Staff = "bottom"
         \override NoteColumn.glissando-skip = ##t
         s2 c,
         \break
         s2
         \revert NoteColumn.glissando-skip c,
      }
      \new Staff = "bottom" { \clef "bass" s1*4 \bar "|." }
    >>
%{
%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Attempt 1
%%%%%%%%%%%%%%%%%%%%%%%%%%

#(define (gliss-pts-1 grob)
  (let* ((left-bound-info (ly:grob-property grob 'left-bound-info))
         (right-bound-info (ly:grob-property grob 'right-bound-info))
         (X-left-coord (ly:assoc-get 'X left-bound-info 0))
         (Y-left-coord (ly:assoc-get 'Y left-bound-info 0))
         (X-right-coord (ly:assoc-get 'X right-bound-info 0))
         (Y-right-coord (ly:assoc-get 'Y right-bound-info 0)))

    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;; Coords are relative to the System grob, we want them relative to the
    ;; Glissando grob. Modifies X-coords.
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    (let* ((sys (ly:grob-system grob))
           (gliss-sys-x (ly:grob-relative-coordinate grob sys X)))
      (set! X-left-coord (- X-left-coord gliss-sys-x))
      (set! X-right-coord (- X-right-coord gliss-sys-x)))

    (list
      (cons X-left-coord Y-left-coord)
      (cons X-right-coord Y-right-coord))))

%% Attempt 1 example
\markup \box \fill-line {
  \column {
    "\\gliss-pts-1 is fine for simple Glissando"
    "It fails for broken ones"
  }
  \null
}

\score {
  \oneStaffMusic
  \layout { \context { \Voice \glissandoX #gliss-pts-1 } }
}
%}
%{
%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Attempt 2
%%%%%%%%%%%%%%%%%%%%%%%%%%

#(define (gliss-pts-2 grob)
  (let* ((left-bound-info (ly:grob-property grob 'left-bound-info))
         (right-bound-info (ly:grob-property grob 'right-bound-info))
         (X-left-coord (ly:assoc-get 'X left-bound-info 0))
         (Y-left-coord (ly:assoc-get 'Y left-bound-info 0))
         (X-right-coord (ly:assoc-get 'X right-bound-info 0))
         (Y-right-coord (ly:assoc-get 'Y right-bound-info 0)))

    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;; Coords are relative to the System grob, we want them relative to the
    ;; Glissando grob. Modifies X-coords.
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    (let* ((sys (ly:grob-system grob))
           (gliss-sys-x (ly:grob-relative-coordinate grob sys X)))
      (set! X-left-coord (- X-left-coord gliss-sys-x))
      (set! X-right-coord (- X-right-coord gliss-sys-x)))

    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;; Take normalized-endpoints into account. Modifies Y-coords.
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    (let ((y-length (- Y-right-coord Y-left-coord))
          (normalized-endpoints
            (ly:grob-property grob 'normalized-endpoints '(0 . 1))))
      (set! Y-left-coord
            (+ Y-left-coord (* (car normalized-endpoints) y-length)))
      (set! Y-right-coord
            (- Y-right-coord (* (- 1 (cdr normalized-endpoints)) y-length))))

    (list
      (cons X-left-coord Y-left-coord)
      (cons X-right-coord Y-right-coord))))

%% Attempt 2 examples
\markup \box \fill-line {
  \column {
    "\\gliss-pts-2 is fine for simple and broken Glissando."
    "It fails if cross-staff."
  }
  \null
}

\score {
  \oneStaffMusic
  \layout { \context { \Voice \glissandoX #gliss-pts-2 } }
}

\score {
  \crossStaffMusic
  \layout { \context { \Voice\glissandoX #gliss-pts-2 } }
}
%}
%{
%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Attempt 3
%%%%%%%%%%%%%%%%%%%%%%%%%%

#(define (gliss-pts-3 grob)
  (let* ((left-bound-info (ly:grob-property grob 'left-bound-info))
         (right-bound-info (ly:grob-property grob 'right-bound-info))
         (X-left-coord (ly:assoc-get 'X left-bound-info 0))
         (Y-left-coord (ly:assoc-get 'Y left-bound-info 0))
         (X-right-coord (ly:assoc-get 'X right-bound-info 0))
         (Y-right-coord (ly:assoc-get 'Y right-bound-info 0)))

    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;; Coords are relative to the System grob, we want them relative to the
    ;; Glissando grob. Modifies X-coords.
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    (let* ((sys (ly:grob-system grob))
           (gliss-sys-x (ly:grob-relative-coordinate grob sys X)))
      (set! X-left-coord (- X-left-coord gliss-sys-x))
      (set! X-right-coord (- X-right-coord gliss-sys-x)))

    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;; Take normalized-endpoints into account. Modifies Y-coords.
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    (let ((y-length (- Y-right-coord Y-left-coord))
          (normalized-endpoints
            (ly:grob-property grob 'normalized-endpoints '(0 . 1))))
      (set! Y-left-coord
            (+ Y-left-coord (* (car normalized-endpoints) y-length)))
      (set! Y-right-coord
            (- Y-right-coord (* (- 1 (cdr normalized-endpoints)) y-length))))

    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;; Attempt for cross-staff Glissando
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    (let ((cross-staff? (ly:grob-property grob 'cross-staff #f)))
      (when cross-staff?
        (let* ((val
                (ly:grob-common-refpoint
                 (ly:spanner-bound grob LEFT)
                 (ly:spanner-bound grob RIGHT) Y))
               (bound-right (ly:spanner-bound grob RIGHT))
               (bound-left (ly:spanner-bound grob LEFT))
               (bounds-delta-Y
                 (- (ly:grob-relative-coordinate bound-right val Y)
                    (ly:grob-relative-coordinate bound-left val Y))))
           (set! Y-right-coord (+ bounds-delta-Y Y-left-coord)))))

    (list
      (cons X-left-coord Y-left-coord)
      (cons X-right-coord Y-right-coord))))

%% Attempt 3 examples
\markup \box \fill-line {
  \column {
    "\\gliss-pts-3 is fine for simple and broken Glissando and unbroken
cross-staff."
    "It fails for broken cross-staff."
  }
  \null
}

\score {
  \oneStaffMusic
  \layout { \context { \Voice \glissandoX #gliss-pts-3 } }
}

\score {
  \crossStaffMusicBroken
  \layout { \context { \Voice\glissandoX #gliss-pts-3 } }
}
%}
%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Attempt 4
%%%%%%%%%%%%%%%%%%%%%%%%%%

#(define (gliss-pts-4 grob)
  (let* ((left-bound-info (ly:grob-property grob 'left-bound-info))
         (right-bound-info (ly:grob-property grob 'right-bound-info))
         (X-left-coord (ly:assoc-get 'X left-bound-info 0))
         (Y-left-coord (ly:assoc-get 'Y left-bound-info 0))
         (X-right-coord (ly:assoc-get 'X right-bound-info 0))
         (Y-right-coord (ly:assoc-get 'Y right-bound-info 0)))

    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;; Coords are relative to the System grob, we want them relative to the
    ;; Glissando grob. Modifies X-coords.
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    (let* ((sys (ly:grob-system grob))
           (gliss-sys-x (ly:grob-relative-coordinate grob sys X)))
      (set! X-left-coord (- X-left-coord gliss-sys-x))
      (set! X-right-coord (- X-right-coord gliss-sys-x)))

    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;; For cross-staff Glissando, take the distance of their bounds left/right
    ;; VerticalAxisGroup into account. Modifies Y-coords.
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    (let ((cross-staff? (ly:grob-property grob 'cross-staff #f)))
      (when cross-staff?
        (let* (;; Get left/right `common-Y`, i.e. the relevant VerticalAxisGroup
               ;; grobs, and calculate their Y-coordinates.
               ;; Move `Y-right-coord` by their difference.
               ;; See also code and comments for `Line_spanner::calc_bound_info`
               ;; in line-spanner.cc
               ;; `Y-left-coord` moves along.
               (vag-left (assoc-get 'common-Y left-bound-info))
               (vag-right (assoc-get 'common-Y right-bound-info))
               (vags-refp (ly:grob-common-refpoint vag-left vag-right Y))
               (vag-left-Y (ly:grob-relative-coordinate vag-left vags-refp Y))
               (vag-right-Y (ly:grob-relative-coordinate vag-right vags-refp Y))
               (vags-delta-Y (- vag-right-Y vag-left-Y)))
          (set! Y-right-coord (+ Y-right-coord vags-delta-Y)))))

    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;; Take normalized-endpoints into account. Modifies Y-coords.
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    (let ((y-length (- Y-right-coord Y-left-coord))
          (normalized-endpoints
            (ly:grob-property grob 'normalized-endpoints '(0 . 1))))
      (set! Y-left-coord
            (+ Y-left-coord (* (car normalized-endpoints) y-length)))
      (set! Y-right-coord
            (- Y-right-coord (* (- 1 (cdr normalized-endpoints)) y-length))))

    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;; Return the final result.
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    (list
      (cons X-left-coord Y-left-coord)
      (cons X-right-coord Y-right-coord))))

%% Attempt 4 examples
\markup \box \fill-line {
  \column {
    "\\gliss-pts-4 is fine for every tested Glissando: single-staff, cross-staff,
broken and unbroken."
  }
  \null
}

\score {
  \oneStaffMusic
  \layout { \context { \Voice \glissandoX #gliss-pts-4 } }
}

\score {
  \crossStaffMusicBroken
  \layout {
      \context { \Voice\glissandoX #gliss-pts-4 }
      \context {
        \PianoStaff
        \override StaffGrouper.staff-staff-spacing =
        #'((minimum-distance . 0)
           (basic-distance . 0)
           (padding . 5)
           (stretchability . 0))
      }
  }
}



Reply via email to