aboutsummaryrefslogtreecommitdiff
path: root/Spear/Math/Octree.hs
diff options
context:
space:
mode:
Diffstat (limited to 'Spear/Math/Octree.hs')
-rw-r--r--Spear/Math/Octree.hs228
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 @@
1module Spear.Math.Octree
2(
3 Octree
4, makeOctree
5, clone
6, Spear.Math.Octree.insert
7, Spear.Math.Octree.map
8, gmap
9)
10where
11
12import Spear.Math.AABB
13import Spear.Math.Collision
14import Spear.Math.Vector
15
16import Control.Applicative ((<*>))
17import Data.List
18import Data.Functor
19import Data.Monoid
20import qualified Data.Foldable as F
21
22-- | An octree.
23data 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.
43makeOctree :: Int -> AABB2 -> Octree e
44makeOctree 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
58subdivide :: AABB2 -> [AABB2]
59subdivide (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.
73clone :: Octree e -> Octree e
74clone (Leaf root ents) = Leaf root []
75clone (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
86keep :: (e -> AABB2 -> CollisionType) -> AABB2 -> e -> Bool
87keep testAABB2 aabb e = test == FullyContainedBy
88 where test = e `testAABB2` aabb
89
90-- | Insert a list of entities into the octree.
91insert :: (e -> AABB2 -> CollisionType) -> Octree e -> [e] -> Octree e
92insert testAABB2 octree es = octree' where (octree', _) = insert' testAABB2 es octree
93
94insert' :: (e -> AABB2 -> CollisionType) -> [e] -> Octree e -> (Octree e, [e])
95
96insert' 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
102insert' 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.
119extract :: Octree e -> (Octree e, [e])
120extract (Leaf root ents) = (Leaf root [], ents)
121extract (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.
136map :: (e -> AABB2 -> CollisionType) -> (e -> e) -> Octree e -> Octree e
137map testAABB2 f o =
138 let (o', outliers) = map' testAABB2 f o
139 in Spear.Math.Octree.insert testAABB2 o' outliers
140
141map' :: (e -> AABB2 -> CollisionType) -> (e -> e) -> Octree e -> (Octree e, [e])
142
143map' 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
149map' 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.
168gmap :: (e -> AABB2 -> CollisionType) -> (e -> e -> e) -> Octree e -> Octree e
169gmap testAABB2 f o =
170 let (o', outliers) = gmap' testAABB2 f o
171 in Spear.Math.Octree.insert testAABB2 o' outliers
172
173gmap' :: (e -> AABB2 -> CollisionType) -> (e -> e -> e) -> Octree e -> (Octree e, [e])
174
175gmap' 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
181gmap' 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
196instance 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
212instance 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