Using path expressions to override stencils
The stencil expression path can be used to override stencils for any printed object. The advantage of using path instead of embedded-ps is that path is supported by both the PostScript and SVG backends of LilyPond, and uses the same syntax.
There are six commands available to use in a path expression, and all commands use prefix notation. The six commands are moveto, rmoveto, lineto, rlineto, curveto, and rcurveto. Note that the commands that begin with r are the relative variants of the other three commands.
The commands moveto, rmoveto, lineto, and rlineto take two arguments; they are the X- and Y-coordinates for the destination point.
The commands curveto and rcurveto create cubic Bézier curves and take six arguments; the first two are the X- and Y-coordinates for the first control point, the second two are the X- and Y-coordinates for the second control point, and the last two are the X- and Y-coordinates for the destination point.
This snippet also shows how to create a ‘filled’ stencil using path, adding a Scheme function to automatically resize custom stencils when individual staves are resized.
Note that replacing the stencil of a grob doesn't change its dimensions. In case you experience strange spacing you have to adjust the X-extent and Y-extent properties. If you experience cropping, you might use the \with-true-dimensions trick shown below.
See also snippet Generate special note head shapes.
\version "2.24"
%{
Note: since SVG path data needs an "M" or "m" instruction at
the beginning to start a new subpath, every path expression
must begin with "rmoveto" (or "moveto") to work with the SVG
backend.
%}
customClefStencilOne =
#(ly:make-stencil
'(path 0.2
(rmoveto 0 0
rcurveto 0 0.75 1 0.75 1 0
rcurveto 0 -0.75 -1 -0.75 -1 0
rcurveto -1 0 -1 1.5 -0.5 1.5
rmoveto 0.5 -1.5
rcurveto -1 0 -1 -1.5 -0.5 -1.5
rmoveto 0.5 1.5
rmoveto 1 0
rcurveto 2.5 0 2.5 4 4 4
rmoveto -4 -4
rcurveto 2.5 0 2.5 -4 4 -4))
(cons -0.5 1)
(cons -3 5))
% A filled custom stencil.
customClefStencilTwo =
#(ly:make-stencil
;; Set path line thickness to 0.1.
'(path
;; Set path line thickness.
0.1
;; Path coordinates.
(moveto 0 0
curveto 0 1 0.7 2.5 1.5 1.5
lineto 1.5 -3
closepath)
;; Path cap style.
round
;; Path join style.
round
;; Is path filled? `#t` or `#f`.
#t)
;; Horizontal extent.
(cons 0 1.5)
;; Vertical extent.
(cons -3 2))
% A Scheme function to automatically resize a custom stencil
% when an individual staff is resized (uses `font-size`).
scaleCustomClefStencilTwo =
#(lambda (grob)
(let* ((sz (ly:grob-property grob 'font-size 0.0))
(mult (magstep sz)))
(ly:stencil-scale customClefStencilTwo mult mult)))
customClefOne = \override Staff.Clef.stencil = \customClefStencilOne
customClefTwo = \override Staff.Clef.stencil = \scaleCustomClefStencilTwo
normalClefs = \revert Staff.Clef.stencil
music = \relative c' {
\hide Staff.TimeSignature
\customClefOne
\clef "alto"
c1 g1
\customClefTwo
\clef "bass"
c1 g1
\normalClefs
\clef "alto"
c1 g1
\clef "bass"
c1 g1
}
\markup \with-true-dimensions % work around a cropping issue
\score {
<<
\new Staff \with {
fontSize = #-3
\override StaffSymbol.staff-space = #(magstep -3)
\override StaffSymbol.thickness = #(magstep -3)
} \music
\new Staff \music
\new Staff \with {
fontSize = #2
\override StaffSymbol.staff-space = #(magstep 2)
\override StaffSymbol.thickness = #(magstep 2)
} \music
>>
}