From 1b554886980bc48aed1ef4025f28292c694b4c55 Mon Sep 17 00:00:00 2001 From: Mark Polesky Date: Sun, 6 Jul 2014 01:11:35 -0700 Subject: [PATCH] Issue 3942: Scale slurs and ties when using \magnifyMusic. --- .../regression/magnifyMusic-dots-beamlets.ly | 30 +++++ .../magnifyMusic-laissez-vibrer-ties.ly | 38 ++++++ .../regression/magnifyMusic-phrasing-slurs.ly | 31 +++++ input/regression/magnifyMusic-repeat-ties.ly | 38 ++++++ input/regression/magnifyMusic-slurs.ly | 27 ++++ .../magnifyMusic-stem-beam-spacing.ly | 42 +++--- input/regression/magnifyMusic-ties.ly | 47 +++++++ ly/music-functions-init.ly | 116 +++++++++++++--- scm/music-functions.scm | 125 ++++++++++++++++++ 9 files changed, 459 insertions(+), 35 deletions(-) create mode 100644 input/regression/magnifyMusic-dots-beamlets.ly create mode 100644 input/regression/magnifyMusic-laissez-vibrer-ties.ly create mode 100644 input/regression/magnifyMusic-phrasing-slurs.ly create mode 100644 input/regression/magnifyMusic-repeat-ties.ly create mode 100644 input/regression/magnifyMusic-slurs.ly create mode 100644 input/regression/magnifyMusic-ties.ly diff --git a/input/regression/magnifyMusic-dots-beamlets.ly b/input/regression/magnifyMusic-dots-beamlets.ly new file mode 100644 index 0000000000..8d14060222 --- /dev/null +++ b/input/regression/magnifyMusic-dots-beamlets.ly @@ -0,0 +1,30 @@ +\version "2.19.8" + +\header { + texidoc = "Dot size and beamlet length should be scaled along +with notation size when using the @code{\magnifyMusic} command." +} + +\score { + \new Voice { + \omit Staff.TimeSignature + \time 7/4 + << + { \repeat unfold 7 \relative { g'8.[ g16] } } + { + \magnifyMusic 0.50 s4 + \magnifyMusic 0.63 s + \magnifyMusic 0.80 s + \magnifyMusic 1.00 s + \magnifyMusic 1.26 s + \magnifyMusic 1.59 s + \magnifyMusic 2.00 s + } + >> + } + \addlyrics { + "50%" _ _ _ _ _ + "100%" _ _ _ _ _ + "200%" _ + } +} diff --git a/input/regression/magnifyMusic-laissez-vibrer-ties.ly b/input/regression/magnifyMusic-laissez-vibrer-ties.ly new file mode 100644 index 0000000000..7a231e8e33 --- /dev/null +++ b/input/regression/magnifyMusic-laissez-vibrer-ties.ly @@ -0,0 +1,38 @@ +\version "2.19.8" + +\header { + texidoc = "Laissez vibrer ties should be scaled along with +notation size when using the @code{\magnifyMusic} command. They +can get thicker than the default, but not thinner." +} + +template = { + \omit Staff.TimeSignature + \time 7/2 + \magnifyMusic 0.50 s2 + \magnifyMusic 0.63 s + \magnifyMusic 0.80 s + \magnifyMusic 1.00 s + \magnifyMusic 1.26 s + \magnifyMusic 1.59 s + \magnifyMusic 2.00 s +} + +\score { + \new StaffGroup << + \new Staff \new Voice = "upper" << + \template + \repeat unfold 7 { g'4\laissezVibrer \hide Rest r4 } + >> + \new Lyrics \with { + \override VerticalAxisGroup.staff-affinity = #DOWN + } \lyricsto "upper" { + " 50%" \skip 1 \skip 1 " 100%" \skip 1 \skip 1 " 200%" + } + \new Staff \new Voice << + \clef bass + \template + \repeat unfold 7 { f4\laissezVibrer \hide Rest r4 } + >> + >> +} diff --git a/input/regression/magnifyMusic-phrasing-slurs.ly b/input/regression/magnifyMusic-phrasing-slurs.ly new file mode 100644 index 0000000000..b691e13250 --- /dev/null +++ b/input/regression/magnifyMusic-phrasing-slurs.ly @@ -0,0 +1,31 @@ +\version "2.19.8" + +\header { + texidoc = "Phrasing slurs should be scaled along with notation +size when using the @code{\magnifyMusic} command. They can get +thicker than the default, but not thinner." +} + +\score { + \new Voice { + \omit Staff.TimeSignature + \time 7/8 + << + { \repeat unfold 7 \relative { g'32[\( a b c\)] } } + { + \magnifyMusic 0.50 s8 + \magnifyMusic 0.63 s + \magnifyMusic 0.80 s + \magnifyMusic 1.00 s + \magnifyMusic 1.26 s + \magnifyMusic 1.59 s + \magnifyMusic 2.00 s + } + >> + } + \addlyrics { + "50%" _ _ _ _ _ _ _ _ _ _ _ + "100%" _ _ _ _ _ _ _ _ _ _ _ + "200%" _ _ _ + } +} diff --git a/input/regression/magnifyMusic-repeat-ties.ly b/input/regression/magnifyMusic-repeat-ties.ly new file mode 100644 index 0000000000..fd1c374fa4 --- /dev/null +++ b/input/regression/magnifyMusic-repeat-ties.ly @@ -0,0 +1,38 @@ +\version "2.19.8" + +\header { + texidoc = "Repeat ties should be scaled along with notation size +when using the @code{\magnifyMusic} command. They can get thicker +than the default, but not thinner." +} + +template = { + \omit Staff.TimeSignature + \time 7/2 + \magnifyMusic 0.50 s2 + \magnifyMusic 0.63 s + \magnifyMusic 0.80 s + \magnifyMusic 1.00 s + \magnifyMusic 1.26 s + \magnifyMusic 1.59 s + \magnifyMusic 2.00 s +} + +\score { + \new StaffGroup << + \new Staff \new Voice = "upper" << + \template + \repeat unfold 7 { g'4\repeatTie \hide Rest r4 } + >> + \new Lyrics \with { + \override VerticalAxisGroup.staff-affinity = #DOWN + } \lyricsto "upper" { + " 50%" \skip 1 \skip 1 " 100%" \skip 1 \skip 1 " 200%" + } + \new Staff \new Voice << + \clef bass + \template + \repeat unfold 7 { f4\repeatTie \hide Rest r4 } + >> + >> +} diff --git a/input/regression/magnifyMusic-slurs.ly b/input/regression/magnifyMusic-slurs.ly new file mode 100644 index 0000000000..31edb57a52 --- /dev/null +++ b/input/regression/magnifyMusic-slurs.ly @@ -0,0 +1,27 @@ +\version "2.19.8" + +\header { + texidoc = "Slurs should be scaled along with notation size when +using the @code{\magnifyMusic} command. They can get thicker than +the default, but not thinner." +} + +\score { + \new Voice { + \omit Staff.TimeSignature + \time 7/8 + << + { \repeat unfold 7 \relative { g'32[( a b c)] } } + { + \magnifyMusic 0.50 s8 + \magnifyMusic 0.63 s + \magnifyMusic 0.80 s + \magnifyMusic 1.00 s + \magnifyMusic 1.26 s + \magnifyMusic 1.59 s + \magnifyMusic 2.00 s + } + >> + } + \addlyrics { "50%" _ _ "100%" _ _ "200%" } +} diff --git a/input/regression/magnifyMusic-stem-beam-spacing.ly b/input/regression/magnifyMusic-stem-beam-spacing.ly index 2ca15238a0..135cfd9e8d 100644 --- a/input/regression/magnifyMusic-stem-beam-spacing.ly +++ b/input/regression/magnifyMusic-stem-beam-spacing.ly @@ -1,22 +1,32 @@ \version "2.19.8" \header { - texidoc = "Stem lengths, beam spacing/thickness, and horizontal -spacing should be scaled along with notation size when using the -@code{\magnifyMusic} command." + texidoc = "Stem length/thickness, beam spacing/thickness, and +horizontal spacing should be scaled along with notation size when +using the @code{\magnifyMusic} command. Stems can get thicker +than the default, but not thinner." } -\layout { ragged-right = ##t } - -\relative << - { \repeat unfold 7 { g'32[ a b c] } } - { - \magnifyMusic 0.50 { s8_"50%" } - \magnifyMusic 0.63 { s } - \magnifyMusic 0.80 { s } - \magnifyMusic 1.00 { s_"100%" } - \magnifyMusic 1.26 { s } - \magnifyMusic 1.59 { s } - \magnifyMusic 2.00 { s_"200%" } +\score { + \new Voice { + \omit Staff.TimeSignature + \time 7/8 + << + { \repeat unfold 7 \relative { g'32[ a b c] } } + { + \magnifyMusic 0.50 s8 + \magnifyMusic 0.63 s + \magnifyMusic 0.80 s + \magnifyMusic 1.00 s + \magnifyMusic 1.26 s + \magnifyMusic 1.59 s + \magnifyMusic 2.00 s + } + >> + } + \addlyrics { + "50%" _ _ _ _ _ _ _ _ _ _ _ + "100%" _ _ _ _ _ _ _ _ _ _ _ + "200%" _ _ _ } ->> +} diff --git a/input/regression/magnifyMusic-ties.ly b/input/regression/magnifyMusic-ties.ly new file mode 100644 index 0000000000..76d98fc47a --- /dev/null +++ b/input/regression/magnifyMusic-ties.ly @@ -0,0 +1,47 @@ +\version "2.19.8" + +\header { + texidoc = "Ties should be scaled along with notation size when +using the @code{\magnifyMusic} command. They can get thicker than +the default, but not thinner." +} + +template = { + \omit Staff.TimeSignature + \time 7/8 + \magnifyMusic 0.50 s8 + \magnifyMusic 0.63 s + \magnifyMusic 0.80 s + \magnifyMusic 1.00 s + \magnifyMusic 1.26 s + \magnifyMusic 1.59 s + \magnifyMusic 2.00 s +} + +\score { + \new StaffGroup << + \new Staff \new Voice = "upper" << + \template + \repeat unfold 7 { g'32[~ g' a'~ a'] } + >> + \new Staff \new Voice << + \template + \repeat unfold 7 { \tieUp g'32[~ g' a'~ a'] } + >> + \new Lyrics \with { + \override VerticalAxisGroup.staff-affinity = #DOWN + } \lyricsto "upper" { + "50%" _ _ _ _ _ "100%" _ _ _ _ _ " 200%" + } + \new Staff \new Voice << + \clef bass + \template + \repeat unfold 7 { f32[~ f e~ e] } + >> + \new Staff \new Voice << + \clef bass + \template + \repeat unfold 7 { \tieDown f32[~ f e~ e] } + >> + >> +} diff --git a/ly/music-functions-init.ly b/ly/music-functions-init.ly index 9e5d3c223b..f3a2daa59e 100644 --- a/ly/music-functions-init.ly +++ b/ly/music-functions-init.ly @@ -633,26 +633,104 @@ languageRestore = magnifyMusic = -#(define-music-function (parser location mag mus) (number? ly:music?) - (_i "Magnify the notation of @var{mus} without changing the -staff-size, using @var{mag} as a size factor. Stems, beams, and -horizontal spacing are adjusted automatically.") +#(define-music-function (parser location mag music) (positive? ly:music?) + (_i "Magnify the notation of @var{music} without changing the +staff-size, using @var{mag} as a size factor. Stems, beams, +slurs, ties, and horizontal spacing are adjusted automatically.") + + ;; these props are NOT allowed to shrink below default size + (define unshrinkable-props + '( + ;; stems + Stem.thickness + ;; slurs + Slur.line-thickness + Slur.thickness + PhrasingSlur.line-thickness + PhrasingSlur.thickness + ;; ties + Tie.line-thickness + Tie.thickness + LaissezVibrerTie.line-thickness + LaissezVibrerTie.thickness + RepeatTie.line-thickness + RepeatTie.thickness + )) + + ;; these props ARE allowed to shrink below default size + (define shrinkable-props + '( + ;; override at the 'Score level + SpacingSpanner.spacing-increment + + ;; Beam.beam-thickness is dealt with separately below + + ;; lengths and heights + Beam.length-fraction + Stem.length-fraction + Stem.beamlet-default-length + Slur.height-limit + Slur.minimum-length + PhrasingSlur.height-limit + PhrasingSlur.minimum-length + + ;; every Slur.details prop that's + ;; not a factor, penalty, ratio, or slope + Slur.details.region-size + Slur.details.free-head-distance + Slur.details.free-slur-distance + Slur.details.gap-to-staffline-inside + Slur.details.gap-to-staffline-outside + Slur.details.extra-encompass-free-distance + Slur.details.extra-encompass-collision-distance + Slur.details.close-to-edge-length + Slur.details.encompass-object-range-overshoot + Slur.details.slur-tie-extrema-min-distance + + ;; every PhrasingSlur.details prop that's + ;; not a factor, penalty, ratio, or slope + PhrasingSlur.details.region-size + PhrasingSlur.details.free-head-distance + PhrasingSlur.details.free-slur-distance + PhrasingSlur.details.gap-to-staffline-inside + PhrasingSlur.details.gap-to-staffline-outside + PhrasingSlur.details.extra-encompass-free-distance + PhrasingSlur.details.extra-encompass-collision-distance + PhrasingSlur.details.close-to-edge-length + PhrasingSlur.details.encompass-object-range-overshoot + PhrasingSlur.details.slur-tie-extrema-min-distance + + ;; every Tie.details prop that's + ;; not a factor, penalty, ratio, or slope + Tie.details.center-staff-line-clearance + Tie.details.tip-staff-line-clearance + Tie.details.note-head-gap + Tie.details.stem-gap + Tie.details.height-limit + Tie.details.tie-tie-collision-distance + Tie.details.intra-space-threshold + Tie.details.outer-tie-vertical-gap + Tie.details.multi-tie-region-size + Tie.details.single-tie-region-size + Tie.details.between-length-limit + )) #{ - \set fontSize = #(magnification->font-size mag) - % gives beam-thickness=0.48 when mag=1 (like default), - % gives beam-thickness=0.35 when mag=0.63 (like CueVoice) - \temporary \override Beam.beam-thickness = #(+ 119/925 (* mag 13/37)) - \temporary \override Beam.length-fraction = #mag - \temporary \override Stem.length-fraction = #mag - \temporary \override Stem.thickness = #(* 1.3 (max 1 mag)) - \temporary \override Score.SpacingSpanner.spacing-increment = #(* 1.2 mag) - #mus - \set fontSize = 0 - \revert Beam.beam-thickness - \revert Beam.length-fraction - \revert Stem.length-fraction - \revert Stem.thickness - \revert Score.SpacingSpanner.spacing-increment + \context Voice { + \newSpacingSection + #(scale-fontSize mag) + #(scale-props unshrinkable-props mag #f) + #(scale-props shrinkable-props mag #t) + #(scale-beam-thickness mag) + + #music + + \newSpacingSection + %% reverse engineer the former fontSize value instead of using \unset + #(revert-fontSize mag) + #(revert-props (append unshrinkable-props + shrinkable-props + (list 'Beam.beam-thickness))) + } #}) makeClusters = diff --git a/scm/music-functions.scm b/scm/music-functions.scm index ba567229e7..0665236a08 100644 --- a/scm/music-functions.scm +++ b/scm/music-functions.scm @@ -2392,3 +2392,128 @@ Offsets are restricted to immutable properties and values of type @code{number}, vals)))) ; return the closure named `self' 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}." + (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}." + (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: + +@example +Slur.height-limit +Slur.details.region-size +@end example" + (make-apply-context + (lambda (context) + (define (scale-prop grob.prop) + (let* ((grob-prop-list (map string->symbol + (string-split + (symbol->string grob.prop) #\.))) + (prop-is-alist? (eq? 3 (length grob-prop-list))) + (grob (car grob-prop-list)) + (prop (cadr grob-prop-list)) + (where (if (eq? grob 'SpacingSpanner) + (ly:context-find context 'Score) + context)) + (grob-def (ly:context-grob-definition where grob))) + (if prop-is-alist? + (let* ((subprop (caddr grob-prop-list)) + (old-alist (ly:assoc-get prop grob-def)) + (val (ly:assoc-get subprop old-alist 1)) + (round-if-needed + (lambda (x) + ;; these props require exact integers + (if (or (eq? subprop 'multi-tie-region-size) + (eq? subprop 'single-tie-region-size)) + (inexact->exact (round x)) + x))) + (new-val (if allowed-to-shrink? + (round-if-needed (* val mag)) + (round-if-needed (* val (max 1 mag))))) + (new-alist (cons (cons subprop new-val) old-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))))) + (for-each scale-prop props)))) + +(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 +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." + (make-apply-context + (lambda (context) + (let* ((grob-def (ly:context-grob-definition context 'Beam)) + (val (ly:assoc-get 'beam-thickness grob-def 0.48)) + (ratio-to-default (/ val 0.48)) + ;; gives beam-thickness=0.48 when mag=1 (like default), + ;; gives beam-thickness=0.35 when mag=0.63 (like CueVoice) + (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 +Slur.details.region-size +@end example + +Nested properties are reverted by reverting the parent property only. +For example, @code{Slur.details.region-size} gets reverted like this: + +@example +\revert Slur.details +@end example + +This is safe as long as the number of reverts matches the number of +overrides. Any user overrides within a @code{\\magnifyMusic} block +should be reverted before closing the block." + (make-apply-context + (lambda (context) + (define (revert-prop grob.prop) + (let* ((grob-prop-list (map string->symbol + (string-split + (symbol->string grob.prop) #\.))) + (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)))) -- 2.39.2