Compare commits
24 Commits
bc61ef7d8f
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| b753c9ee9c | |||
| 3eb52aa430 | |||
| f4b52415dc | |||
| e38cda0261 | |||
| cd2a616ccb | |||
| d0967f0580 | |||
| bcdb95211b | |||
| 641048b56d | |||
| bd704b2c66 | |||
| 151ecb1b1c | |||
| 332b4dc6b3 | |||
| 495e4ac5a5 | |||
| 2da23e143c | |||
| 31f4707fbf | |||
| a3f0ee2eb9 | |||
| 6d6d1eec18 | |||
| 51c3aaa092 | |||
| 4569ddfb1a | |||
| 8c65c0aabd | |||
| 30d2b62033 | |||
| d8bfe4a5b1 | |||
| 87c65bc84e | |||
| da71b1bd85 | |||
| 9e8cf71d3b |
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
*.png
|
||||||
|
*.gif
|
||||||
|
*.webp
|
||||||
|
cgif/libguilecgif.so
|
||||||
|
cgif/config.scm
|
||||||
70
demo.scm
70
demo.scm
@@ -1,52 +1,26 @@
|
|||||||
(use-modules (graphgif)
|
(use-modules (graphgif)
|
||||||
(srfi srfi-1))
|
(d-))
|
||||||
|
|
||||||
(define my-graph
|
(define graph
|
||||||
`(((10 . 10) (1) ,white)
|
(~> (generate-web 10 10)
|
||||||
((30 . 20) () ,red)))
|
(remove-rect 10 1 3 6 3)
|
||||||
|
(remove-rect 10 6 3 6 7)))
|
||||||
|
|
||||||
(define more-complex-graph
|
(define (color-graph graph visited heap)
|
||||||
`(((10 . 10) () ,white)
|
(map (lambda (pair)
|
||||||
((40 . 10) (0) ,white)
|
(cons (car pair)
|
||||||
((25 . 25) (0 1) ,white)
|
(if (memq (car pair) visited)
|
||||||
((10 . 40) (0 2 4) ,white)
|
(set-node-color (cdr pair) red)
|
||||||
((40 . 40) (1 2 3) ,white)))
|
(cdr pair))))
|
||||||
|
graph))
|
||||||
|
(define (make-graph-generator f)
|
||||||
|
(generator
|
||||||
|
(f graph 90 9
|
||||||
|
(lambda (visited heap)
|
||||||
|
(yield (color-graph graph visited heap))))
|
||||||
|
#f))
|
||||||
|
|
||||||
(define (idx->x i w)
|
(define djikstra-generator (make-graph-generator djikstra))
|
||||||
(modulo i w))
|
(define a*-generator (make-graph-generator a*))
|
||||||
|
(write-graphs-to-file "djikstra.webp" djikstra-generator)
|
||||||
(define (idx->y i w)
|
(write-graphs-to-file "astar.webp" a*-generator)
|
||||||
(quotient i w))
|
|
||||||
|
|
||||||
(define (xy->idx x y w)
|
|
||||||
(+ (* y w) x))
|
|
||||||
|
|
||||||
(define (idx->edges i w)
|
|
||||||
(filter-map
|
|
||||||
(lambda (x y)
|
|
||||||
(if (or (negative? (+ (idx->x i w) x))
|
|
||||||
(negative? (+ (idx->y i w) y))
|
|
||||||
(>= (+ (idx->x i w) x) w))
|
|
||||||
#f
|
|
||||||
(xy->idx
|
|
||||||
(+ (idx->x i w) x)
|
|
||||||
(+ (idx->y i w) y)
|
|
||||||
w)))
|
|
||||||
'(+0 -1 -1 +1)
|
|
||||||
'(-1 +0 -1 -1)))
|
|
||||||
|
|
||||||
(define (generate-web w h)
|
|
||||||
(define (make-node i)
|
|
||||||
(list
|
|
||||||
(cons (+ (* 30 (idx->x i w)) 10)
|
|
||||||
(+ (* 30 (idx->y i w)) 10))
|
|
||||||
(idx->edges i w)
|
|
||||||
white))
|
|
||||||
(reverse
|
|
||||||
(let loop ([i 0]
|
|
||||||
[lst '()])
|
|
||||||
(if (>= i (* w h))
|
|
||||||
lst
|
|
||||||
(loop (1+ i) (cons (make-node i) lst))))))
|
|
||||||
|
|
||||||
(write-graph-to-file (generate-web 5 5) (cadr (command-line)))
|
|
||||||
|
|||||||
279
graphgif.scm
279
graphgif.scm
@@ -1,39 +1,53 @@
|
|||||||
(define-module (graphgif))
|
(define-module (graphgif))
|
||||||
|
(use-modules (cairo)
|
||||||
(use-modules (cairo))
|
(d-)
|
||||||
|
(srfi srfi-1)
|
||||||
|
(srfi srfi-9 gnu)
|
||||||
|
(srfi srfi-11)
|
||||||
|
(srfi srfi-43))
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;
|
||||||
;; Basic Drawing ;;
|
;; Basic Drawing ;;
|
||||||
;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
(define-public pi 3.14159)
|
(define-public pi 3.14159) ;; Good enough
|
||||||
(define-public tau (* 2 pi))
|
(define-public tau (* 2 pi))
|
||||||
|
|
||||||
(define-public black (cairo-pattern-create-rgb 0 0 0))
|
(define-public black (cairo-pattern-create-rgb 0 0 0))
|
||||||
(define-public white (cairo-pattern-create-rgb 1 1 1))
|
(define-public white (cairo-pattern-create-rgb 1 1 1))
|
||||||
(define-public red (cairo-pattern-create-rgb 1 0 0))
|
(define-public red (cairo-pattern-create-rgb 1 0 0))
|
||||||
|
|
||||||
|
(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)
|
(define (edge-painter cr graph)
|
||||||
|
"Creates a closure that draws the edges of graph using cr"
|
||||||
(lambda (node)
|
(lambda (node)
|
||||||
(cairo-set-source cr black)
|
(cairo-set-source cr black)
|
||||||
(let ([x (caar node)]
|
(let-values ([(x y) (car+cdr (node-coords node))]
|
||||||
[y (cdar node)]
|
[(edges) (node-edges node)])
|
||||||
[edges (cadr node)])
|
|
||||||
(for-each
|
(for-each
|
||||||
(lambda (edge)
|
(lambda (edge)
|
||||||
(let* ([other (list-ref graph edge)]
|
(let*-values ([(other) (assq-ref graph edge)]
|
||||||
[ox (caar other)]
|
[(ox oy) (car+cdr (node-coords other))])
|
||||||
[oy (cdar other)])
|
|
||||||
(cairo-move-to cr x y)
|
(cairo-move-to cr x y)
|
||||||
(cairo-line-to cr ox oy)
|
(cairo-line-to cr ox oy)
|
||||||
(cairo-stroke cr)))
|
(cairo-stroke cr)))
|
||||||
edges))))
|
edges))))
|
||||||
|
|
||||||
(define (node-painter cr)
|
(define (node-painter cr)
|
||||||
|
"Creates a closure that draws the nodes of graph using cr"
|
||||||
(lambda (node)
|
(lambda (node)
|
||||||
(let ([x (caar node)]
|
(let-values ([(x y) (car+cdr (node-coords node))]
|
||||||
[y (cdar node)]
|
[(color) (or (node-color node) white)])
|
||||||
[color (caddr node)])
|
|
||||||
(cairo-arc cr x y 4. 0. tau)
|
(cairo-arc cr x y 4. 0. tau)
|
||||||
(cairo-set-source cr color)
|
(cairo-set-source cr color)
|
||||||
(cairo-fill-preserve cr)
|
(cairo-fill-preserve cr)
|
||||||
@@ -41,7 +55,8 @@
|
|||||||
(cairo-stroke cr))))
|
(cairo-stroke cr))))
|
||||||
|
|
||||||
(define-public (draw-abstract-graph graph)
|
(define-public (draw-abstract-graph graph)
|
||||||
(let* ([surface (cairo-image-surface-create 'argb32 400 400)]
|
"Creates a cairo surface with graph drawn on it"
|
||||||
|
(let* ([surface (cairo-image-surface-create 'rgb24 400 400)]
|
||||||
[cr (cairo-create surface)])
|
[cr (cairo-create surface)])
|
||||||
(define paint-edges (edge-painter cr graph))
|
(define paint-edges (edge-painter cr graph))
|
||||||
(define paint-nodes (node-painter cr))
|
(define paint-nodes (node-painter cr))
|
||||||
@@ -49,17 +64,237 @@
|
|||||||
(cairo-rectangle cr 0 0 400 400)
|
(cairo-rectangle cr 0 0 400 400)
|
||||||
(cairo-set-source cr white)
|
(cairo-set-source cr white)
|
||||||
(cairo-fill cr)
|
(cairo-fill cr)
|
||||||
(for-each paint-edges graph)
|
(for-each paint-edges (map cdr graph))
|
||||||
(for-each paint-nodes graph)
|
(for-each paint-nodes (map cdr graph))
|
||||||
(cairo-destroy cr)
|
(cairo-destroy cr)
|
||||||
surface))
|
surface))
|
||||||
|
|
||||||
(define-public (write-graph-to-file graph filename)
|
;;;;;;;;;;;;;;;;;;;;;;
|
||||||
(define my-surface (draw-abstract-graph graph))
|
;; Graph Generation ;;
|
||||||
(cairo-surface-write-to-png my-surface filename)
|
;;;;;;;;;;;;;;;;;;;;;;
|
||||||
(cairo-surface-destroy my-surface))
|
|
||||||
|
|
||||||
|
(define (idx->x i w)
|
||||||
|
"Given i and width w, returns the x value"
|
||||||
|
(modulo i w))
|
||||||
|
|
||||||
;; Local Variables:
|
(define (idx->y i w)
|
||||||
;; geiser-scheme-implementation: guile
|
"Given i and width w, returns the y value"
|
||||||
;; End:
|
(quotient i w))
|
||||||
|
|
||||||
|
(define (xy->idx x y w)
|
||||||
|
"Returns the index of x y on width x in a linear buffer"
|
||||||
|
(+ (* y w) x))
|
||||||
|
|
||||||
|
(define (idx->edges i w)
|
||||||
|
"Returns the edges needed to fully connect i on graph width 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
|
||||||
|
;; This combined with forwards and back connections create a
|
||||||
|
;; fully connected graph
|
||||||
|
'(( 0 . -1) ;; Above
|
||||||
|
(-1 . 0) ;; Right
|
||||||
|
(-1 . -1) ;; Right-above
|
||||||
|
(+1 . -1) ;; Left above
|
||||||
|
)))
|
||||||
|
|
||||||
|
(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 (car entry) 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)
|
||||||
|
"Takes a directed graph and connects all edges to make it fully bidirectional"
|
||||||
|
(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)
|
||||||
|
"Creates a fully connected graph of width w and height 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)
|
||||||
|
"Removes a rectangular section of a fully connected rectangular graph"
|
||||||
|
;; 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!!! ;;
|
||||||
|
;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
|
(define-public (heap-insert priority item heap)
|
||||||
|
(define (fix-up! i heap)
|
||||||
|
(if (zero? i)
|
||||||
|
heap
|
||||||
|
(let* ([parent (quotient (1- 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-public (heap-peek heap)
|
||||||
|
(if (vector-empty? heap) #f (vector-ref heap 0)))
|
||||||
|
|
||||||
|
(define-public (heap-pop heap)
|
||||||
|
(define (fix-down! heap i)
|
||||||
|
(let* ([len (vector-length heap)]
|
||||||
|
[left (1+ (* 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 min))))
|
||||||
|
heap)))
|
||||||
|
(if (or (vector-empty? heap) (= (vector-length heap) 1))
|
||||||
|
#()
|
||||||
|
(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))))
|
||||||
|
|
||||||
|
(define (make-inf-queue graph)
|
||||||
|
"Creates a heap of the graph where all priorities are +inf"
|
||||||
|
(list->vector
|
||||||
|
(map (lambda (entry)
|
||||||
|
(cons (inf) (car entry)))
|
||||||
|
graph)))
|
||||||
|
|
||||||
|
(define* (djikstra graph source sink #:optional update)
|
||||||
|
(define unvisited (make-inf-queue graph))
|
||||||
|
(let iter ([heap (heap-insert 0 source unvisited)]
|
||||||
|
[visited '()])
|
||||||
|
(define-values (dist idx) (car+cdr (heap-peek heap)))
|
||||||
|
(cond
|
||||||
|
[(eqv? sink idx) dist]
|
||||||
|
[(memv idx visited) (iter (heap-pop heap) visited)]
|
||||||
|
[(inf? dist) #f]
|
||||||
|
[else
|
||||||
|
(when update (update visited heap))
|
||||||
|
(iter (fold
|
||||||
|
(lambda (edge heap)
|
||||||
|
(heap-insert (+ dist 1) edge heap))
|
||||||
|
(heap-pop heap)
|
||||||
|
(node-edges (assv-ref graph idx)))
|
||||||
|
(cons idx visited))])))
|
||||||
|
(export djikstra)
|
||||||
|
|
||||||
|
(define* (a* graph source sink #:optional update)
|
||||||
|
(define (chebychev idx) ;; Uses the Chebychev distance as a heuristic
|
||||||
|
(let-values ([(x1 y1) (car+cdr (node-coords (assv-ref graph idx)))]
|
||||||
|
[(x2 y2) (car+cdr (node-coords (assv-ref graph sink)))])
|
||||||
|
(max (abs (- x1 x2)) (abs (- y1 y2)))))
|
||||||
|
(define unvisited (make-inf-queue graph))
|
||||||
|
(let iter ([heap (heap-insert (chebychev source) source unvisited)]
|
||||||
|
[visited '()])
|
||||||
|
(define-values (dist idx) (car+cdr (heap-peek heap)))
|
||||||
|
(cond
|
||||||
|
[(eqv? sink idx) dist]
|
||||||
|
[(memv idx visited) (iter (heap-pop heap) visited)]
|
||||||
|
[(inf? dist) #f]
|
||||||
|
[else
|
||||||
|
(when update (update visited heap))
|
||||||
|
(iter (fold
|
||||||
|
(lambda (edge heap)
|
||||||
|
p (heap-insert (+ dist (chebychev edge) 1) edge heap))
|
||||||
|
(heap-pop heap)
|
||||||
|
(node-edges (assv-ref graph idx)))
|
||||||
|
(cons idx visited))])))
|
||||||
|
(export a*)
|
||||||
|
|
||||||
|
;;;;;;;;;;;;
|
||||||
|
;; Output ;;
|
||||||
|
;;;;;;;;;;;;
|
||||||
|
|
||||||
|
(define (output-to-file filename surface-gen)
|
||||||
|
"Takes a filename and a coroutine to generate surfaces, and creates an animation"
|
||||||
|
(define pngdir (mkdtemp "/tmp/graphgif_XXXXXX"))
|
||||||
|
(let loop ([surface (surface-gen)]
|
||||||
|
[i 1])
|
||||||
|
(when surface
|
||||||
|
(cairo-surface-write-to-png
|
||||||
|
surface
|
||||||
|
(string-append pngdir "/img-" (number->string i) ".png"))
|
||||||
|
(loop (surface-gen) (1+ i))))
|
||||||
|
(system* "ffmpeg" "-y"
|
||||||
|
"-i" (string-append pngdir "/img-%d.png")
|
||||||
|
"-loop" "0" filename))
|
||||||
|
|
||||||
|
(define-public (write-graphs-to-file filename graph-gen)
|
||||||
|
"Takes a filename and a couroutine to generate graphs, and creates an animation"
|
||||||
|
(define (surface-gen)
|
||||||
|
(let ([graph (graph-gen)])
|
||||||
|
(and graph (draw-abstract-graph graph))))
|
||||||
|
(output-to-file filename surface-gen))
|
||||||
|
|||||||
9
run.sh
9
run.sh
@@ -1,9 +0,0 @@
|
|||||||
#!/usr/bin/bash
|
|
||||||
|
|
||||||
GRAPH_FILE=/tmp/graph.png
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
guile -L . demo.scm $GRAPH_FILE
|
|
||||||
feh $GRAPH_FILE
|
|
||||||
rm $GRAPH_FILE
|
|
||||||
20
test.scm
Normal file
20
test.scm
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
(use-modules (rnrs base)
|
||||||
|
(d-)
|
||||||
|
(graphgif))
|
||||||
|
|
||||||
|
(begin
|
||||||
|
(display "Testing heap operations...")
|
||||||
|
(newline)
|
||||||
|
(let ([heap (~>> #()
|
||||||
|
(heap-insert (inf) 1)
|
||||||
|
(heap-insert (inf) 2)
|
||||||
|
(heap-insert (inf) 3)
|
||||||
|
(heap-insert 0 1))])
|
||||||
|
(assert (equal? (heap-peek heap) '(0 . 1)))
|
||||||
|
(set! heap (heap-insert 1 2 (heap-pop heap)))
|
||||||
|
(assert (equal? (heap-peek heap) '(1 . 2)))
|
||||||
|
(set! heap (heap-insert 3 3 heap))
|
||||||
|
(assert (equal? (heap-peek heap) '(1 . 2)))
|
||||||
|
(assert (equal? (heap-peek (heap-pop heap)) '(3 . 3))))
|
||||||
|
(assert (equal? (heap-pop #((0 . 0))) #()))
|
||||||
|
(assert (equal? (heap-peek (heap-pop (heap-pop #((1 . 1) (1 . 10) (+inf.0 . 2) (+inf.0 . 3) (1 . 11))))) '(1 . 11))))
|
||||||
Reference in New Issue
Block a user