File: quad-tree.lisp

package info (click to toggle)
cl-containers 20170403-1
  • links: PTS, VCS
  • area: main
  • in suites: buster
  • size: 1,072 kB
  • ctags: 1,387
  • sloc: lisp: 8,341; makefile: 14
file content (141 lines) | stat: -rw-r--r-- 5,098 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
(in-package #:containers)


;;; quad-tree

(defclass* quad-tree (initial-contents-mixin
                        classified-container-mixin
                        findable-container-mixin
                        iteratable-container-mixin
                        container-uses-nodes-mixin
                        rooted-tree-container
                        concrete-container)
  ((size 0))
  :automatic-accessors
  :automatic-initargs
  (:default-initargs
    :key 'identity
    :test 'eq))

(defclass* four-child-node (parent-node-mixin)
  ((top-left-child :initform nil
                   :accessor top-left-child)
   (top-right-child :initform nil
                    :accessor top-right-child)
   (bottom-left-child :initform nil
                      :accessor bottom-left-child)
   (bottom-right-child :initform nil
                       :accessor bottom-right-child)))


(defclass* quad-tree-node (four-child-node)
  ((tree :initform nil
         :initarg :tree
         :accessor tree)))


(defmethod make-node-for-container ((tree quad-tree) (item t) &key)
  (if item
    (make-instance 'quad-tree-node
      :element item
      :tree tree)
    nil))


(defmethod node-empty-p ((node quad-tree-node))
  (null (element node)))


(defmethod print-object ((o quad-tree-node) stream)
  (print-unreadable-object (o stream :type t)
    (format stream "~A" (element o))))


(defgeneric notify-element-of-child-status (element status)
  (:documentation "This is called to allow the element to know its
status as a child. Useful for quad tree elements, where an element's position
relative to its parent could be relevant to the element. Status is one of:
:TOP-LEFT, :TOP-RIGHT, :BOTTOM-LEFT, :BOTTOM-RIGHT or :ROOT")
  
  (:method ((element t) (status t))
           (values nil)))


(defmethod insert-item ((tree quad-tree) (item quad-tree-node))
  (loop with key = (key tree)
        with y = (make-node-for-container tree nil)
        with classifier = (classifier tree)
        and x = (root tree)
        and key-item = (funcall key (element item))
        while (not (node-empty-p x))
        do
        (progn
          (setf y x)
          (case (funcall classifier key-item (funcall key (element x)))
            (:TOP-LEFT (setf x (top-left-child x)))
            (:TOP-RIGHT (setf x (top-right-child x)))
            (:BOTTOM-LEFT (setf x (bottom-left-child x)))
            (:BOTTOM-RIGHT (setf x (bottom-right-child x)))))
        
        finally (progn
                  (setf (parent item) y
                        (tree item) tree)
                  (if (node-empty-p y)
                    (progn
                      (notify-element-of-child-status (element item) :ROOT)
                      (setf (root tree) item))
                    (case (funcall classifier key-item (funcall key (element y)))
                      (:TOP-LEFT 
                       (notify-element-of-child-status (element item) :TOP-LEFT)
                       (setf (top-left-child y) item))
                      (:TOP-RIGHT 
                       (notify-element-of-child-status (element item) :TOP-RIGHT)
                       (setf (top-right-child y) item))
                      (:BOTTOM-LEFT 
                       (notify-element-of-child-status (element item) :BOTTOM-LEFT)
                       (setf (bottom-left-child y) item))
                      (:BOTTOM-RIGHT 
                       (notify-element-of-child-status 
                        (element item) :BOTTOM-RIGHT)
                       (setf (bottom-right-child y) item))))))
  (incf (size tree))
  (values tree))


(defmethod empty-p ((tree quad-tree))
  (node-empty-p (root tree)))

(defmethod empty! ((tree quad-tree))
  (setf (root tree) (make-node-for-container tree nil))
  (setf (size tree) 0) 
  (values tree))


;; find-item needs to operate a bit differently -- it must find the
;; node in the tree that minimizes the test (e.g. minimal overlap);
;; therefore, it keeps searching until it finds a node that doesn't
;; pass the test of the container, and returns its parent.
(defmethod find-item ((tree quad-tree) (item quad-tree-node))
  (let ((last-node nil)
        (current (root tree))
        (test (test tree)))
    (loop with key = (key tree)
          with classifier = (classifier tree)
          and key-item = (funcall key (element item))
          while (and (not (node-empty-p current))
                     (funcall test
                              (element item) (element current))) do
          
          (setf last-node current)
          (case (funcall classifier key-item (funcall key (element current)))
            (:TOP-LEFT (setf current (top-left-child current)))
            (:TOP-RIGHT (setf current (top-right-child current)))
            (:BOTTOM-LEFT (setf current (bottom-left-child current)))
            (:BOTTOM-RIGHT (setf current (bottom-right-child current)))
            (otherwise (setf current nil))))
    
    (if (and (not (node-empty-p last-node))
             (funcall test (element item) (element last-node)))
      (values last-node)
      (values nil))))