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