X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=scm%2Flily-library.scm;h=34c9cb3820096c04603fd358393651bb165491e1;hb=54b02666750062788185bd3f99e644d621e348c2;hp=0864e57deca4271ce57dddb3f33b65e8ef8dedd2;hpb=2426fd110be285784ed32fb0ba9f529a566a272c;p=lilypond.git diff --git a/scm/lily-library.scm b/scm/lily-library.scm index 0864e57dec..34c9cb3820 100644 --- a/scm/lily-library.scm +++ b/scm/lily-library.scm @@ -1,6 +1,6 @@ ;;;; This file is part of LilyPond, the GNU music typesetter. ;;;; -;;;; Copyright (C) 1998--2010 Jan Nieuwenhuizen +;;;; Copyright (C) 1998--2011 Jan Nieuwenhuizen ;;;; Han-Wen Nienhuys ;;;; ;;;; LilyPond is free software: you can redistribute it and/or modify @@ -66,6 +66,15 @@ (or (equal? a b) (ly:momentmoment fraction) + (if (null? fraction) + ZERO-MOMENT + (ly:make-moment (car fraction) (cdr fraction)))) + +(define-public (moment->fraction moment) + (cons (ly:moment-main-numerator moment) + (ly:moment-main-denominator moment))) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; arithmetic (define-public (average x . lst) @@ -75,7 +84,7 @@ ;; parser <-> output hooks. (define-public (collect-bookpart-for-book parser book-part) - "Toplevel book-part handler" + "Toplevel book-part handler." (define (add-bookpart book-part) (ly:parser-define! parser 'toplevel-bookparts @@ -122,21 +131,21 @@ (score-handler (scorify-music music parser))))) (define-public (collect-music-for-book parser music) - "Top-level music handler" + "Top-level music handler." (collect-music-aux (lambda (score) (collect-scores-for-book parser score)) parser music)) (define-public (collect-book-music-for-book parser book music) - "Book music handler" + "Book music handler." (collect-music-aux (lambda (score) (ly:book-add-score! book score)) parser music)) (define-public (scorify-music music parser) - "Preprocess MUSIC." + "Preprocess @var{music}." (for-each (lambda (func) (set! music (func music parser))) @@ -170,14 +179,14 @@ bookoutput function" ;; as the key to out internal a-list (let* ((base-name (get-current-filename parser)) (output-suffix (get-current-suffix parser)) - (alist-key (format "~a~a" base-name output-suffix)) + (alist-key (format #f "~a~a" base-name output-suffix)) (counter-alist (ly:parser-lookup parser 'counter-alist)) (output-count (assoc-get alist-key counter-alist 0)) (result base-name)) ;; Allow all ASCII alphanumerics, including accents (if (string? output-suffix) (set! result - (format "~a-~a" + (format #f "~a-~a" result (string-regexp-substitute "[^-[:alnum:]]" @@ -253,21 +262,22 @@ bookoutput function" (map-alist-vals func (cdr list))))) (define (map-alist-keys func list) - "map FUNC over the keys of an alist LIST, leaving the vals. " + "map FUNC over the keys of an alist LIST, leaving the vals." (if (null? list) '() (cons (cons (func (caar list)) (cdar list)) (map-alist-keys func (cdr list))))) (define-public (first-member members lst) - "Return first successful MEMBER of member from MEMBERS in LST." + "Return first successful member (of member) from @var{members} in +@var{lst}." (if (null? members) #f (let ((m (member (car members) lst))) (if m m (first-member (cdr members) lst))))) (define-public (first-assoc keys lst) - "Return first successful ASSOC of key from KEYS in LST." + "Return first successful assoc of key from @var{keys} in @var{lst}." (if (null? keys) #f (let ((k (assoc (car keys) lst))) @@ -280,6 +290,40 @@ bookoutput function" (cons (cdar alist) (flatten-alist (cdr alist)))))) +(define (assoc-remove key alist) + "Remove key (and its corresponding value) from an alist. + Different than assoc-remove! because it is non-destructive." + (define (assoc-crawler key l r) + (if (null? r) + l + (if (equal? (caar r) key) + (append l (cdr r)) + (assoc-crawler key (append l `(,(car r))) (cdr r))))) + (assoc-crawler key '() alist)) + +(define-public (map-selected-alist-keys function keys alist) + "Return @var{alist} with @var{function} applied to all of the values +in list @var{keys}. + +For example: +@example +@code{guile> (map-selected-alist-keys - '(a b) '((a . 1) (b . -2) (c . 3) (d . 4)))} +@code{((a . -1) (b . 2) (c . 3) (d . 4)} +@end example" + (define (map-selected-alist-keys-helper function key alist) + (map + (lambda (pair) + (if (equal? key (car pair)) + (cons key (function (cdr pair))) + pair)) + alist)) + (if (null? keys) + alist + (map-selected-alist-keys + function + (cdr keys) + (map-selected-alist-keys-helper function (car keys) alist)))) + ;;;;;;;;;;;;;;;; ;; vector @@ -344,7 +388,8 @@ bookoutput function" (helper lst 0)) (define-public (count-list lst) - "Given lst (E1 E2 .. ) return ((E1 . 1) (E2 . 2) ... ) " + "Given @var{lst} as @code{(E1 E2 .. )}, return +@code{((E1 . 1) (E2 . 2) ... )}." (define (helper l acc count) (if (pair? l) @@ -355,7 +400,7 @@ bookoutput function" (reverse (helper lst '() 1))) (define-public (list-join lst intermediate) - "put INTERMEDIATE between all elts of LST." + "Put @var{intermediate} between all elts of @var{lst}." (fold-right (lambda (elem prev) @@ -369,7 +414,7 @@ bookoutput function" (lambda (x) x) (map proc lst))) -(define (flatten-list x) +(define-public (flatten-list x) "Unnest list." (cond ((null? x) '()) ((not (pair? x)) (list x)) @@ -381,7 +426,8 @@ bookoutput function" (lset-difference eq? a b)) (define-public (uniq-list lst) - "Uniq LST, assuming that it is sorted. Uses equal? for comparisons." + "Uniq @var{lst}, assuming that it is sorted. Uses @code{equal?} +for comparisons." (reverse! (fold (lambda (x acc) @@ -394,7 +440,7 @@ bookoutput function" (define (split-at-predicate pred lst) "Split LST into two lists at the first element that returns #f for - (PRED previous_element element). Return the two parts as a pair. + (PRED previous_element element). Return the two parts as a pair. Example: (split-at-predicate < '(1 2 3 2 1)) ==> ((1 2 3) . (2 1))" (if (null? lst) (list lst) @@ -406,9 +452,10 @@ bookoutput function" (list lst))))) (define-public (split-list-by-separator lst pred) - "Split LST at each element that satisfies PRED, and return the parts - (with the separators removed) as a list of lists. Example: - (split-list-by-separator '(a 0 b c 1 d) number?) ==> ((a) (b c) (d))" + "Split @var{lst} at each element that satisfies @var{pred}, and return +the parts (with the separators removed) as a list of lists. For example, +executing @samp{(split-list-by-separator '(a 0 b c 1 d) number?)} returns +@samp{((a) (b c) (d))}." (let loop ((result '()) (lst lst)) (if (and lst (not (null? lst))) (loop @@ -444,7 +491,7 @@ bookoutput function" (cons (- expr) expr)) (define-public (interval-length x) - "Length of the number-pair X, when an interval" + "Length of the number-pair @var{x}, if an interval." (max 0 (- (cdr x) (car x)))) (define-public (ordered-cons a b) @@ -455,14 +502,15 @@ bookoutput function" ((if (= dir RIGHT) cdr car) interval)) (define-public (interval-index interval dir) - "Interpolate INTERVAL between between left (DIR=-1) and right (DIR=+1)" + "Interpolate @var{interval} between between left (@var{dir}=-1) and +right (@var{dir}=+1)." (* (+ (interval-start interval) (interval-end interval) (* dir (- (interval-end interval) (interval-start interval)))) 0.5)) (define-public (interval-center x) - "Center the number-pair X, when an interval" + "Center the number-pair @var{x}, if an interval." (if (interval-empty? x) 0.0 (/ (+ (car x) (cdr x)) 2))) @@ -471,27 +519,25 @@ bookoutput function" (define-public interval-end cdr) -(define-public (interval-translate iv amount) - (cons (+ amount (car iv)) - (+ amount (cdr iv)))) - (define (other-axis a) (remainder (+ a 1) 2)) (define-public (interval-widen iv amount) - (cons (- (car iv) amount) - (+ (cdr iv) amount))) + (cons (- (car iv) amount) + (+ (cdr iv) amount))) (define-public (interval-empty? iv) (> (car iv) (cdr iv))) (define-public (interval-union i1 i2) - (cons (min (car i1) (car i2)) - (max (cdr i1) (cdr i2)))) + (cons + (min (car i1) (car i2)) + (max (cdr i1) (cdr i2)))) (define-public (interval-intersection i1 i2) - (cons (max (car i1) (car i2)) - (min (cdr i1) (cdr i2)))) + (cons + (max (car i1) (car i2)) + (min (cdr i1) (cdr i2)))) (define-public (interval-sane? i) (not (or (nan? (car i)) @@ -504,6 +550,105 @@ bookoutput function" (cons (min (interval-start interval) p) (max (interval-end interval) p))) +(define-public (reverse-interval iv) + (cons (cdr iv) (car iv))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; coordinates + +(define coord-x car) +(define coord-y cdr) + +(define (coord-operation operator operand coordinate) + (if (pair? operand) + (cons (operator (coord-x operand) (coord-x coordinate)) + (operator (coord-y operand) (coord-y coordinate))) + (cons (operator operand (coord-x coordinate)) + (operator operand (coord-y coordinate))))) + +(define (coord-apply function coordinate) + (if (pair? function) + (cons + ((coord-x function) (coord-x coordinate)) + ((coord-y function) (coord-y coordinate))) + (cons + (function (coord-x coordinate)) + (function (coord-y coordinate))))) + +(define-public (coord-translate coordinate amount) + (coord-operation + amount coordinate)) + +(define-public (coord-scale coordinate amount) + (coord-operation * amount coordinate)) + +(define-public (coord-rotate coordinate degrees-in-radians) + (let* + ((coordinate + (cons + (exact->inexact (coord-x coordinate)) + (exact->inexact (coord-y coordinate)))) + (radius + (sqrt + (+ (* (coord-x coordinate) (coord-x coordinate)) + (* (coord-y coordinate) (coord-y coordinate))))) + (angle (angle-0-2pi (atan (coord-y coordinate) (coord-x coordinate))))) + (cons + (* radius (cos (+ angle degrees-in-radians))) + (* radius (sin (+ angle degrees-in-radians)))))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; trig + +(define-public PI (* 4 (atan 1))) + +(define-public TWO-PI (* 2 PI)) + +(define-public PI-OVER-TWO (/ PI 2)) + +(define-public THREE-PI-OVER-TWO (* 3 PI-OVER-TWO)) + +(define-public (cyclic-base-value value cycle) + "Take @var{value} and modulo-maps it between 0 and base @var{cycle}." + (if (< value 0) + (cyclic-base-value (+ value cycle) cycle) + (if (>= value cycle) + (cyclic-base-value (- value cycle) cycle) + value))) + +(define-public (angle-0-2pi angle) + "Take @var{angle} (in radians) and maps it between 0 and 2pi." + (cyclic-base-value angle TWO-PI)) + +(define-public (angle-0-360 angle) + "Take @var{angle} (in degrees) and maps it between 0 and 360 degrees." + (cyclic-base-value angle 360.0)) + +(define-public PI-OVER-180 (/ PI 180)) + +(define-public (degrees->radians angle-degrees) + "Convert the given angle from degrees to radians." + (* angle-degrees PI-OVER-180)) + +(define-public (ellipse-radius x-radius y-radius angle) + (/ + (* x-radius y-radius) + (sqrt + (+ (* (expt y-radius 2) + (* (cos angle) (cos angle))) + (* (expt x-radius 2) + (* (sin angle) (sin angle))))))) + +(define-public (polar->rectangular radius angle-in-degrees) + "Return polar coordinates (@var{radius}, @var{angle-in-degrees}) +as rectangular coordinates @ode{(x-length . y-length)}." + + (let ((complex (make-polar + radius + (degrees->radians angle-in-degrees)))) + (cons + (real-part complex) + (imag-part complex)))) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; string @@ -541,14 +686,15 @@ bookoutput function" (ly:number->string (cdr c)))) (define-public (dir-basename file . rest) - "Strip suffixes in REST, but leave directory component for FILE." + "Strip suffixes in @var{rest}, but leave directory component for +@var{file}." (define (inverse-basename x y) (basename y x)) (simple-format #f "~a/~a" (dirname file) (fold inverse-basename file rest))) (define-public (write-me message x) - "Return X. Display MESSAGE and write X. Handy for debugging, -possibly turned off." + "Return @var{x}. Display @var{message} and write @var{x}. +Handy for debugging, possibly turned off." (display message) (write x) (newline) x) ;; x) @@ -570,7 +716,7 @@ possibly turned off." (cons (f (car x)) (f (cdr x)))) (define-public (list-insert-separator lst between) - "Create new list, inserting BETWEEN between elements of LIST" + "Create new list, inserting @var{between} between elements of @var{lst}." (define (conc x y ) (if (eq? y #f) (list x) @@ -600,6 +746,23 @@ possibly turned off." (reverse matches)) +(define-public (random-string pool n) + "Produces a random lowercase string of length n" + (define (helper alphabet out num) + (let ((rand (random (string-length pool)))) + (if (< num 1) + out + (helper alphabet + (string-concatenate `(,out + ,(substring alphabet + rand + (+ 1 rand)))) + (- num 1))))) + (helper pool "" n)) + +(define-public (random-lowercase-string n) + (random-string "abcdefghijklmnopqrstuvwxyz" n)) + ;;;;;;;;;;;;;;;; ;; other @@ -610,7 +773,7 @@ possibly turned off." (define-public (binary-search start end getter target-val) (_i "Find the index between @var{start} and @var{end} (an integer) -which will produce the closest match to @var{target-val} when +which produces the closest match to @var{target-val} if applied to function @var{getter}.") (if (<= end start) start @@ -626,6 +789,9 @@ applied to function @var{getter}.") (define-public (car< a b) (< (car a) (car b))) +(define-public (car<= a b) + (<= (car a) (car b))) + (define-public (symbolstring lst) (symbol->string r))) @@ -633,9 +799,9 @@ applied to function @var{getter}.") (stringstring (car lst)) (symbol->string (car r)))) (define-public (eval-carefully symbol module . default) - "Check if all symbols in expr SYMBOL are reachable - in module MODULE. In that case evaluate, otherwise - print a warning and set an optional DEFAULT." + "Check whether all symbols in expr @var{symbol} are reachable +in module @var{module}. In that case evaluate, otherwise +print a warning and set an optional @var{default}." (let* ((unavailable? (lambda (sym) (not (module-defined? module sym)))) (sym-unavailable (if (pair? symbol)