Compare commits

..

24 Commits

Author SHA1 Message Date
b753c9ee9c More naval gazing 2024-11-19 12:01:07 -06:00
3eb52aa430 More cleanup, fix things broken in last cleanup 2024-11-19 11:53:59 -06:00
f4b52415dc File cleanup bc I'm anal 2024-11-19 11:39:13 -06:00
e38cda0261 Use new generator syntax 2024-11-11 22:15:04 -06:00
cd2a616ccb Implement the a* algorithm 2024-11-11 21:07:19 -06:00
d0967f0580 Use lexicographic comparisons for heap algorithms 2024-11-11 15:44:18 -06:00
bcdb95211b Writer takes a generator, coroutines! 2024-10-29 12:01:56 -05:00
641048b56d Sucessfully implemented Djikstra's algorithm 2024-10-28 16:43:14 -05:00
bd704b2c66 Use (d-) implement heap procedures 2024-10-25 15:40:16 -05:00
151ecb1b1c Add function to bi-directionally connect a graph 2024-10-25 14:04:42 -05:00
332b4dc6b3 Do it without asking 2024-10-23 09:46:15 -05:00
495e4ac5a5 Actually make webp 2024-10-23 09:13:50 -05:00
2da23e143c use vectors 2024-10-22 09:25:54 -05:00
31f4707fbf Remove a section of the web 2024-10-22 08:51:33 -05:00
a3f0ee2eb9 Use records instead of conss for nodes, alist for graphs 2024-10-21 14:05:11 -05:00
6d6d1eec18 Just use ffmpeg 2024-10-21 10:51:41 -05:00
51c3aaa092 Unixify line endings 2024-10-18 10:38:58 -05:00
4569ddfb1a More stuff fuckit 2024-10-18 10:23:46 -05:00
8c65c0aabd fuck autotools, make it work with Make 2024-10-18 09:44:36 -05:00
30d2b62033 autotools integration 2024-10-17 11:52:28 -05:00
d8bfe4a5b1 gifs 2024-10-16 15:46:16 -05:00
87c65bc84e pre-gif re-write 2024-10-16 13:48:22 -05:00
da71b1bd85 Code cleanup 2024-10-11 17:57:45 -05:00
9e8cf71d3b delete temp 2024-10-11 17:51:11 -05:00
5 changed files with 304 additions and 79 deletions

5
.gitignore vendored Normal file
View File

@@ -0,0 +1,5 @@
*.png
*.gif
*.webp
cgif/libguilecgif.so
cgif/config.scm

View File

@@ -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)))

View File

@@ -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
View File

@@ -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
View 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))))