242 lines
5.9 KiB
Scheme
242 lines
5.9 KiB
Scheme
(define-module (graphgif))
|
|
|
|
(use-modules (cairo)
|
|
(d-)
|
|
(srfi srfi-1)
|
|
(srfi srfi-9 gnu)
|
|
(srfi srfi-43))
|
|
|
|
(re-export (cairo-pattern-create-rgb . create-color))
|
|
|
|
;;;;;;;;;;;;;;;;;;;
|
|
;; Basic Drawing ;;
|
|
;;;;;;;;;;;;;;;;;;;
|
|
|
|
(define-public pi 3.14159)
|
|
(define-public tau (* 2 pi))
|
|
|
|
(define-public black (cairo-pattern-create-rgb 0 0 0))
|
|
(define-public white (cairo-pattern-create-rgb 1 1 1))
|
|
|
|
(define-immutable-record-type <node>
|
|
(node coords edges color)
|
|
node?
|
|
(coords node-coords set-node-coords)
|
|
(edges node-edges set-node-edges)
|
|
(color node-color set-node-color))
|
|
(export node node?
|
|
node-coords set-node-coords
|
|
node-edges set-node-edges
|
|
node-color set-node-color)
|
|
|
|
(define (edge-painter cr graph)
|
|
(lambda (node)
|
|
(cairo-set-source cr black)
|
|
(let ([x (car (node-coords node))]
|
|
[y (cdr (node-coords node))]
|
|
[edges (node-edges node)])
|
|
(for-each
|
|
(lambda (edge)
|
|
(let* ([other (assq-ref graph edge)]
|
|
[ox (car (node-coords other))]
|
|
[oy (cdr (node-coords other))])
|
|
(cairo-move-to cr x y)
|
|
(cairo-line-to cr ox oy)
|
|
(cairo-stroke cr)))
|
|
edges))))
|
|
|
|
(define (node-painter cr)
|
|
(lambda (node)
|
|
(let ([x (car (node-coords node))]
|
|
[y (cdr (node-coords node))]
|
|
[color (or (node-color node) white)])
|
|
(cairo-arc cr x y 4. 0. tau)
|
|
(cairo-set-source cr color)
|
|
(cairo-fill-preserve cr)
|
|
(cairo-set-source cr black)
|
|
(cairo-stroke cr))))
|
|
|
|
(define-public (draw-abstract-graph graph)
|
|
(let* ([surface (cairo-image-surface-create 'rgb24 400 400)]
|
|
[cr (cairo-create surface)])
|
|
(define paint-edges (edge-painter cr graph))
|
|
(define paint-nodes (node-painter cr))
|
|
;; White background
|
|
(cairo-rectangle cr 0 0 400 400)
|
|
(cairo-set-source cr white)
|
|
(cairo-fill cr)
|
|
(for-each paint-edges (map cdr graph))
|
|
(for-each paint-nodes (map cdr graph))
|
|
(cairo-destroy cr)
|
|
surface))
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;
|
|
;; Graph Generation ;;
|
|
;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
(define (idx->x i w)
|
|
(modulo i w))
|
|
|
|
(define (idx->y i w)
|
|
(quotient i w))
|
|
|
|
(define (xy->idx x y w)
|
|
(+ (* y w) x))
|
|
|
|
(define (idx->edges i w)
|
|
(filter-map
|
|
(lambda (offset)
|
|
(let* ([x (idx->x i w)]
|
|
[y (idx->y i w)]
|
|
[ox (+ x (car offset))]
|
|
[oy (+ y (cdr offset))])
|
|
(and
|
|
(not (negative? ox))
|
|
(not (negative? oy))
|
|
(< ox w)
|
|
(xy->idx ox oy w))))
|
|
;; Auto-connect these directions if legal indices
|
|
'(( 0 . -1)
|
|
(-1 . 0)
|
|
(-1 . -1)
|
|
(+1 . -1))))
|
|
|
|
(define lset-unionq (partial lset-union eq?))
|
|
|
|
(define (forward-connect node graph idx)
|
|
(cons idx
|
|
(set-node-edges
|
|
node
|
|
(lset-unionq
|
|
(node-edges node)
|
|
(fold (lambda (entry set)
|
|
(if (memq idx (node-edges (cdr entry)))
|
|
(cons idx set)
|
|
set))
|
|
'()
|
|
graph)))))
|
|
|
|
(define (back-connect edges graph idx)
|
|
(map
|
|
(lambda (entry)
|
|
(cons (car entry)
|
|
(if (memq (car entry) edges)
|
|
(set-node-edges
|
|
(cdr entry)
|
|
(lset-unionq (node-edges (cdr entry)) `(,idx)))
|
|
(cdr entry))))
|
|
graph))
|
|
|
|
(define-public (connect-bidirectionally graph)
|
|
(if (null? graph) '()
|
|
(let* ([idx (caar graph)]
|
|
[node (cdar graph)])
|
|
(cons (forward-connect node (cdr graph) idx)
|
|
(back-connect
|
|
(node-edges node)
|
|
(connect-bidirectionally (cdr graph))
|
|
idx)))))
|
|
|
|
(define-public (generate-web w h)
|
|
(define (make-node i)
|
|
(cons i
|
|
(node
|
|
(cons (+ (* 30 (idx->x i w)) 10)
|
|
(+ (* 30 (idx->y i w)) 10))
|
|
(idx->edges i w)
|
|
#f)))
|
|
(let loop ([i 0]
|
|
[lst '()])
|
|
(if (>= i (* w h))
|
|
(connect-bidirectionally (reverse lst))
|
|
(loop (1+ i) (cons (make-node i) lst)))))
|
|
|
|
(define-public (remove-rect graph w x1 y1 x2 y2)
|
|
;; Assumption is that is a graph generated by generate-web
|
|
;; and that it has not been re-indexed
|
|
;; x1 < x2, y1 < y2
|
|
(define edges-to-remove
|
|
(unfold (lambda (s) (> s (xy->idx x2 y2 w)))
|
|
identity
|
|
(lambda (s)
|
|
(if (>= (idx->x s w) x2)
|
|
(xy->idx x1 (1+ (idx->y s w)) w)
|
|
(1+ s)))
|
|
(xy->idx x1 y1 w)))
|
|
(filter-map
|
|
(lambda (idx/node)
|
|
(if (memq (car idx/node) edges-to-remove)
|
|
#f
|
|
(cons (car idx/node)
|
|
(set-node-edges
|
|
(cdr idx/node)
|
|
(remove
|
|
(lambda (edge) (memq edge edges-to-remove))
|
|
(node-edges (cdr idx/node)))))))
|
|
graph))
|
|
|
|
;;;;;;;;;;;;;;;;;;;
|
|
;; ALGORITHMS!!! ;;
|
|
;;;;;;;;;;;;;;;;;;;
|
|
|
|
;; Heap algorithms
|
|
(define (heap-insert priority item heap)
|
|
(define (fix-up! i heap)
|
|
(if (zero? i)
|
|
heap
|
|
(let* ([parent (quotient i 2)]
|
|
[i-priority (car (vector-ref heap i))]
|
|
[parent-priority (car (vector-ref heap parent))])
|
|
(when (<= i-priority parent-priority)
|
|
(vector-swap! heap i parent))
|
|
(fix-up! parent heap))))
|
|
(fix-up! (vector-length heap) (vector-append heap (vector (cons priority item)))))
|
|
|
|
(define (heap-peek heap)
|
|
(if (vector-empty? heap) #f (vector-ref heap 0)))
|
|
|
|
(define (heap-pop heap)
|
|
(define (fix-down! heap i)
|
|
(let* ([len (vector-length heap)]
|
|
[left (* 2 i)]
|
|
[right (1+ left)])
|
|
(if (< left len)
|
|
(let ([min (if (< right len)
|
|
(argmin (compose car (partial vector-ref heap)) i left right)
|
|
(argmin (compose car (partial vector-ref heap)) i left))])
|
|
(if (= min i)
|
|
heap
|
|
(begin
|
|
(vector-swap! heap i min)
|
|
(fix-down! heap i))))
|
|
heap)))
|
|
(if (vector-empty? heap)
|
|
heap
|
|
(let ([new-heap (vector-copy heap 0 (1- (vector-length heap)))])
|
|
(vector-set! new-heap 0 (vector-ref heap (1- (vector-length heap))))
|
|
(fix-down! new-heap 0))))
|
|
|
|
;;;;;;;;;;;;
|
|
;; Output ;;
|
|
;;;;;;;;;;;;
|
|
|
|
(define (output-to-file surfaces filename)
|
|
(define pngdir (mkdtemp "/tmp/graphgif_XXXXXX"))
|
|
(vector-for-each
|
|
(lambda (i surface)
|
|
(cairo-surface-write-to-png
|
|
surface
|
|
(string-append pngdir "/img-" (number->string i) ".png")))
|
|
surfaces)
|
|
(system* "ffmpeg" "-y"
|
|
"-i" (string-append pngdir "/img-%d.png")
|
|
"-r" "1" "-loop" "0" filename))
|
|
|
|
(define-public (write-graphs-to-file graphs filename)
|
|
(output-to-file (vector-map (lambda (_ graph) (draw-abstract-graph graph)) graphs)
|
|
filename))
|
|
|
|
;; Local Variables:
|
|
;; geiser-scheme-implementation: guile
|
|
;; End:
|