├── LICENSE.txt
├── README.md
├── godoc.png
├── quadtree.go
└── quadtree_test.go
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Original JavaScript Code: Copyright (c) 2012 Timo Hausmann
4 | Go Code: Copyright (c) 2016 James Milner
5 |
6 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
7 |
8 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
9 |
10 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
11 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # :four_leaf_clover: Quadtree (Go)
2 |
3 | [](https://godoc.org/github.com/JamesLMilner/quadtree-go)
4 |
5 | A quadtree implementation in Go, featuring insertion and retrieval of bounding boxes and points.
6 |
7 | # What is it?
8 |
9 | > "A quadtree is a tree data structure in which each internal node has exactly four children. Quadtrees are most often used to partition a two-dimensional space by recursively subdividing it into four quadrants or regions. The regions may be square or rectangular, or may have arbitrary shapes. This data structure was named a quadtree by Raphael Finkel and J.L. Bentley in 1974." - Wikipedia (2016)
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | # Setup
21 |
22 | Usage
23 | ```go
24 | qt := &quadtree.Quadtree{
25 | Bounds: quadtree.Bounds{
26 | X: 0,
27 | Y: 0,
28 | Width: 100,
29 | Height: 100,
30 | },
31 | MaxObjects: 10,
32 | MaxLevels: 4,
33 | Level: 0,
34 | Objects: make([]quadtree.Bounds, 0),
35 | Nodes: make([]quadtree.Quadtree, 0),
36 | }
37 |
38 | // Insert some boxes
39 | qt.Insert(Bounds{
40 | X: 1,
41 | Y: 1,
42 | Width: 10,
43 | Height: 10,
44 | })
45 | qt.Insert(Bounds{
46 | X: 5,
47 | Y: 5,
48 | Width: 10,
49 | Height: 10,
50 | })
51 | qt.Insert(Bounds{
52 | X: 10,
53 | Y: 10,
54 | Width: 10,
55 | Height: 10,
56 | })
57 | qt.Insert(Bounds{
58 | X: 15,
59 | Y: 15,
60 | Width: 10,
61 | Height: 10,
62 | })
63 |
64 | //Get all the intersections
65 | intersections := qt.RetrieveIntersections(Bounds{
66 | X: 5,
67 | Y: 5,
68 | Width: 2.5,
69 | Height: 2.5,
70 | })
71 |
72 | // Clear the Quadtree
73 | qt.Clear()
74 |
75 |
76 | ```
77 | # Acknowledgements
78 |
79 | This package is based on Timo Hausmann [JavaScript Quadtree implementation](https://github.com/timohausmann/quadtree-js). This is in turn based on [this post](http://gamedev.tutsplus.com/tutorials/implementation/quick-tip-use-quadtrees-to-detect-likely-collisions-in-2d-space/).
80 |
81 | # License
82 | MIT
83 |
84 | Original JavaScript code: Copyright © 2012 Timo Hausmann
85 |
86 | Go code: Copyright © 2016 James Milner
87 |
--------------------------------------------------------------------------------
/godoc.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JamesLMilner/quadtree-go/d12870ffe403f3c0db643ff9b227e61a2256ef61/godoc.png
--------------------------------------------------------------------------------
/quadtree.go:
--------------------------------------------------------------------------------
1 | package quadtree
2 |
3 | // Quadtree - The quadtree data structure
4 | type Quadtree struct {
5 | Bounds Bounds
6 | MaxObjects int // Maximum objects a node can hold before splitting into 4 subnodes
7 | MaxLevels int // Total max levels inside root Quadtree
8 | Level int // Depth level, required for subnodes
9 | Objects []Bounds
10 | Nodes []Quadtree
11 | Total int
12 | }
13 |
14 | // Bounds - A bounding box with a x,y origin and width and height
15 | type Bounds struct {
16 | X float64
17 | Y float64
18 | Width float64
19 | Height float64
20 | }
21 |
22 | //IsPoint - Checks if a bounds object is a point or not (has no width or height)
23 | func (b *Bounds) IsPoint() bool {
24 |
25 | if b.Width == 0 && b.Height == 0 {
26 | return true
27 | }
28 |
29 | return false
30 |
31 | }
32 |
33 | // Intersects - Checks if a Bounds object intersects with another Bounds
34 | func (b *Bounds) Intersects(a Bounds) bool {
35 |
36 | aMaxX := a.X + a.Width
37 | aMaxY := a.Y + a.Height
38 | bMaxX := b.X + b.Width
39 | bMaxY := b.Y + b.Height
40 |
41 | // a is left of b
42 | if aMaxX < b.X {
43 | return false
44 | }
45 |
46 | // a is right of b
47 | if a.X > bMaxX {
48 | return false
49 | }
50 |
51 | // a is above b
52 | if aMaxY < b.Y {
53 | return false
54 | }
55 |
56 | // a is below b
57 | if a.Y > bMaxY {
58 | return false
59 | }
60 |
61 | // The two overlap
62 | return true
63 |
64 | }
65 |
66 | // TotalNodes - Retrieve the total number of sub-Quadtrees in a Quadtree
67 | func (qt *Quadtree) TotalNodes() int {
68 |
69 | total := 0
70 |
71 | if len(qt.Nodes) > 0 {
72 | for i := 0; i < len(qt.Nodes); i++ {
73 | total += 1
74 | total += qt.Nodes[i].TotalNodes()
75 | }
76 | }
77 |
78 | return total
79 |
80 | }
81 |
82 | // split - split the node into 4 subnodes
83 | func (qt *Quadtree) split() {
84 |
85 | if len(qt.Nodes) == 4 {
86 | return
87 | }
88 |
89 | nextLevel := qt.Level + 1
90 | subWidth := qt.Bounds.Width / 2
91 | subHeight := qt.Bounds.Height / 2
92 | x := qt.Bounds.X
93 | y := qt.Bounds.Y
94 |
95 | //top right node (0)
96 | qt.Nodes = append(qt.Nodes, Quadtree{
97 | Bounds: Bounds{
98 | X: x + subWidth,
99 | Y: y,
100 | Width: subWidth,
101 | Height: subHeight,
102 | },
103 | MaxObjects: qt.MaxObjects,
104 | MaxLevels: qt.MaxLevels,
105 | Level: nextLevel,
106 | Objects: make([]Bounds, 0),
107 | Nodes: make([]Quadtree, 0, 4),
108 | })
109 |
110 | //top left node (1)
111 | qt.Nodes = append(qt.Nodes, Quadtree{
112 | Bounds: Bounds{
113 | X: x,
114 | Y: y,
115 | Width: subWidth,
116 | Height: subHeight,
117 | },
118 | MaxObjects: qt.MaxObjects,
119 | MaxLevels: qt.MaxLevels,
120 | Level: nextLevel,
121 | Objects: make([]Bounds, 0),
122 | Nodes: make([]Quadtree, 0, 4),
123 | })
124 |
125 | //bottom left node (2)
126 | qt.Nodes = append(qt.Nodes, Quadtree{
127 | Bounds: Bounds{
128 | X: x,
129 | Y: y + subHeight,
130 | Width: subWidth,
131 | Height: subHeight,
132 | },
133 | MaxObjects: qt.MaxObjects,
134 | MaxLevels: qt.MaxLevels,
135 | Level: nextLevel,
136 | Objects: make([]Bounds, 0),
137 | Nodes: make([]Quadtree, 0, 4),
138 | })
139 |
140 | //bottom right node (3)
141 | qt.Nodes = append(qt.Nodes, Quadtree{
142 | Bounds: Bounds{
143 | X: x + subWidth,
144 | Y: y + subHeight,
145 | Width: subWidth,
146 | Height: subHeight,
147 | },
148 | MaxObjects: qt.MaxObjects,
149 | MaxLevels: qt.MaxLevels,
150 | Level: nextLevel,
151 | Objects: make([]Bounds, 0),
152 | Nodes: make([]Quadtree, 0, 4),
153 | })
154 |
155 | }
156 |
157 | // getIndex - Determine which quadrant the object belongs to (0-3)
158 | func (qt *Quadtree) getIndex(pRect Bounds) int {
159 |
160 | index := -1 // index of the subnode (0-3), or -1 if pRect cannot completely fit within a subnode and is part of the parent node
161 |
162 | verticalMidpoint := qt.Bounds.X + (qt.Bounds.Width / 2)
163 | horizontalMidpoint := qt.Bounds.Y + (qt.Bounds.Height / 2)
164 |
165 | //pRect can completely fit within the top quadrants
166 | topQuadrant := (pRect.Y < horizontalMidpoint) && (pRect.Y+pRect.Height < horizontalMidpoint)
167 |
168 | //pRect can completely fit within the bottom quadrants
169 | bottomQuadrant := (pRect.Y > horizontalMidpoint)
170 |
171 | //pRect can completely fit within the left quadrants
172 | if (pRect.X < verticalMidpoint) && (pRect.X+pRect.Width < verticalMidpoint) {
173 |
174 | if topQuadrant {
175 | index = 1
176 | } else if bottomQuadrant {
177 | index = 2
178 | }
179 |
180 | } else if pRect.X > verticalMidpoint {
181 | //pRect can completely fit within the right quadrants
182 |
183 | if topQuadrant {
184 | index = 0
185 | } else if bottomQuadrant {
186 | index = 3
187 | }
188 |
189 | }
190 |
191 | return index
192 |
193 | }
194 |
195 | // Insert - Insert the object into the node. If the node exceeds the capacity,
196 | // it will split and add all objects to their corresponding subnodes.
197 | func (qt *Quadtree) Insert(pRect Bounds) {
198 |
199 | qt.Total++
200 |
201 | i := 0
202 | var index int
203 |
204 | // If we have subnodes within the Quadtree
205 | if len(qt.Nodes) > 0 == true {
206 |
207 | index = qt.getIndex(pRect)
208 |
209 | if index != -1 {
210 | qt.Nodes[index].Insert(pRect)
211 | return
212 | }
213 | }
214 |
215 | // If we don't subnodes within the Quadtree
216 | qt.Objects = append(qt.Objects, pRect)
217 |
218 | // If total objects is greater than max objects and level is less than max levels
219 | if (len(qt.Objects) > qt.MaxObjects) && (qt.Level < qt.MaxLevels) {
220 |
221 | // split if we don't already have subnodes
222 | if len(qt.Nodes) > 0 == false {
223 | qt.split()
224 | }
225 |
226 | // Add all objects to there corresponding subNodes
227 | for i < len(qt.Objects) {
228 |
229 | index = qt.getIndex(qt.Objects[i])
230 |
231 | if index != -1 {
232 |
233 | splice := qt.Objects[i] // Get the object out of the slice
234 | qt.Objects = append(qt.Objects[:i], qt.Objects[i+1:]...) // Remove the object from the slice
235 |
236 | qt.Nodes[index].Insert(splice)
237 |
238 | } else {
239 |
240 | i++
241 |
242 | }
243 |
244 | }
245 |
246 | }
247 |
248 | }
249 |
250 | // Retrieve - Return all objects that could collide with the given object
251 | func (qt *Quadtree) Retrieve(pRect Bounds) []Bounds {
252 |
253 | index := qt.getIndex(pRect)
254 |
255 | // Array with all detected objects
256 | returnObjects := qt.Objects
257 |
258 | //if we have subnodes ...
259 | if len(qt.Nodes) > 0 {
260 |
261 | //if pRect fits into a subnode ..
262 | if index != -1 {
263 |
264 | returnObjects = append(returnObjects, qt.Nodes[index].Retrieve(pRect)...)
265 |
266 | } else {
267 |
268 | //if pRect does not fit into a subnode, check it against all subnodes
269 | for i := 0; i < len(qt.Nodes); i++ {
270 | returnObjects = append(returnObjects, qt.Nodes[i].Retrieve(pRect)...)
271 | }
272 |
273 | }
274 | }
275 |
276 | return returnObjects
277 |
278 | }
279 |
280 | // RetrievePoints - Return all points that collide
281 | func (qt *Quadtree) RetrievePoints(find Bounds) []Bounds {
282 |
283 | var foundPoints []Bounds
284 | potentials := qt.Retrieve(find)
285 | for o := 0; o < len(potentials); o++ {
286 |
287 | // X and Ys are the same and it has no Width and Height (Point)
288 | xyMatch := potentials[o].X == float64(find.X) && potentials[o].Y == float64(find.Y)
289 | if xyMatch && potentials[o].IsPoint() {
290 | foundPoints = append(foundPoints, find)
291 | }
292 | }
293 |
294 | return foundPoints
295 |
296 | }
297 |
298 | // RetrieveIntersections - Bring back all the bounds in a Quadtree that intersect with a provided bounds
299 | func (qt *Quadtree) RetrieveIntersections(find Bounds) []Bounds {
300 |
301 | var foundIntersections []Bounds
302 |
303 | potentials := qt.Retrieve(find)
304 | for o := 0; o < len(potentials); o++ {
305 | if potentials[o].Intersects(find) {
306 | foundIntersections = append(foundIntersections, potentials[o])
307 | }
308 | }
309 |
310 | return foundIntersections
311 |
312 | }
313 |
314 | //Clear - Clear the Quadtree
315 | func (qt *Quadtree) Clear() {
316 |
317 | qt.Objects = []Bounds{}
318 |
319 | if len(qt.Nodes)-1 > 0 {
320 | for i := 0; i < len(qt.Nodes); i++ {
321 | qt.Nodes[i].Clear()
322 | }
323 | }
324 |
325 | qt.Nodes = []Quadtree{}
326 | qt.Total = 0
327 |
328 | }
329 |
--------------------------------------------------------------------------------
/quadtree_test.go:
--------------------------------------------------------------------------------
1 | package quadtree
2 |
3 | import (
4 | "math/rand"
5 | "testing"
6 | "time"
7 | )
8 |
9 | func TestQuadtreeCreation(t *testing.T) {
10 | //x, y, width, height
11 | qt := setupQuadtree(0, 0, 640, 480)
12 | if qt.Bounds.Width != 640 && qt.Bounds.Height != 480 {
13 | t.Errorf("Quadtree was not created correctly")
14 | }
15 | }
16 |
17 | func TestSplit(t *testing.T) {
18 |
19 | //x, y, width, height
20 | qt := setupQuadtree(0, 0, 640, 480)
21 | qt.split()
22 | if len(qt.Nodes) != 4 {
23 | t.Error("Quadtree did not split correctly, expected 4 nodes got", len(qt.Nodes))
24 | }
25 |
26 | qt.split()
27 | if len(qt.Nodes) != 4 {
28 | t.Error("Quadtree should not split itself more than once", len(qt.Nodes))
29 | }
30 |
31 | }
32 |
33 | func TestTotalSubnodes(t *testing.T) {
34 |
35 | //x, y, width, height
36 | qt := setupQuadtree(0, 0, 640, 480)
37 | qt.split()
38 | for i := 0; i < len(qt.Nodes); i++ {
39 | qt.Nodes[i].split()
40 | }
41 |
42 | total := qt.TotalNodes()
43 | if total != 20 {
44 | t.Error("Quadtree did not split correctly, expected 20 nodes got", total)
45 | }
46 |
47 | }
48 |
49 | func TestQuadtreeInsert(t *testing.T) {
50 |
51 | rand.Seed(time.Now().UTC().UnixNano()) // Seed Random properly
52 |
53 | qt := setupQuadtree(0, 0, 640, 480)
54 |
55 | grid := 10.0
56 | gridh := qt.Bounds.Width / grid
57 | gridv := qt.Bounds.Height / grid
58 | var randomObject Bounds
59 | numObjects := 1000
60 |
61 | for i := 0; i < numObjects; i++ {
62 |
63 | x := randMinMax(0, gridh) * grid
64 | y := randMinMax(0, gridv) * grid
65 |
66 | randomObject = Bounds{
67 | X: x,
68 | Y: y,
69 | Width: randMinMax(1, 4) * grid,
70 | Height: randMinMax(1, 4) * grid,
71 | }
72 |
73 | index := qt.getIndex(randomObject)
74 | if index < -1 || index > 3 {
75 | t.Errorf("The index should be -1 or between 0 and 3, got %d \n", index)
76 | }
77 |
78 | qt.Insert(randomObject)
79 |
80 | }
81 |
82 | if qt.Total != numObjects {
83 | t.Errorf("Error: Should have totalled %d, got %d \n", numObjects, qt.Total)
84 | } else {
85 | t.Logf("Success: Total objects in the Quadtree is %d (as expected) \n", qt.Total)
86 | }
87 |
88 | }
89 |
90 | func TestCorrectQuad(t *testing.T) {
91 |
92 | qt := setupQuadtree(0, 0, 100, 100)
93 |
94 | var index int
95 | pass := true
96 |
97 | topRight := Bounds{
98 | X: 99,
99 | Y: 99,
100 | Width: 0,
101 | Height: 0,
102 | }
103 | qt.Insert(topRight)
104 | index = qt.getIndex(topRight)
105 | if index == 0 {
106 | t.Errorf("The index should be 0, got %d \n", index)
107 | pass = false
108 | }
109 |
110 | topLeft := Bounds{
111 | X: 99,
112 | Y: 1,
113 | Width: 0,
114 | Height: 0,
115 | }
116 | qt.Insert(topLeft)
117 | index = qt.getIndex(topLeft)
118 | if index == 1 {
119 | t.Errorf("The index should be 1, got %d \n", index)
120 | pass = false
121 | }
122 |
123 | bottomLeft := Bounds{
124 | X: 1,
125 | Y: 1,
126 | Width: 0,
127 | Height: 0,
128 | }
129 | qt.Insert(bottomLeft)
130 | index = qt.getIndex(bottomLeft)
131 | if index == 2 {
132 | t.Errorf("The index should be 2, got %d \n", index)
133 | pass = false
134 | }
135 |
136 | bottomRight := Bounds{
137 | X: 1,
138 | Y: 51,
139 | Width: 0,
140 | Height: 0,
141 | }
142 | qt.Insert(bottomRight)
143 | index = qt.getIndex(bottomRight)
144 | if index == 3 {
145 | t.Errorf("The index should be 3, got %d \n", index)
146 | pass = false
147 | }
148 |
149 | if pass == true {
150 | t.Log("Success: The points were inserted into the correct quadrants")
151 | }
152 |
153 | }
154 |
155 | func TestQuadtreeRetrieval(t *testing.T) {
156 |
157 | rand.Seed(time.Now().UTC().UnixNano()) // Seed Random properly
158 |
159 | qt := setupQuadtree(0, 0, 640, 480)
160 |
161 | var randomObject Bounds
162 | numObjects := 100
163 |
164 | for i := 0; i < numObjects; i++ {
165 |
166 | randomObject = Bounds{
167 | X: float64(i),
168 | Y: float64(i),
169 | Width: 0,
170 | Height: 0,
171 | }
172 |
173 | qt.Insert(randomObject)
174 |
175 | }
176 |
177 | for j := 0; j < numObjects; j++ {
178 |
179 | Cursor := Bounds{
180 | X: float64(j),
181 | Y: float64(j),
182 | Width: 0,
183 | Height: 0,
184 | }
185 |
186 | objects := qt.Retrieve(Cursor)
187 |
188 | found := false
189 |
190 | if len(objects) >= numObjects {
191 | t.Error("Objects should not be equal to or bigger than the number of retrieved objects")
192 | }
193 |
194 | for o := 0; o < len(objects); o++ {
195 | if objects[o].X == float64(j) && objects[o].Y == float64(j) {
196 | found = true
197 | }
198 | }
199 | if found != true {
200 | t.Error("Error finding the correct point")
201 | }
202 |
203 | }
204 |
205 | }
206 |
207 | func TestQuadtreeRandomPointRetrieval(t *testing.T) {
208 |
209 | rand.Seed(time.Now().UTC().UnixNano()) // Seed Random properly
210 |
211 | qt := setupQuadtree(0, 0, 640, 480)
212 |
213 | numObjects := 1000
214 |
215 | for i := 1; i < numObjects+1; i++ {
216 |
217 | randomObject := Bounds{
218 | X: float64(i),
219 | Y: float64(i),
220 | Width: 0,
221 | Height: 0,
222 | }
223 |
224 | qt.Insert(randomObject)
225 |
226 | }
227 |
228 | failure := false
229 | iterations := 20
230 | for j := 1; j < iterations+1; j++ {
231 |
232 | Cursor := Bounds{
233 | X: float64(j),
234 | Y: float64(j),
235 | Width: 0,
236 | Height: 0,
237 | }
238 |
239 | point := qt.RetrievePoints(Cursor)
240 |
241 | for k := 0; k < len(point); k++ {
242 | if point[k].X == 0 {
243 | failure = true
244 | }
245 | if point[k].Y == 0 {
246 | failure = true
247 | }
248 | if failure {
249 | t.Error("Point was incorrectly retrieved", point)
250 | }
251 | if point[k].IsPoint() == false {
252 | t.Error("Point should have width and height of 0")
253 | }
254 | }
255 |
256 | }
257 |
258 | if failure == false {
259 | t.Logf("Success: All the points were retrieved correctly", iterations, numObjects)
260 | }
261 |
262 | }
263 |
264 | func TestIntersectionRetrieval(t *testing.T) {
265 | qt := setupQuadtree(0, 0, 640, 480)
266 | qt.Insert(Bounds{
267 | X: 1,
268 | Y: 1,
269 | Width: 10,
270 | Height: 10,
271 | })
272 | qt.Insert(Bounds{
273 | X: 5,
274 | Y: 5,
275 | Width: 10,
276 | Height: 10,
277 | })
278 | qt.Insert(Bounds{
279 | X: 10,
280 | Y: 10,
281 | Width: 10,
282 | Height: 10,
283 | })
284 | qt.Insert(Bounds{
285 | X: 15,
286 | Y: 15,
287 | Width: 10,
288 | Height: 10,
289 | })
290 | inter := qt.RetrieveIntersections(Bounds{
291 | X: 5,
292 | Y: 5,
293 | Width: 2.5,
294 | Height: 2.5,
295 | })
296 | if len(inter) != 2 {
297 | t.Error("Should have two intersections")
298 | }
299 | }
300 |
301 | func TestQuadtreeClear(t *testing.T) {
302 |
303 | rand.Seed(time.Now().UTC().UnixNano()) // Seed Random properly
304 |
305 | qt := setupQuadtree(0, 0, 640, 480)
306 |
307 | grid := 10.0
308 | gridh := qt.Bounds.Width / grid
309 | gridv := qt.Bounds.Height / grid
310 | var randomObject Bounds
311 | numObjects := 1000
312 |
313 | for i := 0; i < numObjects; i++ {
314 |
315 | x := randMinMax(0, gridh) * grid
316 | y := randMinMax(0, gridv) * grid
317 |
318 | randomObject = Bounds{
319 | X: x,
320 | Y: y,
321 | Width: randMinMax(1, 4) * grid,
322 | Height: randMinMax(1, 4) * grid,
323 | }
324 |
325 | index := qt.getIndex(randomObject)
326 | if index < -1 || index > 3 {
327 | t.Errorf("The index should be -1 or between 0 and 3, got %d \n", index)
328 | }
329 |
330 | qt.Insert(randomObject)
331 |
332 | }
333 |
334 | qt.Clear()
335 |
336 | if qt.Total != 0 {
337 | t.Errorf("Error: The Quadtree should be cleared")
338 | } else {
339 | t.Logf("Success: The Quadtree was cleared correctly")
340 | }
341 |
342 | }
343 |
344 | // Benchmarks
345 |
346 | func BenchmarkInsertOneThousand(b *testing.B) {
347 |
348 | qt := setupQuadtree(0, 0, 640, 480)
349 |
350 | grid := 10.0
351 | gridh := qt.Bounds.Width / grid
352 | gridv := qt.Bounds.Height / grid
353 | var randomObject Bounds
354 | numObjects := 1000
355 |
356 | for n := 0; n < b.N; n++ {
357 | for i := 0; i < numObjects; i++ {
358 |
359 | x := randMinMax(0, gridh) * grid
360 | y := randMinMax(0, gridv) * grid
361 |
362 | randomObject = Bounds{
363 | X: x,
364 | Y: y,
365 | Width: randMinMax(1, 4) * grid,
366 | Height: randMinMax(1, 4) * grid,
367 | }
368 |
369 | qt.Insert(randomObject)
370 |
371 | }
372 | }
373 |
374 | }
375 |
376 | // Convenience Functions
377 |
378 | func setupQuadtree(x float64, y float64, width float64, height float64) *Quadtree {
379 |
380 | return &Quadtree{
381 | Bounds: Bounds{
382 | X: x,
383 | Y: y,
384 | Width: width,
385 | Height: height,
386 | },
387 | MaxObjects: 4,
388 | MaxLevels: 8,
389 | Level: 0,
390 | Objects: make([]Bounds, 0),
391 | Nodes: make([]Quadtree, 0),
392 | }
393 |
394 | }
395 |
396 | func randMinMax(min float64, max float64) float64 {
397 | val := min + (rand.Float64() * (max - min))
398 | return val
399 | }
400 |
--------------------------------------------------------------------------------