Hi,

On Sun, Apr 26, 2015 at 12:45 PM, Kieren MacMillan <
kieren_macmil...@sympatico.ca> 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
lilypond-user@gnu.org
https://lists.gnu.org/mailman/listinfo/lilypond-user

Reply via email to