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))
}
}
}