-
Notifications
You must be signed in to change notification settings - Fork 0
/
QuadTree.js
157 lines (129 loc) · 4.01 KB
/
QuadTree.js
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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
const NW = 0;
const NE = 1;
const SW = 2;
const SE = 3;
class QuadTree {
maxCapacity = 10;
maxLevels = 150;
constructor(bounds, level) {
this.bounds = bounds;
this.elements = [];
this.quadrants = [];
this.level = level;
this.isSplit = false;
}
/**
* Insert an element into the quad tree. If the limit is surpassed split the quadrant
*/
insert(element) {
if (this.isSplit) {
const indexes = this.index(element);
for (const index of indexes) {
this.quadrants[index].insert(element);
}
return;
}
this.elements.push(element);
if (
this.elements.length > this.maxCapacity &&
this.level < this.maxLevels
) {
if (!this.isSplit) {
this.split();
}
for (const existingElement of this.elements) {
const indexes = this.index(existingElement);
for (const index of indexes) {
this.quadrants[index].insert(existingElement);
}
}
this.elements = [];
}
}
/**
* Splits the current quadrant into 4
*/
split() {
const x = Math.floor(this.bounds.x);
const y = Math.floor(this.bounds.y);
const w = Math.floor(this.bounds.width / 2);
const h = Math.floor(this.bounds.height / 2);
this.isSplit = true;
// North West Quadrant
this.quadrants[NW] = new QuadTree(
new Rectangle(x, y, w, h),
this.level + 1
);
// North East Quadrant
this.quadrants[NE] = new QuadTree(
new Rectangle(x + w, y, w, h),
this.level + 1
);
// South West Quadrant
this.quadrants[SW] = new QuadTree(
new Rectangle(x, y + h, w, h),
this.level + 1
);
// South East Quadrant
this.quadrants[SE] = new QuadTree(
new Rectangle(x + w, y + h, w, h),
this.level + 1
);
}
/**
* Clears all the values from the QuadTree
*/
clear() {
this.elements = [];
for (const quadrant of this.quadrants) {
if (quadrant.isSplit) {
quadrant.clear();
}
}
this.quadrants = [];
this.isSplit = false;
}
/**
* Get the index of the quadrant the element is in
*/
index(element) {
const indexes = [];
const verticalMidpoint = this.bounds.x + this.bounds.width / 2;
const horizontalMidpoint = this.bounds.y + this.bounds.height / 2;
const startNorth = element.y < horizontalMidpoint;
const startWest = element.x < verticalMidpoint;
const endEast = element.x + element.width > verticalMidpoint;
const endSouth = element.y + element.height > horizontalMidpoint;
// check the bounds of each quadrant
if (startNorth && endEast) indexes.push(NE);
if (startWest && startNorth) indexes.push(NW);
if (startWest && endSouth) indexes.push(SW);
if (endEast && endSouth) indexes.push(SE);
return indexes;
}
/**
* Retrieve all the elements with which there could be a collision
*/
retrieve(element) {
const indexes = this.index(element);
let retElements = [...this.elements];
// retrieve elements from child quadrants
if (this.isSplit) {
for (const index of indexes) {
retElements = retElements.concat(
this.quadrants[index].retrieve(element)
);
}
}
// return the elements with all duplicates filtered out
return retElements.filter((e, i) => retElements.indexOf(e) >= i);
}
render() {
this.bounds.render();
if (this.isSplit) {
for (let i = 0; i < this.quadrants.length; i++) {
this.quadrants[i].render();
}
}
}
}