Hi,
On Sun, Apr 26, 2015 at 12:45 PM, Kieren MacMillan <
[email protected]> wrote:
> Hello all,
>
> In the snippet <http://lsr.di.unimi.it/LSR/Snippet?id=383>, the
> instrument names are centered [beautifully!] based on the total indent
> value.
>
> How would one make the instruments be centered in a block only the width
> of the longest instrument name (plus maybe a little padding), with the
> block right-aligned to the beginning of the score?
Attached please find my solution to this unexpectedly involved problem.
Best,
David
\version "2.19.17"
\paper {
indent = 2\in
short-indent = 3\in
}
#(define (center-interval-on-other-interval victim target)
(coord-translate victim
(- (interval-center target)
(interval-center victim))))
#(define (align-interval-on-other victim target dir)
(let ((center (center-interval-on-other-interval victim target)))
(coord-translate center (* (- (car center) (car target)) dir))))
#(define (custom-align grob)
(let* (;; get all InstumentName grobs
(system (ly:grob-system grob))
(elements (ly:grob-array->list
(ly:grob-object system 'elements)))
(text-grobs
(filter
(lambda (e) (grob::has-interface e 'system-start-text-interface))
elements))
;(align-x (ly:grob-property grob 'self-alignment-X 0))
(my-extent (ly:grob-extent grob system X))
;; A delimiter is an initial barline, bracket, brace.
(delims
(filter
(lambda (elt)
(grob::has-interface elt 'system-start-delimiter-interface))
elements))
;; There is no direct way to find out which delimiters affect
;; which InstrumentName grobs. We must compare the StaffSymbol
;; traversed by each delimiter with the StaffSymbol of each
;; InstrumentName.
(staff-lists
(map (lambda (d)
(cons d
(list (ly:grob-array->list
(ly:grob-object d 'elements)))))
delims))
; for a given text, what delimiters are to the right?
(text-grob-delim-to-right
(lambda (tg)
(let loop ((staff-lists staff-lists) (result '()))
(cond
((null? staff-lists) result)
((member (ly:grob-object tg 'staff-symbol)
(cadr (car staff-lists)))
(loop (cdr staff-lists)
(append result (list (caar staff-lists)))))
(else (loop (cdr staff-lists) result))))))
; return a list of text grobs and their delimiters
(text-grobs-delims-list
(map
(lambda (tg)
(cons tg (text-grob-delim-to-right tg)))
text-grobs))
; convert a list of delimiters to a list of extents
(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
(text-grobs-delim-extents
(map
(lambda (tgdl)
(cons (car tgdl)
(delim-extent-list (cdr tgdl))))
text-grobs-delims-list))
; combine extebts
(text-grobs-total-delim-extents
(map (lambda (tgde)
(cons (car tgde)
(reduce interval-union
; in case there is no delimiter
(ly:grob-extent (ly:grob-parent (car tgde) X) system X)
(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. Get it from rightmost delimiter.
(target
(map (lambda (tg)
(let ((len (interval-length (ly:grob-extent tg system X))))
(list 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
(cadr 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)
;(center-interval-on-other-interval (cdr x) (cdr longest))))
(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))
; 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))
music = \repeat unfold 20 { c''1 }
\score {
<<
\new Staff \with { instrumentName = "Flute"
shortInstrumentName = "Abbreviation for Flute" }
\music
\new Staff \with { instrumentName = "Clarinet" shortInstrumentName = "Cl." }
\music
\new StaffGroup
<<
\new StaffGroup \with { systemStartDelimiter = #'SystemStartBrace }
<<
\new Staff \with { instrumentName = "Instrumentissimo I"
shortInstrumentName = "Inst. I"}
\music
\new Staff \with { instrumentName = "Instrumentissimo II" }
\music
>>
\new Staff \with { instrumentName = "Cello" }
\music
>>
>>
\layout {
\context {
\Score
\override InstrumentName.X-offset = #custom-align
\override InstrumentName.padding = 1
}
}
}
\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
\override InstrumentName.padding = 2
}
}
}
_______________________________________________
lilypond-user mailing list
[email protected]
https://lists.gnu.org/mailman/listinfo/lilypond-user