\version "2.19.19" #(define (align-to-interval victim target dir) "Align interval @var{victim} to interval @var{target} according to the value of @var{dir}. If @var{dir} is a number, use it as a reference for both intervals. If @var{dir} is a pair, use the @code{car} for the victim and the @code{cdr} for the target." (let ((v-dir (if (number? dir) dir (car dir))) (t-dir (if (number? dir) dir (cdr dir)))) (coord-translate victim (- (interval-index target t-dir) (interval-index victim v-dir))))) #(define (within-interval? point interval) "Does interval @var{interval} enclose number @var{num}?" (and (interval-sane? interval) (not (< point (car interval))) (not (> point (cdr interval))))) #(define (custom-align grob) (let* ((system (ly:grob-system grob)) (elements (ly:grob-array->list (ly:grob-object system 'elements))) (elements (filter grob::is-live? elements)) (text-grobs (filter (lambda (e) (grob::has-interface e 'system-start-text-interface)) elements)) ;; A delimiter is an initial barline, bracket, brace. (delims (filter (lambda (elt) (grob::has-interface elt 'system-start-delimiter-interface)) elements)) ;; To determine which delimiters affect our grob, we ;; unfortunately have to calculate its Y-position and ;; that of the delimiters! (text-grobs-delims-list (map (lambda (tg) (cons tg (list (remove (lambda (d) (not (within-interval? (ly:grob-property tg 'Y-offset) (ly:grob-extent d system Y)))) delims)))) text-grobs)) (delim-extent-list (lambda (delim-list) (map (lambda (d) (ly:grob-extent d system X)) delim-list))) ; Return a list of text grobs and their associated delimiter extents ; if we have no delimiters (as will happen with a single staff), use ; the left extent of the (unoffset) text grob itself as a basis. (text-grobs-delim-extents (map (lambda (tgdl) (cons (car tgdl) (if (null? (cadr tgdl)) (let ((me-ext (ly:grob-extent (car tgdl) system X))) (list (cons (car me-ext) (car me-ext)))) (delim-extent-list (cadr tgdl))))) text-grobs-delims-list)) ; combine extents (text-grobs-total-delim-extents (map (lambda (tgde) (cons (car tgde) (reduce interval-union '() (cdr tgde)))) text-grobs-delim-extents)) ; assumption? (representative-delim-extent (cdar text-grobs-total-delim-extents)) ; In order to calculate how much to move our name, we first need ; to construct a list of target positions for all grobs. (bar-line-width 0.1) ; Ugh. (target (map (lambda (tg) (let ((len (interval-length (ly:grob-extent tg system X)))) (cons tg (cons (- (cdr representative-delim-extent) bar-line-width) (+ (- (cdr representative-delim-extent) bar-line-width) len))))) text-grobs)) (target (map (lambda (re) (cons (car re) (coord-translate (cdr re) (ly:side-position-interface::x-aligned-side (car re))))) target)) (longest (car (sort target (lambda (x y) (> (interval-length (cdr x)) (interval-length (cdr y))))))) (target (map (lambda (x) (cons (car x) (align-to-interval ;align-interval-on-other (cdr x) (cdr longest) (ly:grob-property (car x) 'self-alignment-X 0)))) target)) ; determine overlaps of target positioning with delimiters (overlap (map (lambda (x y) (cons (car x) (interval-intersection (cdr x) (cdr y)))) target text-grobs-total-delim-extents)) (overlap (filter (lambda (x) (interval-sane? (cdr x))) overlap)) (largest-overlap (if (null? overlap) 0.0 (apply max (map (lambda (x) (interval-length (cdr x))) overlap)))) (right-padding (ly:grob-property grob 'padding 0.3)) (my-extent (ly:grob-extent grob system X)) ; X offset returned will displace grob's extent to match ; the target inc. any overlap with delimiters and padding. (X-offset (- (car (assoc-get grob target)) (car my-extent) largest-overlap right-padding))) X-offset)) %%%%%%%%%%%%% EXAMPLES %%%%%%%%%%%%%%% \paper { indent = 2\in short-indent = 1.5\in } music = \repeat unfold 30 { c''1 } othermusic = { \repeat unfold 20 { R1 } \repeat unfold 10 { c''1 } } \score { << \new Staff \with { instrumentName = "Flute" shortInstrumentName = "Abbreviation for Flute" } \music \new Staff \with { instrumentName = "Clarinet" shortInstrumentName = "Cl." } \othermusic \new StaffGroup << \new StaffGroup \with { systemStartDelimiter = #'SystemStartBrace } << \new Staff \with { instrumentName = "Instrumentissimo I" shortInstrumentName = "Insst. I"} \othermusic \new Staff \with { instrumentName = "Instrumentissimo II" shortInstrumentName = "Insst. II" } \othermusic >> \new Staff \with { instrumentName = "Cello" shortInstrumentName = "Vc."} \othermusic >> >> \layout { \context { \Score \override InstrumentName.X-offset = #custom-align %\override InstrumentName.padding = 0 } \context { \Staff \RemoveEmptyStaves \override VerticalAxisGroup.remove-first = ##t } } } \score { << \new Staff \with { instrumentName = \markup \center-column { "Instrumentissimo I" "Grande" } } c''1 \new Staff \with { instrumentName = "Instrumentissimo II" } c''1 \new StaffGroup << \new StaffGroup \with { systemStartDelimiter = #'SystemStartBrace } << \new Staff \with { instrumentName = "Flute" } c''1 \new Staff \with { instrumentName = "Clarinet" } c''1 >> \new Staff \with { instrumentName = "Cello" } c''1 >> >> \layout { \context { \Score \override InstrumentName.self-alignment-X = #LEFT \override InstrumentName.X-offset = #custom-align } } } \score { << \new Staff \with { instrumentName = \markup \center-column { "Instrumentissimo I" "Grande" } } c''1 \new Staff \with { instrumentName = "Instrumentissimo II" } c''1 \new StaffGroup << \new StaffGroup \with { systemStartDelimiter = #'SystemStartBrace } << \new Staff \with { instrumentName = "Flute" \override InstrumentName.self-alignment-X = #LEFT } c''1 \new Staff \with { instrumentName = "Clarinet" } c''1 >> \new Staff \with { instrumentName = "Cello" \override InstrumentName.self-alignment-X = #RIGHT } c''1 >> >> \layout { \context { \Score \override InstrumentName.X-offset = #custom-align \override InstrumentName.padding = 1 } } } \score { << \new Staff \with { instrumentName = "Inst. I" } c''1 >> \layout { \context { \Score \override InstrumentName.X-offset = #custom-align } } } %%%% Illustrates issue with padding: %%%%% \score { << \new Staff \with { instrumentName = "Soprano" shortInstrumentName = "Sop." } { c''1 \break c''1 } \new Staff \with { instrumentName = "Baritone" shortInstrumentName = "Bar." } { \clef bass c'1 \break c'1 \break c'1 } \new StaffGroup \with { instrumentName = "Piano" shortInstrumentName = "Pno." } << \new Staff { R1*2 c''1 } \new Staff { R1*2 c'1 } >> >> \layout { ragged-right = ##t \context { \Score \override InstrumentName.X-offset = #custom-align \override InstrumentName.self-alignment-X = #CENTER \override InstrumentName.padding = #0 %% try different values to see inconsistency } \context { \Staff \RemoveEmptyStaves \override VerticalAxisGroup.remove-first = ##t } } }