]> git.donarmstrong.com Git - lilypond.git/blobdiff - scm/stencil.scm
simplify `make-bezier-stencil'
[lilypond.git] / scm / stencil.scm
index db5ff186c37d659a863e9cb0d77e65a0d764c61d..6410eae44c961ca049366d4fb12e3f1202da7780 100644 (file)
@@ -1,6 +1,6 @@
 ;;;; This file is part of LilyPond, the GNU music typesetter.
 ;;;;
-;;;; Copyright (C) 2003--2014 Han-Wen Nienhuys <hanwen@xs4all.nl>
+;;;; Copyright (C) 2003--2015 Han-Wen Nienhuys <hanwen@xs4all.nl>
 ;;;;
 ;;;; LilyPond is free software: you can redistribute it and/or modify
 ;;;; it under the terms of the GNU General Public License as published by
 ;;;; You should have received a copy of the GNU General Public License
 ;;;; along with LilyPond.  If not, see <http://www.gnu.org/licenses/>.
 
-(define (make-bezier-sandwich-stencil coords thick xext yext)
-  (let* ((command-list `(moveto
-                         ,(car (list-ref coords 3))
-                         ,(cdr (list-ref coords 3))
-                         curveto
-                         ,(car (list-ref coords 0))
-                         ,(cdr (list-ref coords 0))
-                         ,(car (list-ref coords 1))
-                         ,(cdr (list-ref coords 1))
-                         ,(car (list-ref coords 2))
-                         ,(cdr (list-ref coords 2))
-                         curveto
-                         ,(car (list-ref coords 4))
-                         ,(cdr (list-ref coords 4))
-                         ,(car (list-ref coords 5))
-                         ,(cdr (list-ref coords 5))
-                         ,(car (list-ref coords 6))
-                         ,(cdr (list-ref coords 6))
-                         closepath)))
-    (ly:make-stencil
-     `(path ,thick `(,@' ,command-list) 'round 'round #t)
-     xext
-     yext)))
+(define (make-bezier-sandwich-stencil coords thick)
+   (make-path-stencil
+       `(moveto
+           ,(car (list-ref coords 0))
+           ,(cdr (list-ref coords 0))
+         curveto
+           ,(car (list-ref coords 1))
+           ,(cdr (list-ref coords 1))
+           ,(car (list-ref coords 2))
+           ,(cdr (list-ref coords 2))
+           ,(car (list-ref coords 3))
+           ,(cdr (list-ref coords 3))
+         curveto
+           ,(car (list-ref coords 4))
+           ,(cdr (list-ref coords 4))
+           ,(car (list-ref coords 5))
+           ,(cdr (list-ref coords 5))
+           ,(car (list-ref coords 0))
+           ,(cdr (list-ref coords 0))
+         closepath)
+       thick
+       1
+       1
+       #t))
 
 (define-public (stack-stencils axis dir padding stils)
   "Stack stencils @var{stils} in direction @var{axis}, @var{dir}, using
@@ -142,26 +143,24 @@ the more angular the shape of the parenthesis."
           (cons inner-control-x upper-control-y))
          (lower-inner-control-point
           (cons inner-control-x lower-control-y)))
-
-    (make-bezier-sandwich-stencil
-     (list
-      ;; Step 4: curve through inner control points
-      ;; to lower end point.
-      upper-inner-control-point
-      lower-inner-control-point
-      lower-end-point
-      ;; Step 3: move to upper end point.
-      upper-end-point
-      ;; Step 2: curve through outer control points
-      ;; to upper end point.
-      lower-outer-control-point
-      upper-outer-control-point
-      upper-end-point
-      ;; Step 1: move to lower end point.
-      lower-end-point)
-     (* 2 half-thickness)
-     (interval-widen x-extent (/ line-width 2))
-     (interval-widen y-extent (/ line-width 2)))))
+  (ly:make-stencil
+    (ly:stencil-expr
+      (make-bezier-sandwich-stencil
+       (list
+        ;; Step 1: move to lower end point.
+        lower-end-point
+        ;; Step 2: curve through outer control points
+        ;; to upper end point.
+        lower-outer-control-point
+        upper-outer-control-point
+        upper-end-point
+        ;; Step 3: curve through inner control points
+        ;; to lower end point.
+        upper-inner-control-point
+        lower-inner-control-point)
+       (min (* 2 half-thickness) line-width)))
+    (interval-widen x-extent (/ line-width 2))
+    (interval-widen y-extent (/ line-width 2)))))
 
 (define-public (parenthesize-stencil
                 stencil half-thickness width angularity padding)
@@ -464,9 +463,9 @@ coordinates for the previous point in the path."
                   ((eq? head 'curveto) 6)
                   (else 0)))
           (coordinates-raw (take rest arity))
-          (absolute? (if (memq head-raw
+          (is-absolute (if (memq head-raw
                            '(rmoveto m rlineto l rcurveto c)) #f #t))
-          (coordinates (if absolute?
+          (coordinates (if is-absolute
                            coordinates-raw
                            ;; convert relative coordinates to absolute by
                            ;; adding them to previous point values
@@ -662,24 +661,92 @@ producing a new stencil."
     (set! stencil (ly:stencil-add outer inner))
     stencil))
 
+(define-public (flip-stencil axis stil)
+  "Flip stencil @var{stil} in the direction of @var{axis}.
+Value @code{X} (or @code{0}) for @var{axis} flips it horizontally.
+Value @code{Y} (or @code{1}) flips it vertically.  @var{stil} is
+flipped in place; its position, the coordinates of its bounding
+box, remains the same."
+  (let* (
+          ;; scale stencil using -1 to flip it and
+          ;; then restore it to its original position
+          (xy (if (= axis X) '(-1 . 1) '(1 . -1)))
+          (flipped-stil (ly:stencil-scale stil (car xy) (cdr xy)))
+          (flipped-ext (ly:stencil-extent flipped-stil axis))
+          (original-ext (ly:stencil-extent stil axis))
+          (offset (- (car original-ext) (car flipped-ext)))
+          (replaced-stil (ly:stencil-translate-axis flipped-stil offset axis)))
+    replaced-stil))
+
 (define-public (stencil-with-color stencil color)
   (ly:make-stencil
    (list 'color color (ly:stencil-expr stencil))
    (ly:stencil-extent stencil X)
    (ly:stencil-extent stencil Y)))
 
-(define-public (stencil-whiteout stencil)
+(define*-public (stencil-whiteout
+                 stil #:optional (thickness 0.3) (color white)
+                 (angle-increments 16) (radial-increments 1))
+  "This function works by creating a series of white or @var{color}
+stencils radially offset from the original stencil with angles from
+0 to 2*pi, at an increment of @code{angle-inc}, and with radii
+from @code{radial-inc} to @var{thickness}.  @var{thickness} is how big
+the white outline is in staff-spaces.  @var{radial-increments} is how
+many copies of the white stencil we make on our way out to thickness.
+@var{angle-increments} is how many copies of the white stencil
+we make between 0 and 2*pi."
+  (if (or (not (positive? angle-increments))
+          (not (positive? radial-increments)))
+      (begin
+       (ly:warning "Both angle-increments and radial-increments must be positive numbers.")
+       stil)
+      (let* ((2pi 6.283185307)
+             (angle-inc (/ 2pi angle-increments))
+             (radial-inc (/ thickness radial-increments)))
+
+        (define (circle-plot ang dec radius original-stil new-stil)
+          ;; ang (angle) and dec (decrement) are in radians, not degrees
+          (if (<= ang 0)
+              new-stil
+              (circle-plot (- ang dec) dec radius original-stil
+                (ly:stencil-add
+                 new-stil
+                 (ly:stencil-translate original-stil
+                   (cons
+                    (* radius (cos ang))
+                    (* radius (sin ang))))))))
+
+        (define (radial-plot radius original-stil new-stil)
+          (if (<= radius 0)
+              new-stil
+              (ly:stencil-add new-stil
+                (radial-plot
+                 (- radius radial-inc)
+                 original-stil
+                 (circle-plot 2pi angle-inc
+                   radius original-stil empty-stencil)))))
+
+        (let ((whiteout-expr
+                (ly:stencil-expr
+                 (stencil-with-color
+                  (radial-plot thickness stil empty-stencil)
+                  color))))
+          (ly:stencil-add
+            (ly:make-stencil
+              `(delay-stencil-evaluation ,(delay whiteout-expr)))
+            stil)))))
+
+(define*-public (stencil-whiteout-box stencil
+                 #:optional (thickness 0) (blot 0) (color white))
+  "@var{thickness} is how far in staff-spaces the white outline
+extends past the extents of @var{stencil}."
   (let*
-      ((x-ext (ly:stencil-extent stencil X))
-       (y-ext (ly:stencil-extent stencil Y))
-
-       )
+   ((x-ext (interval-widen (ly:stencil-extent stencil X) thickness))
+    (y-ext (interval-widen (ly:stencil-extent stencil Y) thickness)))
 
-    (ly:stencil-add
-     (stencil-with-color (ly:round-filled-box x-ext y-ext 0.0)
-                         white)
-     stencil)
-    ))
+   (ly:stencil-add
+    (stencil-with-color (ly:round-filled-box x-ext y-ext blot) color)
+    stencil)))
 
 (define-public (arrow-stencil-maker start? end?)
   "Return a function drawing a line from current point to @code{destination},
@@ -770,10 +837,10 @@ with optional arrows of @code{max-size} on start and end controlled by
                           (make-simple-markup (simple-format #f "~a: NaN/inf" name))))
         (let ((text-stencil (interpret-markup
                              layout text-props
-                             (markup #:whiteout #:simple name)))
+                             (markup #:whiteout-box #:simple name)))
               (dim-stencil (interpret-markup
                             layout text-props
-                            (markup #:whiteout
+                            (markup #:whiteout-box
                                     #:simple (cond
                                               ((interval-empty? extent)
                                                "empty")
@@ -800,7 +867,8 @@ with optional arrows of @code{max-size} on start and end controlled by
 
 ;; TODO: figure out how to annotate padding nicely
 ;; TODO: emphasize either padding or min-dist depending on which constraint was active
-(define*-public (annotate-spacing-spec layout spacing-spec start-Y-offset next-staff-Y
+(define*-public (annotate-spacing-spec layout name spacing-spec
+                                       start-Y-offset next-staff-Y
                                        #:key (base-color blue))
   (let* ((get-spacing-var (lambda (sym) (assoc-get sym spacing-spec 0.0)))
          (space (get-spacing-var 'basic-distance))
@@ -809,21 +877,27 @@ with optional arrows of @code{max-size} on start and end controlled by
          (contrast-color (append (cdr base-color) (list (car base-color))))
          (min-dist-blocks (<= (- start-Y-offset min-dist) next-staff-Y))
          (min-dist-color (if min-dist-blocks contrast-color base-color))
-         (basic-annotation (annotate-y-interval layout
-                                                "basic-dist"
-                                                (cons (- start-Y-offset space) start-Y-offset)
-                                                #t
-                                                #:color (map (lambda (x) (* x 0.25)) base-color)))
-         (min-annotation (annotate-y-interval layout
-                                              "min-dist"
-                                              (cons (- start-Y-offset min-dist) start-Y-offset)
-                                              #t
-                                              #:color min-dist-color))
-         (extra-annotation (annotate-y-interval layout
-                                                "extra dist"
-                                                (cons next-staff-Y (- start-Y-offset min-dist))
-                                                #t
-                                                #:color (map (lambda (x) (* x 0.5)) min-dist-color))))
+         (name-string (if (string-null? name)
+                         ""
+                         (simple-format #f " (~a)" name)))
+         (basic-annotation
+          (annotate-y-interval layout
+                               (simple-format #f "basic-dist~a" name-string)
+                               (cons (- start-Y-offset space) start-Y-offset)
+                               #t
+                               #:color (map (lambda (x) (* x 0.25)) base-color)))
+         (min-annotation
+          (annotate-y-interval layout
+                               (simple-format #f "min-dist~a" name-string)
+                               (cons (- start-Y-offset min-dist) start-Y-offset)
+                               #t
+                               #:color min-dist-color))
+         (extra-annotation
+          (annotate-y-interval layout
+                               (simple-format #f "extra dist~a" name-string)
+                               (cons next-staff-Y (- start-Y-offset min-dist))
+                               #t
+                               #:color (map (lambda (x) (* x 0.5)) min-dist-color))))
 
     (stack-stencils X RIGHT 0.0
                     (list