]> git.donarmstrong.com Git - lilypond.git/commitdiff
Issue 3942: Scale slurs and ties when using \magnifyMusic.
authorMark Polesky <markpolesky@yahoo.com>
Sun, 6 Jul 2014 08:11:35 +0000 (01:11 -0700)
committerMark Polesky <markpolesky@yahoo.com>
Sun, 6 Jul 2014 08:11:35 +0000 (01:11 -0700)
input/regression/magnifyMusic-dots-beamlets.ly [new file with mode: 0644]
input/regression/magnifyMusic-laissez-vibrer-ties.ly [new file with mode: 0644]
input/regression/magnifyMusic-phrasing-slurs.ly [new file with mode: 0644]
input/regression/magnifyMusic-repeat-ties.ly [new file with mode: 0644]
input/regression/magnifyMusic-slurs.ly [new file with mode: 0644]
input/regression/magnifyMusic-stem-beam-spacing.ly
input/regression/magnifyMusic-ties.ly [new file with mode: 0644]
ly/music-functions-init.ly
scm/music-functions.scm

diff --git a/input/regression/magnifyMusic-dots-beamlets.ly b/input/regression/magnifyMusic-dots-beamlets.ly
new file mode 100644 (file)
index 0000000..8d14060
--- /dev/null
@@ -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 (file)
index 0000000..7a231e8
--- /dev/null
@@ -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 (file)
index 0000000..b691e13
--- /dev/null
@@ -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 (file)
index 0000000..fd1c374
--- /dev/null
@@ -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 (file)
index 0000000..31edb57
--- /dev/null
@@ -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%" }
+}
index 2ca15238a00a5db49423a4ef313049963e8a204b..135cfd9e8deb773232a64145d2e3c73ff0f915a9 100644 (file)
@@ -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 (file)
index 0000000..76d98fc
--- /dev/null
@@ -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] }
+    >>
+  >>
+}
index 9e5d3c223b8c32db59a70585bd2bdf7f8478c278..f3a2daa59e896adb2c5ebb9321f856d8d595fc99 100644 (file)
@@ -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 =
index ba567229e794f8c4a82ef9079a0981d04d3fc20a..0665236a08af64b780dbc2141f1d349b9e395334 100644 (file)
@@ -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))))