]> git.donarmstrong.com Git - lilypond.git/blob - Documentation/snippets/stem-cross-staff-engraver.ly
e04cae57b39b1adc24e054f352a44e511bcdde17
[lilypond.git] / Documentation / snippets / stem-cross-staff-engraver.ly
1 % DO NOT EDIT this file manually; it is automatically
2 % generated from Documentation/snippets/new
3 % Make any changes in Documentation/snippets/new/
4 % and then run scripts/auxiliar/makelsr.py
5 %
6 % This file is in the public domain.
7 %% Note: this file works from version 2.15.35
8 \version "2.15.35"
9
10 \header {
11   lsrtags = "staff-notation, tweaks-and-overrides, contexts-and-engravers"
12   texidoc = "This file defines and demonstrates a scheme engraver that
13 connects stems across staves.  The stem length need not be specified, as
14 the code takes care of the variable distance between noteheads and staves."
15   doctitle = "Stem cross staff engraver"
16 } % begin verbatim
17
18
19 %{
20   A new stem (referred to as span in the code) is created to connect the
21   original stems.  The original stems are made transparent.
22
23   The span is created as a child of the "root" stem, that is the stem
24   connected to a notehead with the end that is not to be extended.
25
26   Both stem directions are supported.  Connecting more than two stems is
27   possible.
28 %}
29
30 % Values are close enough to ignore the difference
31 #(define (close-enough? x y)
32    (< (abs (- x y)) 0.0001))
33
34 % Combine a list of extents
35 #(define (extent-combine extents)
36    (if (pair? (cdr extents))
37        (interval-union (car extents) (extent-combine (cdr extents)))
38        (car extents)))
39
40 % Check if the stem is connectable to the root
41 #(define ((stem-connectable? ref root) stem)
42    ; The root is always connectable to itself
43    (or (eq? root stem)
44        (and
45         ; Horizontal positions of the stems must be almost the same
46         (close-enough? (car (ly:grob-extent root ref X))
47           (car (ly:grob-extent stem ref X)))
48         ; The stem must be in the direction away from the root's notehead
49         (positive? (* (ly:grob-property root 'direction)
50                      (- (car (ly:grob-extent stem ref Y))
51                        (car (ly:grob-extent root ref Y))))))))
52
53 % Connect stems if we have at least one stems connectable to the root
54 #(define (stem-span-stencil span)
55    (let* ((system (ly:grob-system span))
56           (root (ly:grob-parent span X))
57           (stems (filter (stem-connectable? system root)
58                          (ly:grob-object span 'stems))))
59      (if (<= 2 (length stems))
60          (let* ((yextents (map (lambda (st)
61                                  (ly:grob-extent st system Y)) stems))
62                 (yextent (extent-combine yextents))
63                 (layout (ly:grob-layout root))
64                 (blot (ly:output-def-lookup layout 'blot-diameter)))
65            ; Hide spanned stems
66            (map (lambda (st)
67                   (set! (ly:grob-property st 'transparent) #t))
68              stems)
69            ; Draw a nice looking stem with rounded corners
70            (ly:round-filled-box (ly:grob-extent root root X) yextent blot))
71          ; Nothing to connect, don't draw the span
72          #f)))
73
74 % Create a stem span as a child of the cross-staff stem (the root)
75 #(define ((make-stem-span! stems trans) root)
76    (let ((span (ly:engraver-make-grob trans 'Stem '())))
77      (ly:grob-set-parent! span X root)
78      (set! (ly:grob-object span 'stems) stems)
79      ; Suppress positioning, the stem code is confused by this weird stem
80      (set! (ly:grob-property span 'X-offset) 0)
81      (set! (ly:grob-property span 'stencil) stem-span-stencil)))
82
83 % Set cross-staff property of the stem to this function to connect it to
84 % other stems automatically
85 #(define (cross-staff-connect stem)
86    #t)
87
88 % Check if automatic connecting of the stem was requested.  Stems connected
89 % to cross-staff beams are cross-staff, but they should not be connected to
90 % other stems just because of that.
91 #(define (stem-is-root? stem)
92    (eq? cross-staff-connect (ly:grob-property-data stem 'cross-staff)))
93
94 % Create stem spans for cross-staff stems
95 #(define (make-stem-spans! ctx stems trans)
96    ; Cannot do extensive checks here, just make sure there are at least
97    ; two stems at this musical moment
98    (if (<= 2 (length stems))
99        (let ((roots (filter stem-is-root? stems)))
100          (map (make-stem-span! stems trans) roots))))
101
102 % Connect cross-staff stems to the stems above in the system
103 #(define (Span_stem_engraver ctx)
104    (let ((stems '()))
105      (make-engraver
106       ; Record all stems for the given moment
107       (acknowledgers
108        ((stem-interface trans grob source)
109         (set! stems (cons grob stems))))
110       ; Process stems and reset the stem list to empty
111       ((process-acknowledged trans)
112        (make-stem-spans! ctx stems trans)
113        (set! stems '())))))
114
115 crossStaff =
116 #(define-music-function (parser location notes) (ly:music?) #{
117   \override Stem #'cross-staff = #cross-staff-connect
118   $notes
119   \revert Stem #'cross-staff
120 #})
121
122 \layout {
123   \context {
124     \StaffGroup
125     \consists #Span_stem_engraver
126   }
127 }
128
129 \parallelMusic #'(voiceA voiceB voiceC) {
130   % Bar 1 - durations, beams, flags
131   g'2 g'4 g'8 [ g'16 ] g'16 |
132   \crossStaff { c'2 c'4 c'8 [ c'16 ] c'16 } |
133   R1 |
134
135   % Bar 2 - direction
136   g'8 \stemDown g'8 \crossStaff g'8 \stemNeutral g'8 g'4 r4 |
137   \crossStaff { c'8 \stemDown c'8 } c'8 \stemNeutral c'8 r4 r4 |
138   c8 \stemDown c8 c8 \stemNeutral \crossStaff { c8 c4 c4 } |
139
140   % Bar 3 - multiple voice styles
141   << c''2 \\ \crossStaff d'2 \\ a'2 \\ \crossStaff f'2 >> g'2 |
142   << b'2 \\ c'2 \\ g'2 \\ e'2 >> << e'2 \\ \\ \crossStaff c'2 >> |
143   << \crossStaff b2 \\ c2 \\ \crossStaff g2 \\ e2 >> r2 |
144
145   % Bar 4 - grace notes
146   \grace g'8 a'2 \stemDown \crossStaff { \grace g'8 a'2 } \stemNeutral |
147   \grace c'8 d'2 \stemDown \grace c'8 d'2 \stemNeutral |
148   \crossStaff { \grace c8 d2 } \stemDown \grace c8 d2 \stemNeutral |
149
150   % Bar 5 - cross-staff beams
151   g'8 g'8 g'8 g'8 r2 |
152   s1 |
153   \crossStaff { c8 [ \change Staff=stafftwo c''8 ] }
154     \change Staff=staffthree c8 [ \change Staff=stafftwo c''8 ] r2 |
155 }
156
157 \score {
158   \new StaffGroup <<
159     \new Staff = "staffone" <<
160       \new Voice {
161         \autoBeamOff \voiceA
162       }
163     >>
164     \new Staff = "stafftwo" <<
165       \new Voice {
166         \autoBeamOff \voiceB
167       }
168     >>
169     \new Staff = "staffthree" <<
170       \new Voice {
171         \autoBeamOff \clef bass \voiceC
172       }
173     >>
174   >>
175   \layout { }
176 }