Hi Urs,

On Mon, Sep 28, 2015 at 9:19 AM, Urs Liska <u...@openlilylib.org> wrote:

> Hi David,
>
> thank you very much!
>

You're welcome!


>
> Am 26.09.2015 um 01:25 schrieb David Nalesnik:
>
> Hi,
>
> ...
>
> To do what you want, it would be best of course to have a new grob.
>
> Defining new grobs has no user interface at the moment.  There is a
> regression text, input/regression/scheme-text-spanner.ly which does
> define one in an LY file.
>
> I modeled the attached after that.  I've got the dashed boxes.
>
>
> Did you do that *now* or have you pulled that out from some earlier work???
>

The regression test was originally written by Mike Solomon.

As far as boxes go, I've worked on them from time to time (and all but
given up).  I didn't base this on earlier stuff, partly because I didn't
have the patience to try to find the earlier files, and partly because I
didn't need to add very much to the model.  (Mostly, I just added a stencil
function and changed a few names.)


> I didn't tackle the bracket-with-inner-prong(s)--maybe someone would like
> to give it a go?
>
>
> What would also be very sufficient would be the same dashed box, only
> nested. However, I didn't manage to get that to work, even with additional
> helper voices.
> What will work for this time is the slur you can see in the image. I
> didn't get it to behave properly above the texts but it will do.
>

This is too late, unfortunately, but maybe you'll get some use out of the
attached.

The only way I could see to get nested boxes was to incorporate the idea of
'spanner-id.  I approximated your design by using a nested box with some
changed attributes.  The property 'box-faces is used to hide sides of the
box.  The 'box-dimension-offset property is used to give room for the line.

(Oh, I made \boxSpanStart and the like into post-events so they're like
\startTextSpan, etc.


>
> Very much so.
> The only thing still lacking for now is the box around the synchronuous
> items on the lyrics lines. The way to go for such a thing is probably to
> collect all syllables, then combine all objects at the same musical moment
> then draw boxes around them.
>

Yes.  You could do something like what I did here, but in this case an
engraver which creates an Item rather than a Spanner.

Best,
David

%%%%%%%%%%%%%%%%%%
\version "2.19.27"

%\include "lilypond-book-preamble.ly"

\paper {
  indent = 0\cm
}

%% CUSTOM GROB PROPERTIES

% Taken from http://www.mail-archive.com/lilypond-user%40gnu.org/msg97663.html
% (Paul Morris)

% function from "scm/define-grob-properties.scm" (modified)
#(define (cn-define-grob-property symbol type?)
   (set-object-property! symbol 'backend-type? type?)
   (set-object-property! symbol 'backend-doc "custom grob property")
   symbol)

% How much extra space to top, right, bottom, left?  If unspecified,
% this will be taken as '(1.0 1.0 -1.0 -1.0), creating a padding of 1 staff-space
% around box-contents.
#(cn-define-grob-property 'box-dimension-offset number-list?)

% Which faces will be drawn?  Pattern is top, right, bottom, left.  If
% unspecified, this will be taken as '(#t #t #t #t) -- meaning that all
% faces are visible.
#(cn-define-grob-property 'box-faces list?)

% Based on input/regression/scheme-text-spanner.ly

#(define-event-class 'box-span-event 'span-event)

#(define (add-grob-definition grob-name grob-entry)
   (let* ((meta-entry   (assoc-get 'meta grob-entry))
          (class        (assoc-get 'class meta-entry))
          (ifaces-entry (assoc-get 'interfaces meta-entry)))
     (set-object-property! grob-name 'translation-type? ly:grob-properties?)
     (set-object-property! grob-name 'is-grob? #t)
     (set! ifaces-entry (append (case class
                                  ((Item) '(item-interface))
                                  ((Spanner) '(spanner-interface))
                                  ((Paper_column) '((item-interface
                                                     paper-column-interface)))
                                  ((System) '((system-interface
                                               spanner-interface)))
                                  (else '(unknown-interface)))
                          ifaces-entry))
     (set! ifaces-entry (uniq-list (sort ifaces-entry symbol<?)))
     (set! ifaces-entry (cons 'grob-interface ifaces-entry))
     (set! meta-entry (assoc-set! meta-entry 'name grob-name))
     (set! meta-entry (assoc-set! meta-entry 'interfaces
                        ifaces-entry))
     (set! grob-entry (assoc-set! grob-entry 'meta meta-entry))
     (set! all-grob-descriptions
           (cons (cons grob-name grob-entry)
             all-grob-descriptions))))

#(define (box-it grob)
   (let ((texts (ly:grob-object grob 'texts)))
     (if (ly:grob-array? texts)
         (let* ((lb (ly:spanner-bound grob LEFT))
                (rb (ly:spanner-bound grob RIGHT))
                (common-x (ly:grob-common-refpoint lb rb X))
                (common-y (ly:grob-common-refpoint lb rb Y))
                (dimension-offset
                 (ly:grob-property grob 'box-dimension-offset
                   (list 1.0 1.0 -1.0 -1.0))) ; top/right/bottom/left
                (l-ext (ly:grob-extent lb common-x X))
                (l-ext (coord-translate l-ext (fourth dimension-offset)))
                (r-ext (ly:grob-extent rb common-x X))
                (r-ext (coord-translate r-ext (second dimension-offset)))
                (height (ly:relative-group-extent texts common-y Y))
                (height (coord-translate height
                          (cons (third dimension-offset)
                            (first dimension-offset))))
                (x-coord (ly:grob-relative-coordinate lb common-x X))
                (dh (interval-length height))
                (top (ly:line-interface::line
                      grob (car l-ext) dh (cdr r-ext) dh))
                (right (ly:line-interface::line
                        grob (cdr r-ext) 0.0 (cdr r-ext) dh))
                (bottom (ly:line-interface::line
                         grob (car l-ext) 0.0 (cdr r-ext) 0.0))

                (left (ly:line-interface::line
                       grob (car l-ext) 0.0 (car l-ext) dh))
                ;; by default, draw all faces
                (faces (ly:grob-property grob 'box-faces (list #t #t #t #t)))
                (stencils (list top right bottom left))
                ;; draw stencils based on booleans from 'box-faces property
                (stencils
                 (append-map (lambda (x y)
                               (if x (list y) '()))
                   faces stencils))
                (stil (apply ly:stencil-add stencils))
                ;; give our stencil zero dimension so that it can enfold texts
                (stil (ly:make-stencil
                       (ly:stencil-expr stil)
                       '(0 . 0) '(0 . 0)))
                (sp (ly:grob-property grob 'staff-padding))
                (radius (ly:staff-symbol-staff-radius grob))
                (offset-y (- (car height) radius sp))
                (stil (ly:stencil-translate stil
                        (cons (- x-coord) offset-y))))
           stil)
         empty-stencil)))

#(add-grob-definition
  'BoxTextSpanner
  `(
     (dash-fraction . 0.2)
     (dash-period . 3.0)
     (direction . ,UP)
     (staff-padding . 0.8)
     (stencil . ,box-it)
     (style . dashed-line)
     (meta . ((class . Spanner)
              (interfaces . (line-interface
                             line-spanner-interface
                             side-position-interface))))))

#(define scheme-event-spanner-types
   '(
      (BoxSpanEvent
       . ((description . "Used to signal where scheme text boxes
start and stop.")
          (types . (general-music box-span-event event span-event post-event))
          ))
      ))

#(set!
  scheme-event-spanner-types
  (map (lambda (x)
         (set-object-property! (car x)
           'music-description
           (cdr (assq 'description (cdr x))))
         (let ((lst (cdr x)))
           (set! lst (assoc-set! lst 'name (car x)))
           (set! lst (assq-remove! lst 'description))
           (hashq-set! music-name-to-property-table (car x) lst)
           (cons (car x) lst)))
    scheme-event-spanner-types))

#(set! music-descriptions
       (append scheme-event-spanner-types music-descriptions))

#(set! music-descriptions
       (sort music-descriptions alist<?))

#(define (add-bound-item spanner item)
   (if (null? (ly:spanner-bound spanner LEFT))
       (ly:spanner-set-bound! spanner LEFT item)
       (ly:spanner-set-bound! spanner RIGHT item)))

#(define (axis-offset-symbol axis)
   (if (eq? axis X) 'X-offset 'Y-offset))

#(define (set-axis! grob axis)
   (if (not (number? (ly:grob-property grob 'side-axis)))
       (begin
        (set! (ly:grob-property grob 'side-axis) axis)
        (ly:grob-chain-callback
         grob
         (if (eq? axis X)
             ly:side-position-interface::x-aligned-side
             side-position-interface::y-aligned-side)
         (axis-offset-symbol axis)))))

boxTextSpannerEngraver =
#(lambda (context)
   (let ((span '())
         (finished '())
         (event-start '())
         (event-stop '()))
     (make-engraver

      (listeners
       ((box-span-event engraver event)
        (if (= START (ly:event-property event 'span-direction))
            (set! event-start (cons event event-start))
            (set! event-stop (cons event event-stop)))))

      (acknowledgers
       ((text-script-interface engraver grob source-engraver)
        (for-each (lambda (s)
                    (ly:pointer-group-interface::add-grob
                     s 'texts grob)
                    (add-bound-item s grob))
          span)
        (for-each (lambda (f)
                    (ly:pointer-group-interface::add-grob
                     f 'texts grob)
                    (add-bound-item f grob))
          finished)))

      ((process-music trans)
       (for-each
        (lambda (es)
          (let ((es-id (ly:event-property es 'spanner-id)))
            (let loop ((sp span))
              (let ((sp-id (ly:event-property
                            (event-cause (car sp)) 'spanner-id)))
                (cond
                 ((null? sp) (ly:warning "No spanner to end!!"))
                 ((and
                   (string? sp-id)
                   (string? es-id)
                   (string=? sp-id es-id))
                  (set! finished (cons (car sp) finished))
                  (set! span (remove (lambda (s) (eq? s (car sp))) span)))
                 ((and
                   (null? sp-id)
                   (null? es-id))
                  (set! finished (cons (car sp) finished))
                  (set! span (remove (lambda (s) (eq? s (car sp))) span)))
                 (else (loop (cdr sp))))))))
        event-stop)

       (for-each
        (lambda (f)
          (ly:engraver-announce-end-grob trans f (event-cause f)))
        finished)

       (set! event-stop '())

       (for-each
        (lambda (es)
          (set! span
                (cons
                 (ly:engraver-make-grob trans 'BoxTextSpanner es)
                 span))
          (set-axis! (car span) Y))
        event-start)

       (set! event-start '()))

      ((stop-translation-timestep trans)
       (for-each
        (lambda (s)
          (if (null? (ly:spanner-bound s LEFT))
              (ly:spanner-set-bound! s LEFT)
              (ly:context-property context 'currentMusicalColumn)))
        span)

       (for-each
        (lambda (f)
          (if (null? (ly:spanner-bound f RIGHT))
              (ly:spanner-set-bound! f RIGHT
                (ly:context-property context 'currentMusicalColumn))))
        finished)

       (set! finished '()))

      ((finalize trans)
       (for-each
        (lambda (f)
          (if (null? (ly:spanner-bound f RIGHT))
              (ly:spanner-set-bound! f RIGHT
                (ly:context-property context 'currentMusicalColumn))))
        finished)
       (set! finished '())
       (for-each
        (lambda (sp)
          (ly:warning "incomplete spanner removed!")
          (ly:grob-suicide! sp))
        span)
       (set! span '())))))

boxSpanStart =
#(make-span-event 'BoxSpanEvent START)

boxSpanEnd =
#(make-span-event 'BoxSpanEvent STOP)

startBoxSpanOne =
#(make-music 'BoxSpanEvent 'span-direction START 'spanner-id "1")

stopBoxSpanOne =
#(make-music 'BoxSpanEvent 'span-direction STOP 'spanner-id "1")

startBoxSpanTwo =
#(make-music 'BoxSpanEvent 'span-direction START 'spanner-id "2")

stopBoxSpanTwo =
#(make-music 'BoxSpanEvent 'span-direction STOP 'spanner-id "2")


\layout {
  \context {
    \Global
    \grobdescriptions #all-grob-descriptions
  }
  \context {
    \Voice
    \consists \boxTextSpannerEngraver
  }
}

pieceTitle =
#(define-event-function (text) (markup?)
   #{
     -\tweak TextScript.self-alignment-X #LEFT
     -\tweak TextScript.self-alignment-Y #DOWN
     -\tweak TextScript.padding 3
     -\markup \rotate #90 #text
   #})

notes = \relative es' {
  \omit Staff.Stem
  \key as \major
  \time 3/4
  %% top/right/bottom/left
  %\once \override BoxTextSpanner.box-faces = #(list #f #t #f #t)
  es \boxSpanStart ^\pieceTitle "Pierrot"
  <g bes>
  es
  |
  <bes'\harmonic f'> ^\pieceTitle "Arlequin"

   s2\boxSpanEnd
  |
  bes4 ^\pieceTitle "Valse noble"
  g bes
  |
  \time 2/4
  %% left/right/bottom/top
  \once \override BoxTextSpanner.box-dimension-offset = #'(4 1 -1 -1)
   es,4 ^\pieceTitle "Eusebius"\boxSpanStart
  s4
  |
  \time 5/4
  <<
    {
       <bes' \harmonic f'>4 ^\pieceTitle "Florestan"
       -\tweak style ##f
       -\tweak thickness 2
       -\tweak box-faces #(list #t #f #f #f) \startBoxSpanOne
    }
    \new Voice {
      <g \harmonic d'>
    }
  >>
  s8 \boxSpanEnd
  bes4
  s8
  <g \harmonic d'>4
  s4
  \bar ";"
  |
  \time 3/4

  bes4 ^\pieceTitle "Coquette"
  s2
  \bar ";"
  |

  bes4 \stopBoxSpanOne ^\pieceTitle "Réplique"
  s8
  g4 s8
  \bar "||"
}

functionsOne = \lyricmode {
  \set stanza = \markup \circle "B:"
  S2.
  D2.
  T2.
  S2
}

functionsTwo = \lyricmode {
  \set stanza = \markup  \circle  "g:"
  \skip2.*3
  tG2
  \markup { D \hspace #-.8 \super 7 }4*5
  (tP)2.
  t2
}

\score {
  <<
    \new Staff {
      \omit Score.TimeSignature
      \notes
    }
    \new Lyrics \functionsOne
    \new Lyrics \functionsTwo
  >>
}
_______________________________________________
lilypond-user mailing list
lilypond-user@gnu.org
https://lists.gnu.org/mailman/listinfo/lilypond-user

Reply via email to