I've written code to do rounded polygons /properly/ in both PostScript
and SVG.  The SVG was a bit tricky because SVG doesn't have
PostScript's arct.  I would greatly appreciate some help changing the
code in lookup.cc to match my new drawing procedures.  I'm still not
sure whether the polygon drawer should take a list of vertices or an
array of vertices.  A list makes more sense for the PostScript
backend, an array makes more sense for the SVG backend.  The attached
implementation takes an array.

The essential advantage of the new code over the old is that it
explicitly draws the outline of the rounded polygon rather than
fudging it by shrinking a polygon and stroking it with a certain line
thickness.  This provides several advantages over the current LilyPond
code:

1.  Full support for non-convex, and even self-intersecting, polygons.
 Currently, Lilypond can't round concave corners.
2.  Intuitive implementation.
3.  Much more flexibility.  In particular, corners can be rounded
differently depending on their geometry.  The current code and the
code I attach give strange results when corners are very sharp or
sides are very short.  I think it would probably be good to make the
rounding radius vary smoothly with the angle, using the exact
specified radius only for 90 degree corners.  I haven't experimented
with it yet, but I have a couple of ideas for how it might vary.

David Feuer
(define (polygon points radius)
  (let ((pointlist (vector->list points)))
    (format "~a~%~a~%~a ~a poly~%"
            (numbers->string4 (map car pointlist))
            (numbers->string4 (map cdr pointlist))
            (vector-length points)
            radius)))


Attachment: polygon.ps
Description: PostScript document

(use-modules (ice-9 receive)
	     (srfi srfi-1))

;; represent 2D vectors as pairs


(define x car)
(define y cdr)
(define mkvec cons)

(define (vbin+ v1 v2)
  (mkvec (+ (x v1) (x v2)) (+ (y v1) (y v2))))


(define (v+ . vectors)
  (fold vbin+ '(0 . 0) vectors))

(define (v* r v)
  (mkvec (* r (x v)) (* r (y v))))

(define (v- v . rest)
  (if (null? rest)
      (mkvec (- (x v)) (- (y v)))
      (v+ v (v* -1 (apply v+ rest)))))

(define (vmag v)
  (sqrt (+ (sqr (x v)) (sqr (y v)))))

(define (vsqrmag v)
  (+ (sqr (x v)) (sqr (y v))))
  
(define (normalize v)
  (v* (/ (vmag v)) v))

(define (findparams p1 p2 p3 r)
  (let ((p1-p2 (v- p1 p2))
	(p3-p2 (v- p3 p2)))
    (let ((v1 (normalize p1-p2))
	  (v3 (normalize p3-p2)))
      (let* ((F (/ (vsqrmag (v- v1 v3)) 4))
	     (scale (* r (sqrt (- (/ 1 F) 1))))

	     (e1 (v* scale v1))
	     (e3 (v* scale v3))
             
	     (ccw? (> (- (* (x v3) (y v1)) (* (x v1) (y v3))) 0)))
	(values (v+ p2 e1) (v+ p2 e3) ccw?)))))

(define (do-vertex index points n radius)
  (receive (endpoint1 endpoint2 ccw?)
	   (findparams (vector-ref points (modulo (- index 1) n))
                       (vector-ref points index)
                       (vector-ref points (modulo (+ index 1) n))
                       radius)
           (format #f "L~a,~a A~a,~a 0 0 ~a ~a,~a~%"
                   (x endpoint1)
                   (y endpoint1)
                   radius
                   radius
		   (if ccw? 1 0)
		   (x endpoint2)
		   (y endpoint2))))

(define (polygon points radius)
  (define n (vector-length points))
  (define startpoint (v* 0.5 (v+ (vector-ref points (- n 1)) (vector-ref points 0))))
  
  (entity 'path ""
	  `(d . ,(format
		   #f "M~a,~a~%~aZ"
		   (x startpoint) (y startpoint)
		   (let loop ((index 0) (str ""))
		     (if (= index n)
		       str
		       (loop (+ index 1)
			     (string-append str
					    (do-vertex index points n radius)))))))
	  '(style . "fill:currentColor; stroke:none")))


_______________________________________________
lilypond-devel mailing list
lilypond-devel@gnu.org
http://lists.gnu.org/mailman/listinfo/lilypond-devel

Reply via email to