Browse code
initial
Ed L authored on 04/07/2014 01:18:09
Showing 2 changed files
Showing 2 changed files
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)) |