Hi Urs,
On Mon, Sep 28, 2015 at 9:19 AM, Urs Liska <[email protected]> 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
[email protected]
https://lists.gnu.org/mailman/listinfo/lilypond-user