X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=scm%2Fdefine-markup-commands.scm;h=f4cf31dd3b647910eb84aed42a2ecc0e68bde82a;hb=2f7fe0c6bc80ad57ae73e213f9b20bc9719ba2ce;hp=0f5329658430ebd23b7efb41f20e81348126b6f4;hpb=2f0c6eb19208485a86d3416db3f3640a1d54752a;p=lilypond.git diff --git a/scm/define-markup-commands.scm b/scm/define-markup-commands.scm old mode 100644 new mode 100755 index 0f53296584..f4cf31dd3b --- a/scm/define-markup-commands.scm +++ b/scm/define-markup-commands.scm @@ -139,6 +139,147 @@ A simple line. (y (cdr dest))) (make-line-stencil th 0 0 x y))) +(define-markup-command (draw-dashed-line layout props dest) + (number-pair?) + #:category graphic + #:properties ((thickness 1) + (on 1) + (off 1) + (phase 0) + (full-length #t)) + " +@cindex drawing dashed lines within text + +A dashed line. + +If @code{full-length} is set to #t (default) the dashed-line extends to the +whole length given by @var{dest}, without white space at beginning or end. +@code{off} will then be altered to fit. +To insist on the given (or default) values of @code{on}, @code{off} use +@code{\\override #'(full-length . #f)} +Manual settings for @code{on},@code{off} and @code{phase} are possible. +@lilypond[verbatim,quote] +\\markup { + \\draw-dashed-line #'(5.1 . 2.3) + \\override #'(on . 0.3) + \\override #'(off . 0.5) + \\draw-dashed-line #'(5.1 . 2.3) +} +@end lilypond" + (let* ((line-thickness (ly:output-def-lookup layout 'line-thickness)) + ;; Calculate the thickness to be used. + (th (* line-thickness thickness)) + (half-thick (/ th 2)) + ;; Get the extensions in x- and y-direction. + (x (car dest)) + (y (cdr dest)) + ;; Calculate the length of the dashed line. + (line-length (sqrt (+ (expt x 2) (expt y 2))))) + + (if (and full-length (not (= (+ on off) 0))) + (begin + ;; Add double-thickness to avoid overlapping. + (set! off (+ (* 2 th) off)) + (let* (;; Make a guess how often the off/on-pair should be printed + ;; after the initial `on´. + ;; Assume a minimum of 1 to avoid division by zero. + (guess (max 1 (round (/ (- line-length on) (+ off on))))) + ;; Not sure about the value or why corr is necessary at all, + ;; but it seems to be necessary. + (corr (if (= on 0) + (/ line-thickness 10) + 0)) + ;; Calculate a new value for off to fit the + ;; line-length. + (new-off (/ (- line-length corr (* (1+ guess) on)) guess)) + ) + (cond + + ;; Settings for (= on 0). Resulting in a dotted line. + + ;; If line-length isn't shorter than `th´, change the given + ;; value for `off´ to fit the line-length. + ((and (= on 0) (< th line-length)) + (set! off new-off)) + + ;; If the line-length is shorter than `th´, it makes no + ;; sense to adjust `off´. The rounded edges of the lines + ;; would prevent any nice output. + ;; Do nothing. + ;; This will result in a single dot for very short lines. + ((and (= on 0) (>= th line-length)) + #f) + + ;; Settings for (not (= on 0)). Resulting in a dashed line. + + ;; If line-length isn't shorter than one go of on-off-on, + ;; change the given value for `off´ to fit the line-length. + ((< (+ (* 2 on) off) line-length) + (set! off new-off)) + ;; If the line-length is too short, but greater than + ;; (* 4 th) set on/off to (/ line-length 3) + ((< (* 4 th) line-length) + (set! on (/ line-length 3)) + (set! off (/ line-length 3))) + ;; If the line-length is shorter than (* 4 th), it makes + ;; no sense trying to adjust on/off. The rounded edges of + ;; the lines would prevent any nice output. + ;; Simply set `on´ to line-length. + (else + (set! on line-length)))))) + + ;; If `on´ or `off´ is negative, or the sum of `on' and `off' equals zero a + ;; ghostscript-error occurs while calling + ;; (ly:make-stencil (list 'dashed-line th on off x y phase) x-ext y-ext) + ;; Better be paranoid. + (if (or (= (+ on off) 0) + (negative? on) + (negative? off)) + (begin + (ly:warning "Can't print a line - setting on/off to default") + (set! on 1) + (set! off 1))) + + ;; To give the lines produced by \draw-line and \draw-dashed-line the same + ;; length, half-thick has to be added to the stencil-extensions. + (ly:make-stencil + (list 'dashed-line th on off x y phase) + (interval-widen (ordered-cons 0 x) half-thick) + (interval-widen (ordered-cons 0 y) half-thick)))) + +(define-markup-command (draw-dotted-line layout props dest) + (number-pair?) + #:category graphic + #:properties ((thickness 1) + (off 1) + (phase 0)) + " +@cindex drawing dotted lines within text + +A dotted line. + +The dotted-line always extends to the whole length given by @var{dest}, without +white space at beginning or end. +Manual settings for @code{off} are possible to get larger or smaller space +between the dots. +The given (or default) value of @code{off} will be altered to fit the +line-length. +@lilypond[verbatim,quote] +\\markup { + \\draw-dotted-line #'(5.1 . 2.3) + \\override #'(thickness . 2) + \\override #'(off . 0.2) + \\draw-dotted-line #'(5.1 . 2.3) +} +@end lilypond" + + (let ((new-props (prepend-alist-chain 'on 0 + (prepend-alist-chain 'full-length #t props)))) + + (interpret-markup layout + new-props + (markup #:draw-dashed-line dest)))) + (define-markup-command (draw-hline layout props) () #:category graphic @@ -548,7 +689,6 @@ Create a box of the same height as the space in the current font." (ly:stencil-extent m X) ))) -;; todo: fix negative space (define-markup-command (hspace layout props amount) (number?) #:category align @@ -568,11 +708,8 @@ Create an invisible object taking up horizontal space @var{amount}. } @end lilypond" (let ((corrected-space (- amount word-space))) - (if (> corrected-space 0) - (ly:make-stencil "" (cons 0 corrected-space) '(0 . 0)) - (ly:make-stencil "" (cons corrected-space corrected-space) '(0 . 0))))) + (ly:make-stencil "" (cons 0 corrected-space) '(0 . 0)))) -;; todo: fix negative space (define-markup-command (vspace layout props amount) (number?) #:category align @@ -594,9 +731,7 @@ of @var{amount} multiplied by 3. } @end lilypond" (let ((amount (* amount 3.0))) - (if (> amount 0) - (ly:make-stencil "" (cons 0 0) (cons 0 amount)) - (ly:make-stencil "" (cons 0 0) (cons amount amount))))) + (ly:make-stencil "" (cons 0 0) (cons 0 amount)))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -3217,6 +3352,265 @@ a shortened down stem. (let ((parsed (parse-simple-duration duration))) (note-by-number-markup layout props (car parsed) (cadr parsed) dir))) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; the rest command. +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(define-markup-command (rest-by-number layout props log dot-count) + (number? number?) + #:category music + #:properties ((font-size 0) + (style '()) + (multi-measure-rest #f)) + " +@cindex rests or multi-measure-rests within text by log and dot-count + +A rest or multi-measure-rest symbol. + +@lilypond[verbatim,quote] +\\markup { + \\rest-by-number #3 #2 + \\hspace #2 + \\rest-by-number #0 #1 + \\hspace #2 + \\override #'(multi-measure-rest . #t) + \\rest-by-number #0 #0 +} +@end lilypond" + + (define (get-glyph-name-candidates log style) + (let* (;; Choose the style-string to be added. + ;; If no glyph exists, select others for the specified styles + ;; otherwise defaulting. + (style-strg + (cond ( + ;; 'baroque needs to be special-cased, otherwise + ;; `select-head-glyph´ would catch neomensural-glyphs for + ;; this style, if (< log 0). + (eq? style 'baroque) + (string-append (number->string log) "")) + ((eq? style 'petrucci) + (string-append (number->string log) "mensural")) + ;; In other cases `select-head-glyph´ from output-lib.scm + ;; works for rest-glyphs, too. + ((and (symbol? style) (not (eq? style 'default))) + (select-head-glyph style log)) + (else log))) + ;; Choose ledgered glyphs for whole and half rest. + ;; Except for the specified styles, logs and MultiMeasureRests. + (ledger-style-rests + (if (and (or (list? style) + (not (member style + '(neomensural mensural petrucci)))) + (not multi-measure-rest) + (or (= log 0) (= log 1))) + "o" + ""))) + (format #f "rests.~a~a" style-strg ledger-style-rests))) + + (define (get-glyph-name font cands) + (if (ly:stencil-empty? (ly:font-get-glyph font cands)) + "" + cands)) + + (let* ((font + (ly:paper-get-font layout + (cons '((font-encoding . fetaMusic)) props))) + (rest-glyph-name + (let ((result + (get-glyph-name font + (get-glyph-name-candidates log style)))) + (if (string-null? result) + ;; If no glyph name can be found, select default rests. Though + ;; this usually means an unsupported style has been chosen, it + ;; also prevents unrelated 'style settings from other grobs + ;; (e.g., TextSpanner and TimeSignature) leaking into markup. + (get-glyph-name font (get-glyph-name-candidates log 'default)) + result))) + (rest-glyph (ly:font-get-glyph font rest-glyph-name)) + (dot (ly:font-get-glyph font "dots.dot")) + (dot-width (interval-length (ly:stencil-extent dot X))) + (dots (and (> dot-count 0) + (apply ly:stencil-add + (map (lambda (x) + (ly:stencil-translate-axis + dot (* 2 x dot-width) X)) + (iota dot-count)))))) + + ;; Apart from mensural-, neomensural- and petrucci-style ledgered + ;; glyphs are taken for whole and half rests. + ;; If they are dotted, move the dots in X-direction to avoid collision. + (if (and dots + (< log 2) + (>= log 0) + (not (member style '(neomensural mensural petrucci)))) + (set! dots (ly:stencil-translate-axis dots dot-width X))) + + ;; Add dots to the rest-glyph. + ;; + ;; Not sure how to vertical align dots. + ;; For now the dots are centered for half, whole or longer rests. + ;; Otherwise placed near the top of the rest. + ;; + ;; Dots for rests with (< log 0) dots are allowed, but not + ;; if multi-measure-rest is set #t. + (if (and (not multi-measure-rest) dots) + (set! rest-glyph + (ly:stencil-add + (ly:stencil-translate + dots + (cons + (+ (cdr (ly:stencil-extent rest-glyph X)) dot-width) + (if (< log 2) + (interval-center (ly:stencil-extent rest-glyph Y)) + (- (interval-end (ly:stencil-extent rest-glyph Y)) + (/ (* 2 dot-width) 3))))) + rest-glyph))) + rest-glyph)) + +(define-markup-command (rest layout props duration) + (string?) + #:category music + #:properties ((style '()) + (multi-measure-rest #f) + (multi-measure-rest-number #t) + (word-space 0.6)) + " +@cindex rests or multi-measure-rests within text by string + +This produces a rest, with the @var{duration} for the rest type and +augmentation dots. +@code{\"breve\"}, @code{\"longa\"} and @code{\"maxima\"} are valid +input-strings. + +Printing MultiMeasureRests could be enabled with +@code{\\override #'(multi-measure-rest . #t)} +If MultiMeasureRests are taken, the MultiMeasureRestNumber is printed above. +This is enabled for all styles using default-glyphs. +Could be disabled with @code{\\override #'(multi-measure-rest-number . #f)} + +@lilypond[verbatim,quote] +\\markup { + \\rest #\"4..\" + \\hspace #2 + \\rest #\"breve\" + \\hspace #2 + \\override #'(multi-measure-rest . #t) + { + \\rest #\"7\" + \\hspace #2 + \\override #'(multi-measure-rest-number . #f) + \\rest #\"7\" + } +} +@end lilypond" + ;; Get the number of mmr-glyphs. + ;; Store them in a list. + ;; example: (mmr-numbers 25) -> '(3 0 0 1) + (define (mmr-numbers nmbr) + (let* ((8-bar-glyph (floor (/ nmbr 8))) + (8-remainder (remainder nmbr 8)) + (4-bar-glyph (floor (/ 8-remainder 4))) + (4-remainder (remainder nmbr 4)) + (2-bar-glyph (floor (/ 4-remainder 2))) + (2-remainder (remainder 4-remainder 2)) + (1-bar-glyph (floor (/ 2-remainder 1)))) + (list 8-bar-glyph 4-bar-glyph 2-bar-glyph 1-bar-glyph))) + + ;; Get the correct mmr-glyphs. + ;; Store them in a list. + ;; example: + ;; (get-mmr-glyphs '(1 0 1 0) '("rests.M3" "rests.M2" "rests.M1" "rests.0")) + ;; -> ("rests.M3" "rests.M1") + (define (get-mmr-glyphs lst1 lst2) + (define (helper l1 l2 l3) + (if (null? l1) + (reverse l3) + (helper (cdr l1) + (cdr l2) + (append (make-list (car l1) (car l2)) l3)))) + (helper lst1 lst2 '())) + + ;; If duration is not valid, print a warning and return empty-stencil + (if (or (and (not (integer? (car (parse-simple-duration duration)))) + (not multi-measure-rest)) + (and (= (string-length (car (string-split duration #\. ))) 1) + (= (string->number (car (string-split duration #\. ))) 0))) + (begin + (ly:warning (_ "not a valid duration string: ~a - ignoring") duration) + empty-stencil) + (let* ( + ;; For simple rests: + ;; Get a (log dots) list. + (parsed (parse-simple-duration duration)) + ;; Create the rest-stencil + (stil + (rest-by-number-markup layout props (car parsed) (cadr parsed))) + ;; For MultiMeasureRests: + ;; Get the duration-part of duration + (dur-part-string (car (string-split duration #\. ))) + ;; Get the duration of MMR: + ;; If not a number (eg. "maxima") calculate it. + (mmr-duration + (or (string->number dur-part-string) (expt 2 (abs (car parsed))))) + ;; Get a list of the correct number of each mmr-glyph. + (count-mmr-glyphs-list (mmr-numbers mmr-duration)) + ;; Create a list of mmr-stencils, + ;; translating the glyph for a whole rest. + (mmr-stils-list + (map + (lambda (x) + (let ((single-mmr-stil + (rest-by-number-markup layout props (* -1 x) 0))) + (if (= x 0) + (ly:stencil-translate-axis + single-mmr-stil + ;; Ugh, hard-coded, why 1? + 1 + Y) + single-mmr-stil))) + (get-mmr-glyphs count-mmr-glyphs-list (reverse (iota 4))))) + ;; Adjust the space between the mmr-glyphs, + ;; if not default-glyphs are used. + (word-space (if (member style + '(neomensural mensural petrucci)) + (/ (* word-space 2) 3) + word-space)) + ;; Create the final mmr-stencil + ;; via `stack-stencil-line´ from /scm/markup.scm + (mmr-stil (stack-stencil-line word-space mmr-stils-list))) + + ;; Print the number above a multi-measure-rest + ;; Depends on duration, style and multi-measure-rest-number set #t + (if (and multi-measure-rest + multi-measure-rest-number + (> mmr-duration 1) + (not (member style '(neomensural mensural petrucci)))) + (let* ((mmr-stil-x-center + (interval-center (ly:stencil-extent mmr-stil X))) + (duration-markup + (markup + #:fontsize -2 + #:override '(font-encoding . fetaText) + (number->string mmr-duration))) + (mmr-number-stil + (interpret-markup layout props duration-markup)) + (mmr-number-stil-x-center + (interval-center (ly:stencil-extent mmr-number-stil X)))) + + (set! mmr-stil (ly:stencil-combine-at-edge + mmr-stil + Y UP + (ly:stencil-translate-axis + mmr-number-stil + (- mmr-stil-x-center mmr-number-stil-x-center) + X) + ;; Ugh, hardcoded + 0.8)))) + (if multi-measure-rest + mmr-stil + stil)))) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; translating. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;