git.fiddlerwoaroof.com
Browse code

initial

Ed L authored on 04/07/2014 01:18:09
Showing 2 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,2 @@
1
+*.fasl
2
+*.swp
0 3
new file mode 100644
... ...
@@ -0,0 +1,141 @@
1
+(defpackage quadtree
2
+  (:use :cl)
3
+  (:export make-rect quick-point new-quadtree contains-point intersects-boundary
4
+           apply-to-children query-range insert))
5
+
6
+(in-package :quadtree)
7
+
8
+(defstruct point x y)
9
+(defstruct size  w h)
10
+(defstruct bounds center size)
11
+(defun make-rect (x y w h)
12
+  (make-bounds
13
+    :center (make-point :x x :y y)
14
+    :size   (make-size :w w :h h)))
15
+
16
+(defclass quadtree ()
17
+
18
+  ((bounds :initarg :bounds
19
+           :initform (error "must provide boundaries"))
20
+   (point-count :initform 1)
21
+   (points :initform (make-array 5 :fill-pointer 0 :adjustable t))
22
+   (nw :initform nil :type quadtree)
23
+   (ne :initform nil :type quadtree)
24
+   (sw :initform nil :type quadtree)
25
+   (se :initform nil :type quadtree)))
26
+
27
+(defun twiddle (lis prefix &rest end)
28
+  (cond ((null lis) `(progn ,@end))
29
+        (t (let ((a (car lis))
30
+                 (b (cdr lis)))
31
+             `(,prefix ,(car a) ,(cadr a) ,(apply #'twiddle b prefix end))))))
32
+
33
+(defmacro mwith-slots (arg-forms &body rst)
34
+  (apply #'twiddle arg-forms 'with-slots rst))
35
+
36
+(defmacro with-foursides (names center size &body body)
37
+  "Binds the coordinates of the foor sides of a region: the names are bound to w,e,s,n in that order"
38
+  `(mwith-slots (((x y) ,center)
39
+                 ((w h) ,size))
40
+     (let ((,(first names) (- x w))
41
+           (,(second names) (+ x w))
42
+           (,(third names) (- y h))
43
+           (,(fourth names) (+ y h)))
44
+       ,@body)))
45
+
46
+(defmacro with-bounds-foursides (names bounds &body bod)
47
+  `(with-slots ((a center) (b size)) ,bounds
48
+     (with-foursides ,names a b ,@bod)))
49
+
50
+(defun contains-point (bounds point)
51
+  (with-slots ((x2 x) (y2 y)) point
52
+    (with-bounds-foursides (w_x e_x s_y n_y) bounds
53
+      (and (and (<= w_x x2) (> e_x x2))
54
+           (and (<= s_y y2) (> n_y y2))))))
55
+
56
+(defun between (x a b)
57
+  "Return T if x is between a and b (order insensitive)"
58
+  (if (> a b) (rotatef a b)) (and (< a x) (< x b)))
59
+
60
+(defun quick-point (x y) (make-point :x x :y y))
61
+
62
+(defun intersects~boundary (bounds1 bounds2)
63
+  (flet
64
+    ((inner (bounds1 bounds2)
65
+       (with-bounds-foursides (w_x1 e_x1 s_y1 n_y1) bounds1
66
+         (with-bounds-foursides (w_x2 e_x2 s_y2 n_y2) bounds2
67
+           (or (contains-point bounds1 (quick-point w_x2 n_y2))
68
+               (contains-point bounds1 (quick-point e_x2 n_y2))
69
+               (contains-point bounds1 (quick-point w_x2 s_y2))
70
+               (contains-point bounds1 (quick-point e_x2 s_y2)))
71
+           ))))
72
+    (or (inner bounds1 bounds2) (inner bounds2 bounds1))))
73
+
74
+(defmacro combine (names func first_forms second_forms &body body)
75
+  (flet ((name_combs (n1s n2s) (loop for n1 in n1s append (loop for n2 in n2s collect (list n1 n2)))))
76
+    `(let
77
+       ,(loop for n in names and cs in (name_combs first_forms second_forms) collect (list n (cons func cs)))
78
+       (progn ,@body))))
79
+
80
+(defun subdivide (quadtree)
81
+  (flet ((divquad (bounds)
82
+           (mwith-slots (((center size) bounds)
83
+                         ((x y) center)
84
+                         ((w h) size))
85
+             (let* ((half_w (/ w 2))
86
+                    (w_x (- x half_w))
87
+                    (e_x (+ x half_w))
88
+                    (half_h (/ h 2))
89
+                    (n_y (- y half_h))
90
+                    (s_y (+ y half_h))
91
+                    (newsize (make-size :w half_w :h half_h)))
92
+               (combine (p1 p2 p3 p4) quick-point (n_y s_y) (w_x e_x)
93
+                 (vector (make-bounds :center p1 :size newsize)
94
+                       (make-bounds :center p2 :size newsize)
95
+                       (make-bounds :center p3 :size newsize)
96
+                       (make-bounds :center p4 :size newsize)))))))
97
+    (let ((bounds (divquad (slot-value quadtree 'bounds))))
98
+      (with-slots (nw sw ne se) quadtree
99
+        (if (null nw) (setf nw (make-instance 'quadtree :bounds (elt bounds 0))))
100
+        (if (null ne) (setf ne (make-instance 'quadtree :bounds (elt bounds 1)))) 
101
+        (if (null sw) (setf sw (make-instance 'quadtree :bounds (elt bounds 2)))) 
102
+        (if (null se) (setf se (make-instance 'quadtree :bounds (elt bounds 3))))))))
103
+
104
+(defmacro apply-to-children (quadtree func &rest args)
105
+  (let*
106
+    ((nw (gensym))
107
+     (ne (gensym))
108
+     (sw (gensym))
109
+     (se (gensym))
110
+     (qt (gensym))
111
+     )
112
+    `(let ((,qt quadtree))
113
+       (with-slots (,nw ,ne ,sw ,se) ,qt
114
+         ,@(loop for x in `(,nw ,ne ,sw ,se) collect
115
+                 `(,func ,x ,@args))))))
116
+
117
+(defun insert (quadtree point)
118
+  (with-slots (bounds points point-count nw ne sw se) quadtree
119
+    (if (contains-point bounds point)
120
+      (if (< (length points) point-count)
121
+        (progn
122
+          (vector-push-extend point points)
123
+          t)
124
+        (progn
125
+          (subdivide quadtree)
126
+          (or (apply-to-children quadtree insert point))))
127
+      nil))) 
128
+
129
+(defun query-range (quadtree bounds)
130
+  (let ((pir (make-array 5 :fill-pointer 0 :adjustable t)))
131
+    (with-slots (qbounds qpoints nw) quadtree
132
+      (if (intersects-boundary qbounds bounds)
133
+        (progn
134
+          (loop for p across qpoints
135
+                if (contains-point bounds p)
136
+                do (vector-push-extend p pir))
137
+          (if (not (null nw))
138
+            (setf pir
139
+                  (concatenate 'vector
140
+                               (apply-to-children quadtree query-range bounds)))))))
141
+    pir))