;;
;; Generic interface for 2D graphics output.
;;
;; Based on Ocaml code by MatÃas Giovannini.
;;
;; Copyright 2011-2013 Ivan Raikov and the Okinawa Institute of
;; Science and Technology.
;;
;; This program 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.
;;
;; This program 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.
;;
;; A full copy of the GPL license can be found at
;; .
;;
(module groc
(
color? make-color color-r color-g color-b
make-
make-
make-
state? St
state-Monad state-MonadOps state-get state-put state-run
output? Output
output-Monad output-MonadOps
output-objects
EPS-GROC output-EPS
output-points-EPS output-gray-points-EPS
output-boxes-EPS output-boxes-and-points-EPS
)
(import scheme chicken)
(require-library posix)
(import (only posix seconds->utc-time time->string)
(only extras fprintf sprintf pp)
(only srfi-1 filter concatenate fold-right)
(only data-structures ->string))
(require-extension srfi-4 datatype typeclass)
(define make-point vector)
(define (coord i p) (f64vector-ref p i))
(define-record-type color
(make-color r g b)
color?
(r color-r )
(g color-g )
(b color-b )
)
(define-class return bind)
(define-class ( m) seq mapm_ )
(define=> (seq ) (lambda (m f) (bind m (lambda x f))))
(define=> (mapm_ )
(lambda (seq)
(lambda (f l)
(fold-right (lambda (x ax) (seq (f x) ax))
(return '()) l)
)))
(define (Monad->MonadOps m)
(let* ((seq* (seq m))
(mapm_* ((mapm_ m) seq*))
(this (make- m seq* mapm_* )))
this))
(define-datatype state state?
(St (f procedure?)))
(define state-Monad
(let ((return (lambda (a) (St (lambda (s) (cons a s)))))
(bind (lambda (x f)
(cases state x
(St (m)
(St (lambda (s)
(let* ((xs1 (m s)) (x (car xs1))
(s1 (cdr xs1)) (xm1 (f x)))
(cases state xm1
(St (m1) (m1 s1)))))))
))))
(make- return bind)))
(define state-MonadOps (Monad->MonadOps state-Monad))
(define state-get (St (lambda (s) (cons s s))))
(define state-put (lambda (s) (St (lambda (_) (cons '() s)))))
(define (state-run x) (cases state x (St (m) (lambda (s) (cdr (m s))))))
(define-datatype output output? (Output (f procedure?)))
(define output-Monad
(let ((return (lambda (x) (Output (lambda (_) x))))
(bind (lambda (x f)
(cases output x
(Output (m)
(Output
(lambda (out)
(let* ((x (m out)) (xm1 (f x)))
(cases output xm1
(Output (m1) (m1 out)))))
))
))))
(make- return bind)))
(define output-MonadOps (Monad->MonadOps output-Monad))
(define (kprintf k format arguments)
(let ((x (cons format arguments)))
(k x)))
(define (output-fmt fmt . arguments)
(let ((ship (lambda (s) (Output (lambda (out) (fprintf out "~?~%" (car s) (cdr s)))))))
(kprintf ship fmt arguments)))
(define (output-write name x)
(cases output x
(Output (m) (call-with-output-file name m))
))
(define-class ( o) weight gray color save dot line rect poly translate)
(define EPS-GROC
(with-instance (( output-MonadOps))
(let* ((fmt output-fmt)
(weight (lambda (w) (fmt "~A setlinewidth" w)))
(gray (lambda (w) (fmt "~A setgray" w)))
(color (lambda (c) (fmt "~A ~A ~A setrgbcolor" (color-r c) (color-g c) (color-b c))))
(save (lambda (f) (seq (seq (fmt "gsave") f) (fmt "grestore"))))
(moveto (lambda (p) (fmt "~A ~A moveto" (coord 0 p) (coord 1 p))))
(lineto (lambda (p) (fmt "~A ~A lineto" (coord 0 p) (coord 1 p))))
(draw (lambda () (fmt "stroke")))
(paint (lambda () (fmt "fill")))
(path (lambda (f) (seq (seq (fmt "newpath") f) (fmt "closepath"))))
(dot (lambda (p)
(seq (path (fmt "~A ~A currentlinewidth 1.5 mul 0 360 arc" (coord 0 p) (coord 1 p)))
(paint))))
(line (lambda (p q)
(seq (fmt "newpath") (seq (moveto p) (seq (lineto q) draw)))))
(rect (lambda (p1 p2 #!key (fill #f))
(let ((w (- (coord 0 p2) (coord 0 p1))) (h (- (coord 1 p2) (coord 1 p1))))
(fmt "~A ~A ~A ~A rect~A" (coord 0 p1) (coord 1 p1) w h
(if fill "fill" "stroke")))))
(polyline (lambda (closing l)
(if (null? l) (return '())
(let ((p (car l)) (ps (cdr l)))
(seq (path (seq (moveto p) (mapm_ lineto ps))) closing)))))
(poly (lambda (l #!key (fill #f))
(polyline (if fill (paint) (draw)) l)))
(translate (lambda (x y) (fmt "~A ~A translate" x y)))
)
(make- output-MonadOps weight gray color save dot line rect poly translate)
))
)
(define (isotime) (time->string (seconds->utc-time) "%Y%m%dT%H%M%S"))
(define output-EPS
(let ((format-EPS
((lambda=> ()
(let ((fmt output-fmt))
(lambda (margin width height drawing)
(let ((appname "http://www.call-cc.org/egg/groc"))
(seq
(fmt "%%!PS-Adobe-3.0 EPSF-3.0")
(seq (fmt "%%%%BoundingBox: 0 0 ~A ~A"
(truncate (ceiling (+ width (* 2 margin))))
(truncate (ceiling (+ height (* 2 margin)))))
(seq (fmt "%%%%Creator: ~A" appname)
(seq (fmt "%%%%CreationDate: ~A" (isotime))
(seq (fmt "%%%%DocumentData: Clean7Bit")
(seq (fmt "%%%%EndComments")
(seq drawing
(seq (fmt "showpage")
(fmt "%%%%EOF"))
))
))
))
))))) EPS-GROC )))
(lambda (name width height f #!key (margin 0.5))
(output-write (sprintf "~A.eps" name) (format-EPS margin width height f)))))
(define=> (output-objects )
(lambda (output draw)
(lambda (name width height objects #!key (bbox #f) (w 0.9) (translation #f))
(let ((bbox-drawing
(or (and bbox (let ((x1 (list-ref bbox 0)) (y1 (list-ref bbox 1))
(x2 (list-ref bbox 2)) (y2 (list-ref bbox 3)) )
(seq (color (make-color 0.7 0.8 0.1 ))
(seq (weight 3.0)
(poly (list (make-point x1 y1)
(make-point x1 y2)
(make-point x2 y2)
(make-point x2 y1)))
))
))
(return '()) ))
(translation (or (and translation (translate
(car translation)
(cadr translation)))
(return '())))
)
(output (->string (gensym (string->symbol (->string name))))
width height (seq translation
(seq bbox-drawing
(seq (weight w) (seq (gray 0) (draw objects)))
))
)
))
))
(define=> (draw-points )
(lambda (l) (mapm_ dot l)))
(define draw-points-EPS (draw-points EPS-GROC))
(define output-points-EPS ((output-objects EPS-GROC) output-EPS draw-points-EPS))
(define=> (draw-gray-points )
(lambda (l) (mapm_ (lambda (x) (seq (gray (- 1.0 (car x))) (dot (cadr x)))) l)))
(define draw-gray-points-EPS (draw-gray-points EPS-GROC))
(define output-gray-points-EPS ((output-objects EPS-GROC) output-EPS draw-gray-points-EPS))
(define=> (draw-boxes )
(lambda (bl)
(mapm_
(lambda (b)
(let ((x1 (list-ref b 0)) (y1 (list-ref b 1))
(x2 (list-ref b 2)) (y2 (list-ref b 3)) )
(seq (gray 0)
(poly (list (make-point x1 y1) (make-point x1 y2) (make-point x2 y2) (make-point x2 y1))))
))
bl)))
(define draw-boxes-EPS (draw-boxes EPS-GROC))
(define output-boxes-EPS ((output-objects EPS-GROC) output-EPS draw-boxes-EPS))
(define=> (draw-boxes-and-points )
(lambda (bl)
(mapm_
(lambda (bpts)
(let ((b (car bpts)) (pts (cadr bpts))
(c (and (pair? (cddr bpts)) (caddr bpts))))
(let ((x1 (list-ref b 0)) (y1 (list-ref b 1))
(x2 (list-ref b 2)) (y2 (list-ref b 3)) )
(seq
(seq (or (and c (case (car c)
((gray) (gray (cdr c)))
((color) (color (apply make-color (cdr c))))))
(gray 0))
(poly (list (make-point x1 y1)
(make-point x1 y2)
(make-point x2 y2)
(make-point x2 y1)))
)
(mapm_ (lambda (x) (seq (weight (* 0.5 (car x))) (dot (cadr x)))) pts))
))
)
bl)))
(define draw-boxes-and-points-EPS (draw-boxes-and-points EPS-GROC))
(define output-boxes-and-points-EPS ((output-objects EPS-GROC) output-EPS draw-boxes-and-points-EPS))
)