diff options
Diffstat (limited to 'Spear/Math/Octree.hs')
-rw-r--r-- | Spear/Math/Octree.hs | 228 |
1 files changed, 228 insertions, 0 deletions
diff --git a/Spear/Math/Octree.hs b/Spear/Math/Octree.hs new file mode 100644 index 0000000..f5538b4 --- /dev/null +++ b/Spear/Math/Octree.hs | |||
@@ -0,0 +1,228 @@ | |||
1 | module Spear.Math.Octree | ||
2 | ( | ||
3 | Octree | ||
4 | , makeOctree | ||
5 | , clone | ||
6 | , Spear.Math.Octree.insert | ||
7 | , Spear.Math.Octree.map | ||
8 | , gmap | ||
9 | ) | ||
10 | where | ||
11 | |||
12 | import Spear.Math.AABB | ||
13 | import Spear.Math.Collision | ||
14 | import Spear.Math.Vector | ||
15 | |||
16 | import Control.Applicative ((<*>)) | ||
17 | import Data.List | ||
18 | import Data.Functor | ||
19 | import Data.Monoid | ||
20 | import qualified Data.Foldable as F | ||
21 | |||
22 | -- | An octree. | ||
23 | data Octree e | ||
24 | = Octree | ||
25 | { root :: !AABB2 | ||
26 | , ents :: ![e] | ||
27 | , c1 :: !(Octree e) | ||
28 | , c2 :: !(Octree e) | ||
29 | , c3 :: !(Octree e) | ||
30 | , c4 :: !(Octree e) | ||
31 | , c5 :: !(Octree e) | ||
32 | , c6 :: !(Octree e) | ||
33 | , c7 :: !(Octree e) | ||
34 | , c8 :: !(Octree e) | ||
35 | } | ||
36 | | | ||
37 | Leaf | ||
38 | { root :: !AABB2 | ||
39 | , ents :: ![e] | ||
40 | } | ||
41 | |||
42 | -- | Construct an octree using the specified AABB as the root and having the specified depth. | ||
43 | makeOctree :: Int -> AABB2 -> Octree e | ||
44 | makeOctree d root@(AABB2 min max) | ||
45 | | d == 0 = Leaf root [] | ||
46 | | otherwise = Octree root [] c1 c2 c3 c4 c5 c6 c7 c8 | ||
47 | where | ||
48 | boxes = subdivide root | ||
49 | c1 = makeOctree (d-1) $ boxes !! 0 | ||
50 | c2 = makeOctree (d-1) $ boxes !! 1 | ||
51 | c3 = makeOctree (d-1) $ boxes !! 2 | ||
52 | c4 = makeOctree (d-1) $ boxes !! 3 | ||
53 | c5 = makeOctree (d-1) $ boxes !! 4 | ||
54 | c6 = makeOctree (d-1) $ boxes !! 5 | ||
55 | c7 = makeOctree (d-1) $ boxes !! 6 | ||
56 | c8 = makeOctree (d-1) $ boxes !! 7 | ||
57 | |||
58 | subdivide :: AABB2 -> [AABB2] | ||
59 | subdivide (AABB2 min max) = [a1, a2, a3, a4, a5, a6, a7, a8] | ||
60 | where | ||
61 | v = (max-min) / 2 | ||
62 | c = vec2 (x min + x v) (y min + y v) | ||
63 | a1 = AABB2 min c | ||
64 | a2 = AABB2 ( vec2 (x min) (y min)) ( vec2 (x c) (y c) ) | ||
65 | a3 = AABB2 ( vec2 (x min) (y c) ) ( vec2 (x c) (y max)) | ||
66 | a4 = AABB2 ( vec2 (x min) (y c) ) ( vec2 (x c) (y max)) | ||
67 | a5 = AABB2 ( vec2 (x c) (y min)) ( vec2 (x max) (y c) ) | ||
68 | a6 = AABB2 ( vec2 (x c) (y min)) ( vec2 (x max) (y c) ) | ||
69 | a7 = AABB2 ( vec2 (x c) (y c) ) ( vec2 (x max) (y max)) | ||
70 | a8 = AABB2 c max | ||
71 | |||
72 | -- | Clone the structure of the octree. The new octree has no entities. | ||
73 | clone :: Octree e -> Octree e | ||
74 | clone (Leaf root ents) = Leaf root [] | ||
75 | clone (Octree root ents c1 c2 c3 c4 c5 c6 c7 c8) = Octree root [] c1' c2' c3' c4' c5' c6' c7' c8' | ||
76 | where | ||
77 | c1' = clone c1 | ||
78 | c2' = clone c2 | ||
79 | c3' = clone c3 | ||
80 | c4' = clone c4 | ||
81 | c5' = clone c5 | ||
82 | c6' = clone c6 | ||
83 | c7' = clone c7 | ||
84 | c8' = clone c8 | ||
85 | |||
86 | keep :: (e -> AABB2 -> CollisionType) -> AABB2 -> e -> Bool | ||
87 | keep testAABB2 aabb e = test == FullyContainedBy | ||
88 | where test = e `testAABB2` aabb | ||
89 | |||
90 | -- | Insert a list of entities into the octree. | ||
91 | insert :: (e -> AABB2 -> CollisionType) -> Octree e -> [e] -> Octree e | ||
92 | insert testAABB2 octree es = octree' where (octree', _) = insert' testAABB2 es octree | ||
93 | |||
94 | insert' :: (e -> AABB2 -> CollisionType) -> [e] -> Octree e -> (Octree e, [e]) | ||
95 | |||
96 | insert' testAABB2 es (Leaf root ents) = (Leaf root ents', outliers) | ||
97 | where | ||
98 | ents' = ents ++ ents_kept | ||
99 | ents_kept = filter (keep testAABB2 root) es | ||
100 | outliers = filter (not . keep testAABB2 root) es | ||
101 | |||
102 | insert' testAABB2 es (Octree root ents c1 c2 c3 c4 c5 c6 c7 c8) = | ||
103 | (Octree root ents' c1' c2' c3' c4' c5' c6' c7' c8', outliers) | ||
104 | where | ||
105 | ents' = ents ++ ents_kept | ||
106 | new_ents = es ++ ents1 ++ ents2 ++ ents3 ++ ents4 ++ ents5 ++ ents6 ++ ents7 ++ ents8 | ||
107 | ents_kept = filter (keep testAABB2 root) new_ents | ||
108 | outliers = filter (not . keep testAABB2 root) new_ents | ||
109 | (c1', ents1) = insert' testAABB2 es c1 | ||
110 | (c2', ents2) = insert' testAABB2 es c2 | ||
111 | (c3', ents3) = insert' testAABB2 es c3 | ||
112 | (c4', ents4) = insert' testAABB2 es c4 | ||
113 | (c5', ents5) = insert' testAABB2 es c5 | ||
114 | (c6', ents6) = insert' testAABB2 es c6 | ||
115 | (c7', ents7) = insert' testAABB2 es c7 | ||
116 | (c8', ents8) = insert' testAABB2 es c8 | ||
117 | |||
118 | -- | Extract all entities from the octree. The resulting octree has no entities. | ||
119 | extract :: Octree e -> (Octree e, [e]) | ||
120 | extract (Leaf root ents) = (Leaf root [], ents) | ||
121 | extract (Octree root ents c1 c2 c3 c4 c5 c6 c7 c8) = (Octree root [] c1' c2' c3' c4' c5' c6' c7' c8', ents') | ||
122 | where | ||
123 | (c1', ents1) = extract c1 | ||
124 | (c2', ents2) = extract c2 | ||
125 | (c3', ents3) = extract c3 | ||
126 | (c4', ents4) = extract c4 | ||
127 | (c5', ents5) = extract c5 | ||
128 | (c6', ents6) = extract c6 | ||
129 | (c7', ents7) = extract c7 | ||
130 | (c8', ents8) = extract c8 | ||
131 | ents' = ents ++ ents1 ++ ents2 ++ ents3 ++ ents4 ++ ents5 ++ ents6 ++ ents7 ++ ents8 | ||
132 | |||
133 | -- | Apply the given function to the entities in the octree. | ||
134 | -- | ||
135 | -- Entities that break out of their cell are reallocated appropriately. | ||
136 | map :: (e -> AABB2 -> CollisionType) -> (e -> e) -> Octree e -> Octree e | ||
137 | map testAABB2 f o = | ||
138 | let (o', outliers) = map' testAABB2 f o | ||
139 | in Spear.Math.Octree.insert testAABB2 o' outliers | ||
140 | |||
141 | map' :: (e -> AABB2 -> CollisionType) -> (e -> e) -> Octree e -> (Octree e, [e]) | ||
142 | |||
143 | map' testAABB2 f (Leaf root ents) = (Leaf root ents_kept, outliers) | ||
144 | where | ||
145 | ents' = fmap f ents | ||
146 | ents_kept = filter (keep testAABB2 root) ents' | ||
147 | outliers = filter (not . keep testAABB2 root) ents' | ||
148 | |||
149 | map' testAABB2 f (Octree root ents c1 c2 c3 c4 c5 c6 c7 c8) = | ||
150 | (Octree root ents_kept c1' c2' c3' c4' c5' c6' c7' c8', outliers) | ||
151 | where | ||
152 | ents' = (fmap f ents) ++ out1 ++ out2 ++ out3 ++ out4 ++ out5 ++ out6 ++ out7 ++ out8 | ||
153 | ents_kept = filter (keep testAABB2 root) ents' | ||
154 | outliers = filter (not . keep testAABB2 root) ents' | ||
155 | (c1', out1) = map' testAABB2 f c1 | ||
156 | (c2', out2) = map' testAABB2 f c2 | ||
157 | (c3', out3) = map' testAABB2 f c3 | ||
158 | (c4', out4) = map' testAABB2 f c4 | ||
159 | (c5', out5) = map' testAABB2 f c5 | ||
160 | (c6', out6) = map' testAABB2 f c6 | ||
161 | (c7', out7) = map' testAABB2 f c7 | ||
162 | (c8', out8) = map' testAABB2 f c8 | ||
163 | |||
164 | |||
165 | -- | Apply a function to the entity groups in the octree. | ||
166 | -- | ||
167 | -- Entities that break out of their cell are reallocated appropriately. | ||
168 | gmap :: (e -> AABB2 -> CollisionType) -> (e -> e -> e) -> Octree e -> Octree e | ||
169 | gmap testAABB2 f o = | ||
170 | let (o', outliers) = gmap' testAABB2 f o | ||
171 | in Spear.Math.Octree.insert testAABB2 o' outliers | ||
172 | |||
173 | gmap' :: (e -> AABB2 -> CollisionType) -> (e -> e -> e) -> Octree e -> (Octree e, [e]) | ||
174 | |||
175 | gmap' testAABB2 f (Leaf root ents) = (Leaf root ents_kept, outliers) | ||
176 | where | ||
177 | ents' = f <$> ents <*> ents | ||
178 | ents_kept = filter (keep testAABB2 root) ents' | ||
179 | outliers = filter (not . keep testAABB2 root) ents' | ||
180 | |||
181 | gmap' testAABB2 f (Octree root ents c1 c2 c3 c4 c5 c6 c7 c8) = | ||
182 | (Octree root ents_kept c1' c2' c3' c4' c5' c6' c7' c8', outliers) | ||
183 | where | ||
184 | ents'= (f <$> ents <*> ents) ++ out1 ++ out2 ++ out3 ++ out4 ++ out5 ++ out6 ++ out7 ++ out8 | ||
185 | ents_kept = filter (keep testAABB2 root) ents' | ||
186 | outliers = filter (not . keep testAABB2 root) ents' | ||
187 | (c1', out1) = gmap' testAABB2 f c1 | ||
188 | (c2', out2) = gmap' testAABB2 f c2 | ||
189 | (c3', out3) = gmap' testAABB2 f c3 | ||
190 | (c4', out4) = gmap' testAABB2 f c4 | ||
191 | (c5', out5) = gmap' testAABB2 f c5 | ||
192 | (c6', out6) = gmap' testAABB2 f c6 | ||
193 | (c7', out7) = gmap' testAABB2 f c7 | ||
194 | (c8', out8) = gmap' testAABB2 f c8 | ||
195 | |||
196 | instance Functor Octree where | ||
197 | |||
198 | fmap f (Leaf root ents) = Leaf root $ fmap f ents | ||
199 | |||
200 | fmap f (Octree root ents c1 c2 c3 c4 c5 c6 c7 c8) = | ||
201 | Octree root (fmap f ents) c1' c2' c3' c4' c5' c6' c7' c8' | ||
202 | where | ||
203 | c1' = fmap f c1 | ||
204 | c2' = fmap f c2 | ||
205 | c3' = fmap f c3 | ||
206 | c4' = fmap f c4 | ||
207 | c5' = fmap f c5 | ||
208 | c6' = fmap f c6 | ||
209 | c7' = fmap f c7 | ||
210 | c8' = fmap f c8 | ||
211 | |||
212 | instance F.Foldable Octree where | ||
213 | |||
214 | foldMap f (Leaf root ents) = mconcat . fmap f $ ents | ||
215 | |||
216 | foldMap f (Octree root ents c1 c2 c3 c4 c5 c6 c7 c8) = | ||
217 | mconcat (fmap f ents) `mappend` | ||
218 | c1' `mappend` c2' `mappend` c3' `mappend` c4' `mappend` | ||
219 | c5' `mappend` c6' `mappend` c7' `mappend` c8' | ||
220 | where | ||
221 | c1' = F.foldMap f c1 | ||
222 | c2' = F.foldMap f c2 | ||
223 | c3' = F.foldMap f c3 | ||
224 | c4' = F.foldMap f c4 | ||
225 | c5' = F.foldMap f c5 | ||
226 | c6' = F.foldMap f c6 | ||
227 | c7' = F.foldMap f c7 | ||
228 | c8' = F.foldMap f c8 | ||