+;;;; LilyPond is free software: you can redistribute it and/or modify
+;;;; it under the terms of the GNU General Public License as published by
+;;;; the Free Software Foundation, either version 3 of the License, or
+;;;; (at your option) any later version.
+;;;;
+;;;; LilyPond is distributed in the hope that it will be useful,
+;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;;;; GNU General Public License for more details.
+;;;;
+;;;; You should have received a copy of the GNU General Public License
+;;;; along with LilyPond. If not, see <http://www.gnu.org/licenses/>.
+
+;
+; Utility functions
+;
+;
+
+(define (string-x-extent start-point end-point)
+ "Return the x-extent of a string that goes from start-point
+to end-point."
+ (let ((x1 (car start-point))
+ (x2 (car end-point)))
+ (if (> x1 x2)
+ (cons x2 x1)
+ (cons x1 x2))))
+
+(define (string-y-extent start-point end-point)
+ "Return the y-extent of a string that goes from start-point
+to end-point."
+ (let ((y1 (cdr start-point))
+ (y2 (cdr end-point)))
+ (if (> y1 y2)
+ (cons y2 y1)
+ (cons y1 y2))))
+
+
+(define (cons-fret new-value old-list)
+ "Put together a fret-list in the format desired by parse-string"
+ (if (eq? old-list '())
+ (list new-value)
+ (cons* new-value old-list)))
+
+(define (get-numeric-from-key keystring)
+ "Get the numeric value from a key of the form k:val"
+ (string->number (substring keystring 2 (string-length keystring))))
+
+(define (numerify mylist)
+ "Convert string values to numeric or character"
+ (if (null? mylist)
+ '()
+ (let ((numeric-value (string->number (car mylist))))
+ (if numeric-value
+ (cons* numeric-value (numerify (cdr mylist)))
+ (cons* (car (string->list (car mylist)))
+ (numerify (cdr mylist)))))))
+
+(define (stepmag mag)
+ "Calculate the font step necessary to get a desired magnification"
+ (* 6 (/ (log mag) (log 2))))
+
+(define (fret-count fret-range)
+ "Calculate the fret count for the diagram given the range of frets in the diagram."
+ (1+ (- (cdr fret-range) (car fret-range))))
+
+(define (subtract-base-fret base-fret dot-list)
+ "Subtract @var{base-fret} from every fret in @var{dot-list}"
+ (if (null? dot-list)
+ '()
+ (let ((this-list (car dot-list)))
+ (cons* (list (car this-list) (- (second this-list) base-fret)
+ (if (null? (cddr this-list))
+ '()
+ (third this-list)))
+ (subtract-base-fret base-fret (cdr dot-list))))))
+
+(define (drop-paren item-list)
+ "Drop a final parentheses from a fret indication list
+@code{item-list} resulting from a terse string specification of barre."
+ (if (> (length item-list) 0)
+ (let* ((max-index (- (length item-list) 1))
+ (last-element (car (list-tail item-list max-index))))
+ (if (or (equal? last-element ")") (equal? last-element "("))
+ (list-head item-list max-index)
+ item-list))
+ item-list))
+
+(define (get-sub-list value master-list)
+ "Get a sub-list whose cadr is equal to @var{value} from @var{master-list}"
+ (if (eq? master-list '())
+ #f
+ (let ((sublist (car master-list)))
+ (if (equal? (cadr sublist) value)
+ sublist
+ (get-sub-list value (cdr master-list))))))
+
+(define (merge-details key alist-list . default)
+ "Return @code{alist-list} entries for @code{key}, in one combined alist.
+There can be two @code{alist-list} entries for a given key. The first
+comes from the override-markup function, the second comes
+from property settings during a regular override.
+This is necessary because some details can be set in one
+place, while others are set in the other. Both details
+lists must be merged into a single alist.
+Return @code{default} (optional, else #f) if not
+found."
+
+ (define (helper key alist-list default)
+ (if (null? alist-list)
+ default
+ (let* ((entry (assoc-get key (car alist-list))))
+ (if entry
+ (append entry (chain-assoc-get key (cdr alist-list) '()))
+ (helper key (cdr alist-list) default)))))
+
+ (helper key alist-list
+ (if (pair? default) (car default) #f)))
+
+;
+; Conversions between fret/string coordinate system and x-y coordinate
+; system.
+;
+; Fret coordinates are measured down the fretboard from the nut,
+; starting at 0.
+;
+; String coordinates are measured from the lowest string, starting at 0.
+;
+; The x-y origin is at the intersection of the nut and the lowest string.
+;
+; X coordinates are positive to the right.
+; Y coordinates are positive up.
+;
+
+(define (negate-extent extent)
+ "Return the extent in an axis opposite to the axis of @code{extent}."
+ (cons (- (cdr extent)) (- (car extent))))
+
+(define (stencil-fretboard-extent stencil fretboard-axis orientation)
+ "Return the extent of @code{stencil} in the @code{fretboard-axis}
+direction."
+ (if (eq? fretboard-axis 'fret)
+ (cond ((eq? orientation 'landscape)
+ (ly:stencil-extent stencil X))
+ ((eq? orientation 'opposing-landscape)
+ (negate-extent (ly:stencil-extent stencil X)))
+ (else
+ (negate-extent (ly:stencil-extent stencil Y))))
+ ; else -- eq? fretboard-axis 'string
+ (cond ((eq? orientation 'landscape)
+ (ly:stencil-extent stencil Y))
+ ((eq? orientation 'opposing-landscape)
+ (negate-extent (ly:stencil-extent stencil Y)))
+ (else
+ (ly:stencil-extent stencil Y)))))
+
+
+(define (stencil-fretboard-offset stencil fretboard-axis orientation)
+ "Return a the stencil coordinates of the center of @code{stencil}
+in the @code{fretboard-axis} direction."
+ (* 0.5 (interval-length
+ (stencil-fretboard-extent stencil fretboard-axis orientation))))
+
+
+(define (string-thickness string thickness-factor)
+ (expt (1+ thickness-factor) (1- string)))
+
+;
+; Functions that create stencils used in the fret diagram
+;