From: Mark Polesky Date: Tue, 15 Jul 2014 19:34:15 +0000 (-0700) Subject: Issue 4015: Add \magnifyStaff. X-Git-Tag: release/2.19.11-1~26 X-Git-Url: https://git.donarmstrong.com/?a=commitdiff_plain;h=38a17c6133689ec42e6b8ecca925be59f3b78962;p=lilypond.git Issue 4015: Add \magnifyStaff. * Add \magnifyStaff. * Rewrite parts of the \magnifyMusic function so that it can share some scheme code with the new \magnifyStaff function. * Add regtests. * Update documentation. --- diff --git a/Documentation/essay/engraving.itely b/Documentation/essay/engraving.itely index 5040f2771b..5136a331e0 100644 --- a/Documentation/essay/engraving.itely +++ b/Documentation/essay/engraving.itely @@ -447,13 +447,9 @@ magnified by 236% to print at the same size as the previous example: At smaller sizes, LilyPond uses proportionally heavier lines so the music will still read well. -@ignore This also allows staves of different sizes to coexist peacefully when used together on the same page: -@c TODO: are the stems in this example the right thickness? How should -@c line weights be scaled for small staves? - @c Grieg's Violin Sonata Op. 45 @lilypond[indent=1.5cm] global = { @@ -464,9 +460,7 @@ global = { \score { << \new Staff \with { - fontSize = #-4 - \override StaffSymbol.staff-space = #(magstep -4) - \override StaffSymbol.thickness = #(magstep -3) + \magnifyStaff #2/3 } \relative c' { \global @@ -503,7 +497,7 @@ global = { >> } @end lilypond -@end ignore + @node Why work so hard? @unnumberedsubsec Why work so hard? diff --git a/Documentation/music-glossary.tely b/Documentation/music-glossary.tely index a58b49971b..056d6b0064 100644 --- a/Documentation/music-glossary.tely +++ b/Documentation/music-glossary.tely @@ -3618,8 +3618,7 @@ played above the bass notes. @lilypond[quote,line-width=13.0\cm] \new GrandStaff << \new Staff = "rh" \with { - fontSize = #-3 - \override StaffSymbol #'staff-space = #(magstep -3) + \magnifyStaff #2/3 } \relative c'' { \clef treble \key es \major diff --git a/Documentation/notation/spacing.itely b/Documentation/notation/spacing.itely index dd8ef02293..c88f6b2c1b 100644 --- a/Documentation/notation/spacing.itely +++ b/Documentation/notation/spacing.itely @@ -1283,24 +1283,17 @@ block: @end example @item -To set the staff size for a single staff within a system, set the -staff's font-size and staff-space using units relative to the -score's default staff size. Using relative units prevents the -proportion of staff sizes from being altered if the default staff -size is modified with @code{set-global-staff-size} or -@code{layout-set-staff-size}. - -For example, traditionally engraved chamber music scores with -piano often used 7mm piano staves while the other staves were up -to 5/7 as large (if space allowed), or down to 3/5 as large (if -space was cramped). To achieve the 5/7 proportion, use: +To set the staff size for a single staff within a system, use the +@code{\magnifyStaff} command. For example, traditionally engraved +chamber music scores with piano often used 7mm piano staves while +the other staves were typically between 3/5 and 5/7 as large +(between 60% and 71%). To achieve the 5/7 proportion, use: @example \score @{ << \new Staff \with @{ - fontSize = #(magnification->font-size 5/7) - \override StaffSymbol.staff-space = #5/7 + \magnifyStaff #5/7 @} @{ @dots{} @} \new PianoStaff @{ @dots{} @} >> @@ -1314,8 +1307,7 @@ could use the following form: \score @{ << \new Staff \with @{ - fontSize = -3 - \override StaffSymbol.staff-space = #(magstep -3) + \magnifyStaff #(magstep -3) @} @{ @dots{} @} \new PianoStaff @{ @dots{} @} >> diff --git a/Documentation/notation/staff.itely b/Documentation/notation/staff.itely index 0fa4f35fac..d2bb608d32 100644 --- a/Documentation/notation/staff.itely +++ b/Documentation/notation/staff.itely @@ -608,9 +608,7 @@ only a few ossia staves are needed. \new Staff \with { \remove "Time_signature_engraver" alignAboveContext = #"main" - fontSize = #-3 - \override StaffSymbol.staff-space = #(magstep -3) - \override StaffSymbol.thickness = #(magstep -3) + \magnifyStaff #2/3 firstClef = ##f } { e4 d f e } @@ -632,9 +630,7 @@ example. \new Staff = "ossia" \with { \remove "Time_signature_engraver" \hide Clef - fontSize = #-3 - \override StaffSymbol.staff-space = #(magstep -3) - \override StaffSymbol.thickness = #(magstep -3) + \magnifyStaff #2/3 } { \stopStaff s1*6 } @@ -671,9 +667,7 @@ break. For more information about \new Staff = "ossia" \with { \remove "Time_signature_engraver" \hide Clef - fontSize = #-3 - \override StaffSymbol.staff-space = #(magstep -3) - \override StaffSymbol.thickness = #(magstep -3) + \magnifyStaff #2/3 } \relative c'' { R1*3 c4 e8 d c2 diff --git a/input/regression/magnifyStaff-bar-lines.ly b/input/regression/magnifyStaff-bar-lines.ly new file mode 100644 index 0000000000..4bc357e817 --- /dev/null +++ b/input/regression/magnifyStaff-bar-lines.ly @@ -0,0 +1,33 @@ +\version "2.19.11" + +\header { + texidoc = "Bar line thickness and spacing should be scaled along +with notation size when using the @code{\magnifyStaff} command." +} + +\paper { + score-system-spacing = #'((padding . 4)) +} + +example = +#(define-music-function (parser location mag) (positive?) + #{ + \new Staff \with { + \magnifyStaff #mag + instrumentName = \markup { + \fontsize #(+ 3 (- (magnification->font-size mag))) + #(format #f "~,2f" mag) + } + } { + \omit Staff.Clef + \omit Staff.TimeSignature + s4 \bar "|" + s4 \bar ":|.|:" + s4 \bar ":|.S.|:" + s4 \bar "|." + } + #}) + +\example 0.50 +\example 1.00 +\example 2.00 diff --git a/input/regression/magnifyStaff-dots-beamlets.ly b/input/regression/magnifyStaff-dots-beamlets.ly new file mode 100644 index 0000000000..e4dd1f7a5b --- /dev/null +++ b/input/regression/magnifyStaff-dots-beamlets.ly @@ -0,0 +1,21 @@ +\version "2.19.11" + +\header { + texidoc = "Dot size and beamlet length should be scaled along +with notation size when using the @code{\magnifyStaff} command." +} + +music = { a'8.[ a'16] } +{ + \magnifyStaff 0.50 \music \music \bar "|" + \magnifyStaff 0.71 \music \bar "|" + \magnifyStaff 1.00 \music \bar "|" + \magnifyStaff 1.41 \music \bar "|" + \magnifyStaff 2.00 \music \bar "|" +} \addlyrics { + "0.50 " _ _ _ + "0.71 " _ + "1.00 " _ + "1.41 " _ + "2.00 " _ +} diff --git a/input/regression/magnifyStaff-space-alist.ly b/input/regression/magnifyStaff-space-alist.ly new file mode 100644 index 0000000000..561ac9e72a --- /dev/null +++ b/input/regression/magnifyStaff-space-alist.ly @@ -0,0 +1,38 @@ +\version "2.19.11" + +\header { + texidoc = "@code{space-alist} values should be scaled along +with notation size when using the @code{\magnifyStaff} command." +} + +\paper { + indent = 0 + ragged-right = ##t + system-system-spacing = #'((padding . 3)) + score-markup-spacing = #'((padding . 6)) +} + +example = +#(define-music-function (parser location mag) (positive?) + #{ + \new Staff \with { + \magnifyStaff #mag + \consists "Custos_engraver" + \override Custos.style = #'mensural + } \new Voice \with { + \consists "Ambitus_engraver" + } { + \omit Score.BarNumber + \key d \major + d''2 \breathe d'' | + \break + g'1 | + \clef treble + \key c \major + g'1 | + } + #}) + +\markup "0.50:" \example 0.50 +\markup "1.00:" \example 1.00 +\markup "2.00:" \example 2.00 diff --git a/input/regression/magnifyStaff-staff-line-thickness.ly b/input/regression/magnifyStaff-staff-line-thickness.ly new file mode 100644 index 0000000000..4ff9568d55 --- /dev/null +++ b/input/regression/magnifyStaff-staff-line-thickness.ly @@ -0,0 +1,21 @@ +\version "2.19.11" + +\header { + texidoc = "Staff line thickness should be scaled along with +staff size when using the @code{\magnifyStaff} command. Staff +lines can get thicker than the default, but not thinner." +} + +{ + \magnifyStaff 0.50 b'1 + \magnifyStaff 0.71 b'1 + \magnifyStaff 1.00 b'1 + \magnifyStaff 1.41 b'1 + \magnifyStaff 2.00 b'1 +} \addlyrics { + " 0.50 " + " 0.71 " + " 1.00 " + " 1.41 " + " 2.00 " +} diff --git a/ly/music-functions-init.ly b/ly/music-functions-init.ly index b5eb7feb0f..65b4a6db7a 100644 --- a/ly/music-functions-init.ly +++ b/ly/music-functions-init.ly @@ -681,9 +681,9 @@ slurs, ties, and horizontal spacing are adjusted automatically.") \context Bottom { %% TODO: uncomment \newSpacingSection once Issue 3990 is fixed %\newSpacingSection - #(scale-fontSize mag) - #(scale-props unshrinkable-props mag #f) - #(scale-props shrinkable-props mag #t) + #(scale-fontSize 'magnifyMusic mag) + #(scale-props 'magnifyMusic mag #f unshrinkable-props) + #(scale-props 'magnifyMusic mag #t shrinkable-props) #(scale-beam-thickness mag) #music @@ -691,13 +691,63 @@ slurs, ties, and horizontal spacing are adjusted automatically.") %% TODO: uncomment \newSpacingSection once Issue 3990 is fixed %\newSpacingSection %% reverse engineer the former fontSize value instead of using \unset - #(revert-fontSize mag) - #(revert-props (append unshrinkable-props - shrinkable-props - '((Beam beam-thickness)))) + #(revert-fontSize 'magnifyMusic mag) + #(revert-props 'magnifyMusic mag (append unshrinkable-props + shrinkable-props + '((Beam beam-thickness)))) } #}) +magnifyStaff = +#(define-music-function (parser location mag) (positive?) + (_i "Change the size of the staff, adjusting notation size and +horizontal spacing automatically, using @var{mag} as a size factor.") + + ;; these props are NOT allowed to shrink below default size + (define unshrinkable-props + '((StaffSymbol thickness))) + + ;; these props ARE allowed to shrink below default size + (define shrinkable-props + (let ((space-alist-props + (find-all-space-alist-props all-grob-descriptions))) + (append + space-alist-props + '( + ;; override at the 'Score level + (SpacingSpanner spacing-increment) + + (StaffSymbol staff-space) + (BarLine kern) + (BarLine segno-kern) + (BarLine hair-thickness) + (BarLine thick-thickness) + (Stem beamlet-default-length) + )))) + + #{ + \stopStaff + + %% revert settings from last time + %% (but only if \magnifyStaff has already been used + %% and the staff magnification is changing) + #(revert-fontSize 'magnifyStaff mag) + #(revert-props 'magnifyStaff mag (append unshrinkable-props + shrinkable-props)) + + %% scale settings + %% (but only if staff magnification is changing) + #(scale-fontSize 'magnifyStaff mag) + #(scale-props 'magnifyStaff mag #f unshrinkable-props) + #(scale-props 'magnifyStaff mag #t shrinkable-props) + + %% this might cause problems until Issue 3990 is fixed + \newSpacingSection + + \startStaff + \set Staff.magnifyStaffValue = #mag + #}) + makeClusters = #(define-music-function (parser location arg) (ly:music?) (_i "Display chords in @var{arg} as clusters.") diff --git a/scm/define-context-properties.scm b/scm/define-context-properties.scm index 2d38188d29..b88c8f59f8 100644 --- a/scm/define-context-properties.scm +++ b/scm/define-context-properties.scm @@ -562,6 +562,8 @@ part-combining.") @rinternals{Pitch_squash_engraver}.") (staffLineLayoutFunction ,procedure? "Layout of staff lines, @code{traditional}, or @code{semitone}.") + (magnifyStaffValue ,positive? "The most recent value set with +@code{\\magnifyStaff}.") (stanza ,markup? "Stanza @q{number} to print before the start of a verse. Use in @code{Lyrics} context.") (startRepeatSegnoType ,string? "Set the default bar line for the diff --git a/scm/music-functions.scm b/scm/music-functions.scm index 83cee5ce99..22c6eac92b 100644 --- a/scm/music-functions.scm +++ b/scm/music-functions.scm @@ -2404,37 +2404,108 @@ Offsets are restricted to immutable properties and values of type @code{number}, self) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; The following are used by \magnifyMusic - -(define-public (scale-fontSize mag) - "Used by @code{\\magnifyMusic}. Look up the current fontSize and -scale it by the magnification factor @var{mag}." +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; \magnifyMusic and \magnifyStaff + +;; defined as a function instead of a list because the +;; all-grob-descriptions alist is not available yet +(define-public (find-all-space-alist-props grob-descriptions) + "Used by @code{\\magnifyStaff}. When @var{grob-descriptions} is equal +to the @code{all-grob-descriptions} alist (defined in +@file{scm/define-grobs.scm}), this will find all grobs that have an +initialized value for the @code{space-alist} property, and return them +as a list in the following format: +@example +'((Ambitus space-alist) + (BarLine space-alist) + ...) +@end example" + (define (has-space-alist? grob-desc) + (ly:assoc-get 'space-alist (cdr grob-desc))) + (let* ((grob-descriptions-with-space-alist + (filter has-space-alist? grob-descriptions)) + (grob-names-with-space-alist + (map car grob-descriptions-with-space-alist))) + (map (lambda (grob-name) (list grob-name 'space-alist)) + grob-names-with-space-alist))) + +(define (magnifyStaff-is-set? context mag) + (let* ((Staff (ly:context-find context 'Staff)) + (old-mag (ly:context-property Staff 'magnifyStaffValue))) + (not (null? old-mag)))) + +(define (staff-magnification-is-changing? context mag) + (let* ((Staff (ly:context-find context 'Staff)) + (old-mag (ly:context-property Staff 'magnifyStaffValue 1))) + (not (= old-mag mag)))) + +(define-public (scale-fontSize func-name mag) + "Used by @code{\\magnifyMusic} and @code{\\magnifyStaff}. Look up the +current @code{fontSize} in the appropriate context and scale it by the +magnification factor @var{mag}. @var{func-name} is either +@code{'magnifyMusic} or @code{'magnifyStaff}." (make-apply-context (lambda (context) - (let* ((fontSize (ly:context-property context 'fontSize 0)) - (new-fontSize (+ fontSize (magnification->font-size mag)))) - (ly:context-set-property! context 'fontSize new-fontSize))))) - -(define-public (revert-fontSize mag) - "Used by @code{\\magnifyMusic}. Calculate the previous fontSize value -(before scaling) by factoring out the magnification factor @var{mag}." + (if (or (eq? func-name 'magnifyMusic) + ;; for \magnifyStaff, only scale the fontSize + ;; if staff magnification is changing + (staff-magnification-is-changing? context mag)) + (let* ((where (case func-name + ((magnifyMusic) context) + ((magnifyStaff) (ly:context-find context 'Staff)))) + (fontSize (ly:context-property where 'fontSize 0)) + (new-fontSize (+ fontSize (magnification->font-size mag)))) + (ly:context-set-property! where 'fontSize new-fontSize)))))) + +(define-public (revert-fontSize func-name mag) + "Used by @code{\\magnifyMusic} and @code{\\magnifyStaff}. Calculate +the previous @code{fontSize} value (before scaling) by factoring out the +magnification factor @var{mag} (if @var{func-name} is +@code{'magnifyMusic}), or by factoring out the context property +@code{magnifyStaffValue} (if @var{func-name} is @code{'magnifyStaff}). +Revert the @code{fontSize} in the appropriate context accordingly. + +With @code{\\magnifyMusic}, the scaling is reverted after the music +block it operates on. @code{\\magnifyStaff} does not operate on a music +block, so the scaling from a previous call (if there is one) is reverted +before the new scaling takes effect." (make-apply-context (lambda (context) - (let* ((fontSize (ly:context-property context 'fontSize 0)) - (old-fontSize (- fontSize (magnification->font-size mag)))) - (ly:context-set-property! context 'fontSize old-fontSize))))) - -(define-public (scale-props props mag allowed-to-shrink?) - "Used by @code{\\magnifyMusic}. For each prop in @var{props}, find -the current value of the requested prop, scale it by the magnification -factor @var{mag}, and do the equivalent of a -@code{\\temporary@tie{}\\override} with the new value. If -@code{allowed-to-shrink?} is @code{#f}, don't let the new value be less -than the current value. Props are formatted like: - + (if (or (eq? func-name 'magnifyMusic) + ;; for \magnifyStaff... + (and + ;; don't revert the user's fontSize choice + ;; the first time \magnifyStaff is called + (magnifyStaff-is-set? context mag) + ;; only revert the previous fontSize + ;; if staff magnification is changing + (staff-magnification-is-changing? context mag))) + (let* ((where + (case func-name + ((magnifyMusic) context) + ((magnifyStaff) (ly:context-find context 'Staff)))) + (old-mag + (case func-name + ((magnifyMusic) mag) + ((magnifyStaff) + (ly:context-property where 'magnifyStaffValue 1)))) + (fontSize (ly:context-property where 'fontSize 0)) + (old-fontSize (- fontSize (magnification->font-size old-mag)))) + (ly:context-set-property! where 'fontSize old-fontSize)))))) + +(define-public (scale-props func-name mag allowed-to-shrink? props) + "Used by @code{\\magnifyMusic} and @code{\\magnifyStaff}. For each +prop in @var{props}, find the current value of the requested prop, scale +it by the magnification factor @var{mag}, and do the equivalent of a +@code{\\temporary@tie{}\\override} with the new value in the appropriate +context. If @var{allowed-to-shrink?} is @code{#f}, don't let the new +value be less than the current value. @var{func-name} is either +@code{'magnifyMusic} or @code{'magnifyStaff}. The @var{props} list is +formatted like: @example -'(Slur height-limit) +'((Stem thickness) + (Slur line-thickness) + ...) @end example" (make-apply-context (lambda (context) @@ -2443,23 +2514,71 @@ than the current value. Props are formatted like: (prop (cadr grob-prop-list)) (where (if (eq? grob 'SpacingSpanner) (ly:context-find context 'Score) - context)) - (grob-def (ly:context-grob-definition where grob)) - (val (ly:assoc-get prop grob-def 1)) - (proc (lambda (x) - (if allowed-to-shrink? - (* x mag) - (* x (max 1 mag))))) - (new-val (if (number-pair? val) - (cons (proc (car val)) - (proc (cdr val))) - (proc val)))) - (ly:context-pushpop-property where grob prop new-val))) - (for-each scale-prop props)))) - + (case func-name + ((magnifyMusic) context) + ((magnifyStaff) (ly:context-find context 'Staff))))) + (grob-def (ly:context-grob-definition where grob))) + (if (eq? prop 'space-alist) + (let* ((space-alist (ly:assoc-get prop grob-def)) + (scale-spacing-tuple (lambda (x) + (cons (car x) + (cons (cadr x) + (* mag (cddr x)))))) + (scaled-tuples (map scale-spacing-tuple space-alist)) + (new-alist (append scaled-tuples space-alist))) + (ly:context-pushpop-property where grob prop new-alist)) + (let* ((val (ly:assoc-get prop grob-def 1)) + (proc (lambda (x) + (if allowed-to-shrink? + (* x mag) + (* x (max 1 mag))))) + (new-val (if (number-pair? val) + (cons (proc (car val)) + (proc (cdr val))) + (proc val)))) + (ly:context-pushpop-property where grob prop new-val))))) + (if (or (eq? func-name 'magnifyMusic) + ;; for \magnifyStaff, only scale the properties + ;; if staff magnification is changing + (staff-magnification-is-changing? context mag)) + (for-each scale-prop props))))) + +(define-public (revert-props func-name mag props) + "Used by @code{\\magnifyMusic} and @code{\\magnifyStaff}. Revert each +prop in @var{props} in the appropriate context. @var{func-name} is +either @code{'magnifyMusic} or @code{'magnifyStaff}. The @var{props} +list is formatted like: +@example +'((Stem thickness) + (Slur line-thickness) + ...) +@end example" + (make-apply-context + (lambda (context) + (define (revert-prop grob-prop-list) + (let* ((grob (car grob-prop-list)) + (prop (cadr grob-prop-list)) + (where (if (eq? grob 'SpacingSpanner) + (ly:context-find context 'Score) + (case func-name + ((magnifyMusic) context) + ((magnifyStaff) (ly:context-find context 'Staff)))))) + (ly:context-pushpop-property where grob prop))) + (if (or (eq? func-name 'magnifyMusic) + ;; for \magnifyStaff... + (and + ;; don't revert the user's property overrides + ;; the first time \magnifyStaff is called + (magnifyStaff-is-set? context mag) + ;; revert the overrides from the previous \magnifyStaff, + ;; but only if staff magnification is changing + (staff-magnification-is-changing? context mag))) + (for-each revert-prop props))))) + +;; \magnifyMusic only (define-public (scale-beam-thickness mag) "Used by @code{\\magnifyMusic}. Scaling @code{Beam.beam-thickness} -exactly to the @var{mag} value won't work. This uses two reference +exactly to the @var{mag} value will not work. This uses two reference values for @code{beam-thickness} to determine an acceptable value when scaling, then does the equivalent of a @code{\\temporary@tie{}\\override} with the new value." @@ -2473,21 +2592,3 @@ scaling, then does the equivalent of a (scaled-default (+ 119/925 (* mag 13/37))) (new-val (* scaled-default ratio-to-default))) (ly:context-pushpop-property context 'Beam 'beam-thickness new-val))))) - -(define-public (revert-props props) - "Used by @code{\\magnifyMusic}. Revert each prop in @var{props}. -Props are formatted like: - -@example -'(Slur height-limit) -@end example" - (make-apply-context - (lambda (context) - (define (revert-prop grob-prop-list) - (let* ((grob (car grob-prop-list)) - (prop (cadr grob-prop-list)) - (where (if (eq? grob 'SpacingSpanner) - (ly:context-find context 'Score) - context))) - (ly:context-pushpop-property where grob prop))) - (for-each revert-prop props))))