Update of /project/gsharp/cvsroot/gsharp In directory clnet:/tmp/cvs-serv28358
Modified Files: buffer.lisp gui.lisp measure.lisp modes.lisp sdl.lisp Log Message: Support for semi/sesqui sharp/flat.
* don't declare the type of the accidentals slot any more; we can put that back in a little, after we work out a declarative way of defining all properties of accidentals.
* microsharpen and microflatten commands and functions; define sharpen and flatten in terms of those (and knowing which accidentals are the tonal ones). Keybindings for the commands.
* a more declarative table-based system for kerning accidentals, along with the ability to specify a per-glyph default (and a default default). Choose a sensible default default; also alter the :sharp/:sharp table when +4 steps away, as the previous value was colliding a little too much.
* support for playing the semi accidentals in equal temperament. No support in linear temperament, as I don't know what they mean.
* glyphs defined with a little too much liberal cut'n'paste. Some FIXMEs note the essential differences between the related glyphs.
--- /project/gsharp/cvsroot/gsharp/buffer.lisp 2007/06/18 15:18:17 1.46 +++ /project/gsharp/cvsroot/gsharp/buffer.lisp 2007/06/21 11:14:25 1.47 @@ -219,8 +219,10 @@ (head :initform nil :initarg :head :reader head :type (or (member :whole :half :filled) null)) (accidentals :initform :natural :initarg :accidentals :reader accidentals - :type (member :natural :flat :double-flat - :sharp :double-sharp)) + ;; FIXME: we want :TYPE ACCIDENTAL here but need to + ;; sort out order of definition for that to be useful. + #+nil #+nil + :type (member :natural :flat :double-flat :sharp :double-sharp)) (dots :initform nil :initarg :dots :reader dots :type (or (integer 0 3) null)) (%tie-right :initform nil :initarg :tie-right :accessor tie-right) @@ -230,8 +232,9 @@ (declare (type (integer 0 127) pitch) (type staff staff) (type (or (member :whole :half :filled) null) head) - (type (member :natural :flat :double-flat - :sharp :double-sharp) + ;; FIXME: :TYPE ACCIDENTAL + #+nil #+nil + (type (member :natural :flat :double-flat :sharp :double-sharp) accidentals) (type (or (integer 0 3) null) dots) (ignore head accidentals dots)) @@ -300,9 +303,13 @@ (ecase pitch (0 0) (1 200) (2 400) (3 500) (4 700) (5 900) (6 1100)) (ecase (accidentals note) (:double-flat -200) + (:sesquiflat -150) (:flat -100) + (:semiflat -50) (:natural 0) + (:semisharp 50) (:sharp 100) + (:sesquisharp 150) (:double-sharp 200)))))
;;; regular temperaments are temperaments that --- /project/gsharp/cvsroot/gsharp/gui.lisp 2007/06/18 15:18:17 1.77 +++ /project/gsharp/cvsroot/gsharp/gui.lisp 2007/06/21 11:14:25 1.78 @@ -822,17 +822,61 @@ (add-note element new-note) (setf *current-note* new-note))))
+(defmacro define-microtonal-accidentals (&rest microaccidentals) + `(progn + (setf (symbol-plist 'microsharpen) + ',(loop for (a b) on microaccidentals + if b collect a and collect b + else collect a and collect a)) + (setf (symbol-plist 'microflatten) + ',(loop for (a b) on (reverse microaccidentals) + if b collect a and collect b + else collect a and collect a)) + (deftype accidental () '(member ,@microaccidentals)) + (defun microsharpen (accidental) + (or (getf (symbol-plist 'microsharpen) accidental) + (error 'type-error :datum accidental :expected-type 'microaccidental))) + (defun microflatten (accidental) + (or (getf (symbol-plist 'microflatten) accidental) + (error 'type-error :datum accidental :expected-type 'microaccidental))))) + +(defmacro define-accidentals (&rest accidentals) + `(progn + (deftype accidental () '(member ,@accidentals)) + (defun sharpen (accidental) + (do ((a (microsharpen accidental) (microsharpen a)) + (olda accidental a)) + ((or (eq a olda) (member a ',accidentals)) a))) + (defun flatten (accidental) + (do ((a (microflatten accidental) (microflatten a)) + (olda accidental a)) + ((or (eq a olda) (member a ',accidentals)) a))))) + +(define-microtonal-accidentals :double-flat :sesquiflat :flat :semiflat + :natural + :semisharp :sharp :sesquisharp :double-sharp) + +(define-accidentals :double-flat :flat :natural :sharp :double-sharp) + (define-gsharp-command com-sharper () (let* ((cluster (cur-cluster)) (note (cur-note)) (new-note (make-note (pitch note) (staff note) :head (head note) - :accidentals (ecase (accidentals note) - (:double-sharp :double-sharp) - (:sharp :double-sharp) - (:natural :sharp) - (:flat :natural) - (:double-flat :flat)) + :accidentals (sharpen (accidentals note)) + :dots (dots note)))) + (remove-note note) + (add-note cluster new-note) + (setf *current-note* new-note))) + +(define-gsharp-command com-microsharper () + ;; FIXME: what are CUR-CLUSTER and CUR-NOTE and how do they relate + ;; to CURRENT-CLUSTER &c? + (let* ((cluster (cur-cluster)) + (note (cur-note)) + (new-note (make-note (pitch note) (staff note) + :head (head note) + :accidentals (microsharpen (accidentals note)) :dots (dots note)))) (remove-note note) (add-note cluster new-note) @@ -843,12 +887,18 @@ (note (cur-note)) (new-note (make-note (pitch note) (staff note) :head (head note) - :accidentals (ecase (accidentals note) - (:double-sharp :sharp) - (:sharp :natural) - (:natural :flat) - (:flat :double-flat) - (:double-flat :double-flat)) + :accidentals (flatten (accidentals note)) + :dots (dots note)))) + (remove-note note) + (add-note cluster new-note) + (setf *current-note* new-note))) + +(define-gsharp-command com-microflatter () + (let* ((cluster (cur-cluster)) + (note (cur-note)) + (new-note (make-note (pitch note) (staff note) + :head (head note) + :accidentals (microflatten (accidentals note)) :dots (dots note)))) (remove-note note) (add-note cluster new-note) --- /project/gsharp/cvsroot/gsharp/measure.lisp 2006/06/21 16:31:54 1.32 +++ /project/gsharp/cvsroot/gsharp/measure.lisp 2007/06/21 11:14:25 1.33 @@ -248,48 +248,57 @@ nil (accidentals note)))))
-;;; table of x offsets (in staff steps) of accendentals. -;;; The first index represents a notehead or a type of accidental. -;;; The second index represents a type of accidentsl. -;;; The third index is a vertical distance, measured in difference -;;; in staff steps between the two. -;;; The table entry gives how much the accidental represented by -;;; the second parameter must be positioned to the left of the -;;; first one. -;;; Entries in the table are offset by 5 in the last dimension -;;; so that vertical distances between -5 and 5 can be represented -(defparameter *accidental-offset* - ;;; -5 -4 -3 -2 -1 0 1 2 3 4 5 - #3A((( 0 0 0 3.5 3.5 3.5 3.5 3.5 3.5 1 0) ; notehead - dbl flat - ( 0 0 0 3.5 3.5 3.5 3.5 3.5 3.5 1 0) ; notehead - flat - ( 0 3.5 3.5 3.5 3.5 3.5 3.5 3.5 1 1 0) ; notehead - natural - ( 0 3.5 3.5 3.5 3.5 3.5 3.5 3.5 1 1 0) ; notehead - sharp - ( 0 0 0 3.5 3.5 3.5 3.5 3.5 0 0 0)) ; notehead - dbl sharp - (( 3.8 3.8 3.8 3.8 3.8 3.8 3.8 3.8 3 3 0) ; dbl flat - dbl flat - ( 3.8 3.8 3.8 3.8 3.8 3.8 3.8 3.8 3 3 0) ; dbl flat - flat - ( 3.8 3.8 3.8 3.8 3.8 3.8 3.8 3.8 3 3 0) ; dbl flat - natural - ( 4 4 4 4 4 4 4 4 4 3.5 0) ; dbl flat - sharp - ( 3.8 3.8 3.8 3.8 3.8 3.8 3.8 3.8 0 0 0)) ; dbl flat - dbl sharp - (( 2 2 2 2 2 2 2 2 1.5 1 0) ; flat - dbl flat - ( 2 2 2 2 2 2 2 2 1.5 1 0) ; flat - flat - ( 2 2 2 2 2 2 2 2 1.5 1 0) ; flat - natural - ( 2.4 2.4 2.4 2.4 2.4 2.4 2.4 2.4 2.4 1.5 0) ; flat - sharp - ( 2.4 2.4 2.4 2.4 2.4 2.4 2.4 2.4 0 0 0)) ; flat - dbl sharp - (( 2 2 2 2 2 2 2 2 2 1.5 1.5) ; natural - dbl flat - ( 2 2 2 2 2 2 2 2 2 1.5 1.5) ; natural - flat - ( 2 2 2 2 2 2 2 2 2 1.5 1.5) ; natural - natural - ( 2 2 2 2 2 2 2 2 2 2 2) ; natural - sharp - ( 2 2 2 2 2 2 2 2 1 1 1)) ; natural - dbl sharp - (( 0 2.4 2.4 2.4 2.4 2.4 2.4 2.4 2.0 1.5 1.0) ; sharp - dbl flat - ( 0 2.4 2.4 2.4 2.4 2.4 2.4 2.4 2.0 1.5 1.0) ; sharp - flat - ( 2.4 2.4 2.4 2.4 2.4 2.4 2.4 2.4 2.0 1.5 1.0) ; sharp - natural - ( 2.4 2.4 2.4 2.4 2.4 2.4 2.4 2.4 2.4 2.0 2.0) ; sharp - sharp - ( 0 2.4 2.4 2.4 2.4 2.4 2.4 2.4 2.4 0 0)) ; sharp - dbl sharp - (( 0 0 2.4 2.4 2.4 2.4 2.4 2.4 2 1 0) ; dbl sharp - dbl flat - ( 0 0 2.4 2.4 2.4 2.4 2.4 2.4 2 1 0) ; dbl sharp - flat - ( 0 0 2.4 2.4 2.4 2.4 2.4 2.4 2 1 0) ; dbl sharp - natural - ( 0 2.8 2.8 2.8 2.8 2.8 2.8 2.8 2.8 2.8 0) ; dbl sharp - sharp - ( 0 0 0 2.8 2.8 2.8 2.8 2.8 0 0 0)))) ; dbl sharp - dbl sharp +(defmacro define-accidental-kerning (left right table) + `(let ((plist (getf (symbol-plist 'accidental-kerning) ',right))) + (setf (getf (symbol-plist 'accidental-kerning) ',right) + (cons (cons ',left ',table) + (remove ',left plist :key #'car))))) +(defmacro define-default-accidental-kerning (right table) + `(define-accidental-kerning default ,right ,table)) + +(macrolet ((define-kernings (&rest args) + `(progn ,@(loop for (left right table) on args by #'cdddr + collect `(define-accidental-kerning ,left ,right ,table))))) + (define-kernings + :double-flat :notehead #( 0 0 0 3.5 3.5 3.5 3.5 3.5 3.5 1 0) + :flat :notehead #( 0 0 0 3.5 3.5 3.5 3.5 3.5 3.5 1 0) + :natural :notehead #( 0 3.5 3.5 3.5 3.5 3.5 3.5 3.5 1 1 0) + :sharp :notehead #( 0 3.5 3.5 3.5 3.5 3.5 3.5 3.5 1 1 0) + :double-sharp :notehead #( 0 0 0 3.5 3.5 3.5 3.5 3.5 0 0 0) + + :double-flat :double-flat #(3.8 3.8 3.8 3.8 3.8 3.8 3.8 3.8 3 3 0) + :flat :double-flat #(3.8 3.8 3.8 3.8 3.8 3.8 3.8 3.8 3 3 0) + :natural :double-flat #(3.8 3.8 3.8 3.8 3.8 3.8 3.8 3.8 3 3 0) + :sharp :double-flat #( 4 4 4 4 4 4 4 4 4 3.5 0) + :double-sharp :double-flat #(3.8 3.8 3.8 3.8 3.8 3.8 3.8 3.8 0 0 0) + + :double-flat :flat #( 2 2 2 2 2 2 2 2 1.5 1 0) + :flat :flat #( 2 2 2 2 2 2 2 2 1.5 1 0) + :natural :flat #( 2 2 2 2 2 2 2 2 1.5 1 0) + :sharp :flat #(2.4 2.4 2.4 2.4 2.4 2.4 2.4 2.4 2.4 1.5 0) + :double-sharp :flat #(2.4 2.4 2.4 2.4 2.4 2.4 2.4 2.4 0 0 0) + + :double-flat :natural #( 2 2 2 2 2 2 2 2 2 1.5 1.5) + :flat :natural #( 2 2 2 2 2 2 2 2 2 1.5 1.5) + :natural :natural #( 2 2 2 2 2 2 2 2 2 1.5 1.5) + :sharp :natural #( 2 2 2 2 2 2 2 2 2 2 2) + :double-sharp :natural #( 2 2 2 2 2 2 2 2 1 1 1) + + :double-flat :sharp #( 0 2.4 2.4 2.4 2.4 2.4 2.4 2.4 2.0 1.5 1.0) + :flat :sharp #( 0 2.4 2.4 2.4 2.4 2.4 2.4 2.4 2.0 1.5 1.0) + :natural :sharp #(2.4 2.4 2.4 2.4 2.4 2.4 2.4 2.4 2.0 1.5 1.0) + :sharp :sharp #(2.4 2.4 2.4 2.4 2.4 2.4 2.4 2.4 2.4 2.4 2.0) + :double-sharp :sharp #( 0 2.4 2.4 2.4 2.4 2.4 2.4 2.4 2.4 0 0) + + :double-flat :double-sharp #( 0 0 2.4 2.4 2.4 2.4 2.4 2.4 2 1 0) + :flat :double-sharp #( 0 0 2.4 2.4 2.4 2.4 2.4 2.4 2 1 0) + :natural :double-sharp #( 0 0 2.4 2.4 2.4 2.4 2.4 2.4 2 1 0) + :sharp :double-sharp #( 0 2.8 2.8 2.8 2.8 2.8 2.8 2.8 2.8 2.8 0) + :double-sharp :double-sharp #( 0 0 0 2.8 2.8 2.8 2.8 2.8 0 0 0) + )) + +(defvar *default-accidental-kerning* + #(4.0 4.0 4.0 4.0 4.0 4.0 4.0 4.0 4.0 4.0 4.0))
;;; given 1) a type of accidental 2) its position (in staff steps) 3) ;;; a type of accidental or a type of notehead, and 4) its position, @@ -297,24 +306,16 @@ ;;; steps to the left that it must be moved in order to avoid overlap ;;; with the second one. (defun accidental-distance (acc1 pos1 acc2 pos2) - (let ((dist (- pos2 pos1))) - (if (> (abs dist) 5) - 0 - (aref *accidental-offset* - (ecase acc2 - (:notehead 0) - (:double-flat 1) - (:flat 2) - (:natural 3) - (:sharp 4) - (:double-sharp 5)) - (ecase acc1 - (:double-flat 0) - (:flat 1) - (:natural 2) - (:sharp 3) - (:double-sharp 4)) - (+ dist 5))))) + (let* ((dist (- pos2 pos1)) + (right-info (getf (symbol-plist 'accidental-kerning) acc2)) + (left-right-info (cdr (assoc acc1 right-info))) + (default-right-info (cdr (assoc 'default right-info)))) + (cond + ((> (abs dist) 5) 0) + ((or (not right-info) (and (not left-right-info) (not default-right-info))) + (aref *default-accidental-kerning* (+ dist 5))) + ((not left-right-info) (aref default-right-info (+ dist 5))) + (t (aref left-right-info (+ dist 5))))))
;;; given two notes (where the first one has an accidental, and the ;;; second one may or may not have an accidental) and the conversion --- /project/gsharp/cvsroot/gsharp/modes.lisp 2007/06/10 08:15:29 1.23 +++ /project/gsharp/cvsroot/gsharp/modes.lisp 2007/06/21 11:14:27 1.24 @@ -84,6 +84,8 @@
(set-key 'com-sharper 'cluster-table '((##))) (set-key 'com-flatter 'cluster-table '(#@)) +(set-key 'com-microsharper 'cluster-table '((## :control))) +(set-key 'com-microflatter 'cluster-table '((#@ :control :shift))) (set-key 'com-add-note-c 'cluster-table '(#\C)) (set-key 'com-add-note-d 'cluster-table '(#\D)) (set-key 'com-add-note-e 'cluster-table '(#\E)) --- /project/gsharp/cvsroot/gsharp/sdl.lisp 2006/06/07 22:40:26 1.31 +++ /project/gsharp/cvsroot/gsharp/sdl.lisp 2007/06/21 11:14:27 1.32 @@ -1009,6 +1009,56 @@ ;;; ;;; Accidentals
+(defmethod compute-design ((font font) (shape (eql :semisharp))) + (with-slots ((sld staff-line-distance) + (slt staff-line-thickness) + stem-thickness + yoffset) font + (let* (;; A factor that determines the space between the vertical + ;; bars and the outer edge of the character as a fraction of + ;; the staff line distance + (edge-distance-multiplier 0.2) + ;; A factor that determines the height of the thin part as a + ;; fraction of the staff line distance + (height-multiplier 2.5) + ;; A factor that determines the width of the hole as a fraction of the + ;; staff line distance. + (hole-width-multiplier 0.33) + (hole-width (round (* hole-width-multiplier sld))) + ;; Hope that half a pixel will not be visible and will not influence + ;; the required distance to the noteheads. + ;; + ;; FIXME: this is the only real difference between the + ;; :semisharp and :sesquisharp glyph calculations, and the + ;; :sharp glyph. Find a way to unify the glyph + ;; computations in a proper metafonty way. + (xoffset (if (oddp hole-width) 0.5 0.5)) + (edge-distance (* edge-distance-multiplier sld)) + (width (+ hole-width (* 2 stem-thickness) (* 2 edge-distance))) + ;; FIXME: this leads to a blurry glyph at most sizes: + ;; choose a coordinate which lies on a pixel boundary in + ;; preference. + (xleft (* -0.25 width)) + (xright (- xleft)) + (yleft (* -0.15 width)) + (yright (- yleft)) + ;; The path for the thick part symmetric around (0, 0) + (thickpart (mf (complex xleft yleft) -- (complex xright yright))) + ;; Determine the y coordinate of the previous path at the + ;; cross point of the thin part. Use congruent triangles. + (ythin (/ (* (- xright edge-distance) yright) xright)) + (height (* height-multiplier sld)) + ;; The path for the thin part symmetric around (0, 0) + (thinpart (mf (complex 0 (* 0.5 height)) -- (complex 0 (* -0.5 height))))) + (clim:region-union + (with-pen (rotate (scale +razor+ (* 0.4 sld)) (/ pi 2)) + (clim:region-union (draw-path (translate thickpart + (complex xoffset (+ yoffset (* 0.5 sld))))) + (draw-path (translate thickpart + (complex xoffset (+ yoffset (* -0.5 sld))))))) + (with-pen (scale +razor+ stem-thickness) + (draw-path (translate thinpart (complex xoffset yoffset)))))))) + (defmethod compute-design ((font font) (shape (eql :sharp))) (with-slots ((sld staff-line-distance) (slt staff-line-thickness) @@ -1060,6 +1110,58 @@ (* 0.5 stem-thickness)) (+ yoffset ythin))))))))))
+(defmethod compute-design ((font font) (shape (eql :sesquisharp))) + (with-slots ((sld staff-line-distance) + (slt staff-line-thickness) + stem-thickness + yoffset) font + (let* (;; A factor that determines the space between the vertical + ;; bars and the outer edge of the character as a fraction of + ;; the staff line distance + (edge-distance-multiplier 0.2) + ;; A factor that determines the height of the thin part as a + ;; fraction of the staff line distance + (height-multiplier 2.5) + ;; A factor that determines the width of the hole as a fraction of the + ;; staff line distance. + (hole-width-multiplier 0.33) + (hole-width (round (* hole-width-multiplier sld))) + ;; Hope that half a pixel will not be visible and will not + ;; influence the required distance to the noteheads. + ;; + ;; FIXME: see note in :semisharp glyph at this point + (xoffset (if (oddp hole-width) 0.5 0.5)) + (edge-distance (* edge-distance-multiplier sld)) + (width (+ hole-width (* 2 stem-thickness) (* 2 edge-distance))) + (xleft (* -0.75 width)) + (xright (- xleft)) + (yleft (* -0.15 width)) + (yright (- yleft)) + ;; The path for the thick part symmetric around (0, 0) + (thickpart (mf (complex xleft yleft) -- (complex xright yright))) + ;; Determine the y coordinate of the previous path at the + ;; cross point of the thin part. Use congruent triangles. + (ythin (/ (* (- xright edge-distance) yright) xright)) + (height (* height-multiplier sld)) + ;; The path for the thin part symmetric around (0, 0) + (thinpart (mf (complex 0 (* 0.5 height)) -- (complex 0 (* -0.5 height))))) + (clim:region-union + (with-pen (rotate (scale +razor+ (* 0.4 sld)) (/ pi 2)) + (clim:region-union (draw-path (translate thickpart + (complex xoffset (+ yoffset (* 0.5 sld))))) + (draw-path (translate thickpart + (complex xoffset (+ yoffset (* -0.5 sld))))))) + (with-pen (scale +razor+ stem-thickness) + (clim:region-union + (clim:region-union + (draw-path (translate thinpart + (complex (- xoffset hole-width (* 1 stem-thickness)) + (- yoffset ythin)))) + (draw-path (translate thinpart (complex (- xoffset (* 0 stem-thickness)) yoffset)))) + (draw-path (translate thinpart + (complex (+ xoffset hole-width (* 1 stem-thickness)) + (+ yoffset ythin)))))))))) + (defmethod compute-design ((font font) (shape (eql :double-sharp))) (with-slots ((sld staff-line-distance) xoffset yoffset) font (flet ((c (x y) (complex x y))) @@ -1075,13 +1177,38 @@ (translate (rotate leg (* pi 1.0)) (c xoffset yoffset)) (translate (rotate leg (* pi 1.5)) (c xoffset yoffset))))))))
+(defmethod compute-design ((font font) (shape (eql :semiflat))) + (with-slots ((sld staff-line-distance) stem-thickness) font + (flet ((c (x y) (complex x y))) + (let* ((outer (xyscale (translate (rotate +half-circle+ pi) #c(-0.5 0)) + (* 1 sld) (* 1 sld))) + ;; FIXME: 1.2 here (and in the :sesquiflat glyph, below) + ;; represents the difference in width between the + ;; :semiflat bulge and the regular :flat bulge. Find a + ;; way to share code between the glyphs. + (inner (xyscale (translate (rotate +half-circle+ pi) #c(-0.6 0)) + (* 0.75 sld) (* (/ 0.75 1.2) sld))) + (middle (mf (climi::path-end outer) -- (climi::path-end inner))) + (finish (mf (climi::path-start inner) -- (climi::path-start outer))) + (combined (climi::close-path + (reduce #'clim:region-union + (list outer middle (climi::reverse-path inner) finish))))) + (clim:region-union (translate (rotate (slant combined 0.6) (- (/ pi 2))) + (c (round (- (* -0.2 sld) stem-thickness)) (* -0.5 sld))) + (with-pen (scale +razor+ stem-thickness) + (draw-path (mf (c (- (round (* -0.2 sld)) (* 0.5 stem-thickness)) + (* 1.5 sld)) + -- + (c (- (round (* -0.2 sld)) (* 0.5 stem-thickness)) + (* -0.5 sld)))))))))) + (defmethod compute-design ((font font) (shape (eql :flat))) (with-slots ((sld staff-line-distance) stem-thickness) font (flet ((c (x y) (complex x y))) (let* ((outer (xyscale (translate +half-circle+ #c(-0.5 0)) sld (* 1.2 sld))) (inner (scale (translate +half-circle+ #c(-0.6 0)) - (* 0.8 sld))) + (* 0.75 sld))) (middle (mf (climi::path-end outer) -- (climi::path-end inner))) (finish (mf (climi::path-start inner) -- (climi::path-start outer))) (combined (climi::close-path @@ -1096,6 +1223,38 @@ (c (- (round (* -0.2 sld)) (* 0.5 stem-thickness)) (* -0.5 sld))))))))))
+(defmethod compute-design ((font font) (shape (eql :sesquiflat))) + (with-slots ((sld staff-line-distance) stem-thickness) font + (flet ((c (x y) (complex x y))) + (let* ((outer (xyscale (translate (rotate +half-circle+ pi) #c(-0.5 0)) + (* 1 sld) (* 1 sld))) + (inner (xyscale (translate (rotate +half-circle+ pi) #c(-0.6 0)) + (* 0.75 sld) (* (/ 0.75 1.2) sld))) + (middle (mf (climi::path-end outer) -- (climi::path-end inner))) + (finish (mf (climi::path-start inner) -- (climi::path-start outer))) + (combined (climi::close-path + (reduce #'clim:region-union + (list outer middle (climi::reverse-path inner) finish)))) + (outer1 (xyscale (translate +half-circle+ #c(-0.5 0)) + sld (* 1.2 sld))) + (inner1 (scale (translate +half-circle+ #c(-0.6 0)) + (* 0.75 sld))) + (middle1 (mf (climi::path-end outer1) -- (climi::path-end inner1))) + (finish1 (mf (climi::path-start inner1) -- (climi::path-start outer1))) + (combined1 (climi::close-path + (reduce #'clim:region-union + (list outer1 middle1 (climi::reverse-path inner1) finish1))))) + (clim:region-union (clim:region-union (translate (rotate (slant combined (* 0.6 1.2)) (- (/ pi 2))) + (c (round (- (* -0.2 sld) stem-thickness)) (* -0.5 sld))) + (translate (rotate (slant combined1 -0.6) (- (/ pi 2))) + (c (round (* -0.2 sld)) (* -0.5 sld)))) + (with-pen (scale +razor+ stem-thickness) + (draw-path (mf (c (- (round (* -0.2 sld)) (* 0.5 stem-thickness)) + (* 1.5 sld)) + -- + (c (- (round (* -0.2 sld)) (* 0.5 stem-thickness)) + (* -0.5 sld)))))))))) + (defmethod compute-design ((font font) (shape (eql :double-flat))) (with-slots ((sld staff-line-distance) stem-thickness) font (flet ((c (x y) (complex x y)))