Skip to content

Commit 55e8292

Browse files
Fix width and height usage (#57)
* Update inequality for readability * fix tests * Fixed the logic for the contains and intersects. This breaks tests so working on fixing those now. z * Fix rectanglt tests to fix contains logic * Fixed issue with subdivide logic * Fixed logic for intersects and tests * Made the borders properties of rect rather than functions * Added queryContains function to be used when querying a range rather than adding new nodes. * Added additional test for bottom edge * Updated circle to edge calculation to use w and h correctly * Removed queryContains and made logic inclusive for all contains * Updated point out of bounds test to add points just outside each border * Added additional test to check points are added if they fall on the boundary
1 parent e39d994 commit 55e8292

File tree

5 files changed

+102
-91
lines changed

5 files changed

+102
-91
lines changed

quadtree.js

Lines changed: 45 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -18,40 +18,38 @@ class Rectangle {
1818
this.y = y;
1919
this.w = w;
2020
this.h = h;
21-
}
22-
23-
get left() {
24-
return this.x - this.w / 2;
25-
}
26-
27-
get right() {
28-
return this.x + this.w / 2;
29-
}
30-
31-
get top() {
32-
return this.y - this.h / 2;
33-
}
34-
35-
get bottom() {
36-
return this.y + this.h / 2;
21+
this.left = x - w / 2;
22+
this.right = x + w / 2;
23+
this.top = y - h / 2;
24+
this.bottom = y + h / 2;
3725
}
3826

3927
contains(point) {
40-
return (point.x >= this.x - this.w &&
41-
point.x <= this.x + this.w &&
42-
point.y >= this.y - this.h &&
43-
point.y <= this.y + this.h);
28+
return (
29+
this.left <= point.x && point.x <= this.right &&
30+
this.top <= point.y && point.y <= this.bottom
31+
);
4432
}
4533

46-
4734
intersects(range) {
48-
return !(range.x - range.w > this.x + this.w ||
49-
range.x + range.w < this.x - this.w ||
50-
range.y - range.h > this.y + this.h ||
51-
range.y + range.h < this.y - this.h);
35+
return !(
36+
this.right < range.left || range.right < this.left ||
37+
this.bottom < range.top || range.bottom < this.top
38+
);
5239
}
5340

54-
41+
subdivide(quadrant) {
42+
switch (quadrant) {
43+
case 'ne':
44+
return new Rectangle(this.x + this.w / 4, this.y - this.h / 4, this.w / 2, this.h / 2);
45+
case 'nw':
46+
return new Rectangle(this.x - this.w / 4, this.y - this.h / 4, this.w / 2, this.h / 2);
47+
case 'se':
48+
return new Rectangle(this.x + this.w / 4, this.y + this.h / 4, this.w / 2, this.h / 2);
49+
case 'sw':
50+
return new Rectangle(this.x - this.w / 4, this.y + this.h / 4, this.w / 2, this.h / 2);
51+
}
52+
}
5553
}
5654

5755
// circle class for a circle shaped query
@@ -79,8 +77,8 @@ class Circle {
7977
// radius of the circle
8078
let r = this.r;
8179

82-
let w = range.w;
83-
let h = range.h;
80+
let w = range.w / 2;
81+
let h = range.h / 2;
8482

8583
let edges = Math.pow((xDist - w), 2) + Math.pow((yDist - h), 2);
8684

@@ -195,24 +193,24 @@ class QuadTree {
195193
let h = qt.boundary.h / 2;
196194

197195
if ("ne" in obj) {
198-
qt.northeast = QuadTree.fromJSON(obj.ne, x + w, y - h, w, h, capacity);
196+
qt.northeast = QuadTree.fromJSON(obj.ne, x + w/2, y - h/2, w, h, capacity);
199197
} else {
200-
qt.northeast = new QuadTree(new Rectangle(x + w, y - h, w, h), capacity);
198+
qt.northeast = new QuadTree(qt.boundary.subdivide('ne'), capacity);
201199
}
202200
if ("nw" in obj) {
203-
qt.northwest = QuadTree.fromJSON(obj.nw, x - w, y - h, w, h, capacity);
201+
qt.northwest = QuadTree.fromJSON(obj.nw, x - w/2, y - h/2, w, h, capacity);
204202
} else {
205-
qt.northwest = new QuadTree(new Rectangle(x - w, y - h, w, h), capacity);
203+
qt.northwest = new QuadTree(qt.boundary.subdivide('nw'), capacity);
206204
}
207205
if ("se" in obj) {
208-
qt.southeast = QuadTree.fromJSON(obj.se, x + w, y + h, w, h, capacity);
206+
qt.southeast = QuadTree.fromJSON(obj.se, x + w/2, y + h/2, w, h, capacity);
209207
} else {
210-
qt.southeast = new QuadTree(new Rectangle(x + w, y + h, w, h), capacity);
208+
qt.southeast = new QuadTree(qt.boundary.subdivide('se'), capacity);
211209
}
212210
if ("sw" in obj) {
213-
qt.southwest = QuadTree.fromJSON(obj.sw, x - w, y + h, w, h, capacity);
211+
qt.southwest = QuadTree.fromJSON(obj.sw, x - w/2, y + h/2, w, h, capacity);
214212
} else {
215-
qt.southwest = new QuadTree(new Rectangle(x - w, y + h, w, h), capacity);
213+
qt.southwest = new QuadTree(qt.boundary.subdivide('sw'), capacity);
216214
}
217215

218216
qt.divided = true;
@@ -221,19 +219,10 @@ class QuadTree {
221219
}
222220

223221
subdivide() {
224-
let x = this.boundary.x;
225-
let y = this.boundary.y;
226-
let w = this.boundary.w / 2;
227-
let h = this.boundary.h / 2;
228-
229-
let ne = new Rectangle(x + w, y - h, w, h);
230-
this.northeast = new QuadTree(ne, this.capacity);
231-
let nw = new Rectangle(x - w, y - h, w, h);
232-
this.northwest = new QuadTree(nw, this.capacity);
233-
let se = new Rectangle(x + w, y + h, w, h);
234-
this.southeast = new QuadTree(se, this.capacity);
235-
let sw = new Rectangle(x - w, y + h, w, h);
236-
this.southwest = new QuadTree(sw, this.capacity);
222+
this.northeast = new QuadTree(this.boundary.subdivide('ne'), this.capacity);
223+
this.northwest = new QuadTree(this.boundary.subdivide('nw'), this.capacity);
224+
this.southeast = new QuadTree(this.boundary.subdivide('se'), this.capacity);
225+
this.southwest = new QuadTree(this.boundary.subdivide('sw'), this.capacity);
237226

238227
this.divided = true;
239228
}
@@ -252,8 +241,12 @@ class QuadTree {
252241
this.subdivide();
253242
}
254243

255-
return (this.northeast.insert(point) || this.northwest.insert(point) ||
256-
this.southeast.insert(point) || this.southwest.insert(point));
244+
return (
245+
this.northeast.insert(point) ||
246+
this.northwest.insert(point) ||
247+
this.southeast.insert(point) ||
248+
this.southwest.insert(point)
249+
);
257250
}
258251

259252
query(range, found) {
@@ -299,7 +292,7 @@ class QuadTree {
299292
if (typeof maxDistance === "undefined") {
300293
// A circle as big as the QuadTree's boundary
301294
const outerReach = Math.sqrt(
302-
Math.pow(this.boundary.w, 2) + Math.pow(this.boundary.h, 2)
295+
Math.pow(this.boundary.w / 2, 2) + Math.pow(this.boundary.h / 2, 2)
303296
);
304297
// Distance of query point from center
305298
const pointDistance = Math.sqrt(

test/circle.spec.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ describe('Circle', () => {
4545
});
4646
});
4747
describe('intersects', () => {
48-
let circle;
48+
let circle;
4949
let cx = 100;
5050
let cy = 50;
5151
let r = 25;
@@ -85,4 +85,4 @@ describe('Circle', () => {
8585
expect(circle.intersects(rect)).to.be.true;
8686
});
8787
});
88-
});
88+
});

test/quadtree.json.spec.js

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,14 @@ describe('QuadTree', () => {
77
beforeEach(() => {
88
quadtree = new QuadTree(new Rectangle(0, 0, 40, 40), 2);
99
points = [
10-
new Point(-20, 20, { index: 0 }),
11-
new Point(-20, -20, { index: 1 }),
12-
new Point(20, 20, { index: 2 }),
13-
new Point(20, -20, { index: 3 }),
14-
new Point(-25, 25, { index: 4 }),
15-
new Point(-25, -25, { index: 5 }),
16-
new Point(25, 25, { index: 6 }),
17-
new Point(25, -25, { index: 7 })
10+
new Point(-10, 10, { index: 0 }),
11+
new Point(-10, -10, { index: 1 }),
12+
new Point(10, 10, { index: 2 }),
13+
new Point(10, -10, { index: 3 }),
14+
new Point(-15, 15, { index: 4 }),
15+
new Point(-15, -15, { index: 5 }),
16+
new Point(15, 15, { index: 6 }),
17+
new Point(15, -15, { index: 7 })
1818
];
1919
points.forEach(point => quadtree.insert(point));
2020
});
@@ -90,4 +90,4 @@ describe('QuadTree', () => {
9090
expect(test.southwest.y).to.be.equal(quadtree.southwest.y);
9191
});
9292
});
93-
});
93+
});

test/quadtree.spec.js

Lines changed: 34 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -115,10 +115,10 @@ describe('QuadTree', () => {
115115
it('marks subdivided as true', () => {
116116
expect(quadtree.divided).to.be.true;
117117
});
118-
childTests('northwest', cx - w / 2, cy - h / 2);
119-
childTests('northeast', cx + w / 2, cy - h / 2);
120-
childTests('southwest', cx - w / 2, cy + h / 2);
121-
childTests('southeast', cx + w / 2, cy + h / 2);
118+
childTests('northwest', cx - w / 4, cy - h / 4);
119+
childTests('northeast', cx + w / 4, cy - h / 4);
120+
childTests('southwest', cx - w / 4, cy + h / 4);
121+
childTests('southeast', cx + w / 4, cy + h / 4);
122122
});
123123
describe('insert', () => {
124124
let rect;
@@ -131,9 +131,19 @@ describe('QuadTree', () => {
131131
expect(quadtree.insert(new Point(10, 20))).to.be.false;
132132
});
133133
it('does not add to points array when boundary does not contain point', () => {
134-
quadtree.insert(new Point(10, 20));
134+
quadtree.insert(new Point(89, 200));
135+
quadtree.insert(new Point(111, 200));
136+
quadtree.insert(new Point(100, 174));
137+
quadtree.insert(new Point(100, 226));
135138
expect(quadtree.points).to.have.length(0);
136139
});
140+
it('does add to points array when point is on the boundary', () => {
141+
quadtree.insert(new Point(90, 200));
142+
quadtree.insert(new Point(110, 200));
143+
quadtree.insert(new Point(100, 175));
144+
quadtree.insert(new Point(100, 225));
145+
expect(quadtree.points).to.have.length(4);
146+
});
137147
it('returns true when capacity not hit and boundary does contain point', () => {
138148
expect(quadtree.insert(new Point(100,200))).to.be.true;
139149
});
@@ -165,11 +175,11 @@ describe('QuadTree', () => {
165175
expect(quadtree.insert(new Point(100 - 10, 200 - 10))).to.be.true;
166176
});
167177
it('correctly adds to northeast', () => {
168-
quadtree.insert(new Point(100 + 10, 200 - 10));
178+
quadtree.insert(new Point(100 + 5, 200 - 10));
169179
expect(quadtree.northeast.points).to.have.length(1);
170180
});
171181
it('returns true when added to northeast', () => {
172-
expect(quadtree.insert(new Point(100 + 10, 200 - 10))).to.be.true;
182+
expect(quadtree.insert(new Point(100 + 5, 200 - 10))).to.be.true;
173183
});
174184
it('correctly adds to southwest', () => {
175185
quadtree.insert(new Point(100 - 10, 200 + 10));
@@ -179,16 +189,16 @@ describe('QuadTree', () => {
179189
expect(quadtree.insert(new Point(100 - 10, 200 + 10))).to.be.true;
180190
});
181191
it('correctly adds to southeast', () => {
182-
quadtree.insert(new Point(100 + 10, 200 + 10));
192+
quadtree.insert(new Point(100 + 5, 200 + 10));
183193
expect(quadtree.southeast.points).to.have.length(1);
184194
});
185195
it('returns true when added to southeast', () => {
186-
expect(quadtree.insert(new Point(100 + 10, 200 + 10))).to.be.true;
196+
expect(quadtree.insert(new Point(100 + 5, 200 + 10))).to.be.true;
187197
});
188198
it('does not trigger multiple subdivisions', () => {
189-
quadtree.insert(new Point(100 + 10, 200 + 10));
199+
quadtree.insert(new Point(100 + 5, 200 + 10));
190200
let temp = quadtree.northeast;
191-
quadtree.insert(new Point(100 + 10, 200 + 10));
201+
quadtree.insert(new Point(100 + 5, 200 + 10));
192202
expect(quadtree.northeast).to.equal(temp);
193203
});
194204
});
@@ -231,6 +241,14 @@ describe('QuadTree', () => {
231241
let found = quadtree.query(new Rectangle(25, 25, 10, 10));
232242
expect(found).to.contain(points[3]);
233243
});
244+
it('returns an item on the right edge of the query region', () => {
245+
let found = quadtree.query(new Rectangle(20, 25, 10, 10));
246+
expect(found).to.contain(points[3]);
247+
});
248+
it('returns an item on the bottom edge of the query region', () => {
249+
let found = quadtree.query(new Rectangle(25, 20, 10, 10));
250+
expect(found).to.contain(points[3]);
251+
});
234252
describe('when a subdivision occurs', () => {
235253
beforeEach(() => {
236254
points.push(new Point(-26, -26));
@@ -280,11 +298,11 @@ describe('QuadTree', () => {
280298
expect(found).to.contain(points[5]);
281299
});
282300
it('returns correct number of points from multiple regions', () => {
283-
let found = quadtree.query(new Rectangle(0, -25, 50, 10));
301+
let found = quadtree.query(new Rectangle(0, -25, 60, 10));
284302
expect(found).to.have.length(4);
285303
});
286304
it('returns correct points from multiple regions', () => {
287-
let found = quadtree.query(new Rectangle(0, -25, 50, 10));
305+
let found = quadtree.query(new Rectangle(0, -25, 60, 10));
288306
expect(found).to.contain(points[0]);
289307
expect(found).to.contain(points[4]);
290308
expect(found).to.contain(points[1]);
@@ -313,8 +331,8 @@ describe('QuadTree', () => {
313331
points = [
314332
new Point(20, 0),
315333
new Point(40, 0),
316-
new Point(60, 0),
317-
new Point(80, 0)
334+
new Point(-30, 0),
335+
new Point(-40, 0)
318336
];
319337
points.forEach(point => quadtree.insert(point));
320338
});
@@ -353,7 +371,7 @@ describe('QuadTree', () => {
353371
it('returns correct number of items when far away', () => {
354372
found = quadtree.closest(new Point(-30000, 0), 1);
355373
expect(found).to.have.length(1);
356-
expect(found).to.contain(points[0]);
374+
expect(found).to.contain(points[3]);
357375
});
358376
// Supplied maxDistance
359377
it('limits search to maxDistance', () => {

test/rectangle.spec.js

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,10 @@ describe('Rectangle', () => {
3636
let w = 25;
3737
let h = 30;
3838
rect = new Rectangle(cx, cy, w, h);;
39-
left = cx - w;
40-
right = cx + w;
41-
top = cy - h;
42-
bottom = cy + h;
39+
left = rect.left;
40+
right = rect.right;
41+
top = rect.top;
42+
bottom = rect.bottom;
4343
});
4444
it('returns true when point is in the center', () => {
4545
let point = new Point(cx, cy);
@@ -109,11 +109,11 @@ describe('Rectangle', () => {
109109
cy = 200;
110110
w = 50;
111111
h = 25;
112-
left = cx - w;
113-
right = cx + w;
114-
top = cy - h;
115-
bottom = cy + h;
116112
base = new Rectangle(cx, cy, w, h);
113+
left = base.left;
114+
right = base.right ;
115+
top = base.top;
116+
bottom = base.bottom;
117117
});
118118
it('returns true when second rectangle is inside first', () => {
119119
let test = new Rectangle(cx, cy, w / 2, h / 2);
@@ -128,31 +128,31 @@ describe('Rectangle', () => {
128128
expect(base.intersects(test)).to.be.true;
129129
});
130130
it('returns true when edges line up on the left', () => {
131-
let test = new Rectangle(left - 10, cy, 10, 10);
131+
let test = new Rectangle(left - 10, cy, 20, 20);
132132
expect(base.intersects(test)).to.be.true;
133133
});
134134
it('returns false when edges do not line up on the left', () => {
135135
let test = new Rectangle(left - 10 - 1, cy, 10, 10);
136136
expect(base.intersects(test)).not.to.be.true;
137137
});
138138
it('returns true when edges line up on the right', () => {
139-
let test = new Rectangle(right + 10, cy, 10, 10);
139+
let test = new Rectangle(right + 10, cy, 20, 20);
140140
expect(base.intersects(test)).to.be.true;
141141
});
142142
it('returns false when edges do not line up on the right', () => {
143143
let test = new Rectangle(right + 10 + 1, cy, 10, 10);
144144
expect(base.intersects(test)).not.to.be.true;
145145
});
146146
it('returns true when edges line up on the top', () => {
147-
let test = new Rectangle(cx, top - 10, 10, 10);
147+
let test = new Rectangle(cx, top - 10, 20, 20);
148148
expect(base.intersects(test)).to.be.true;
149149
});
150150
it('returns false when edges do not line up on the top', () => {
151151
let test = new Rectangle(cx, top - 10 - 1, 10, 10);
152152
expect(base.intersects(test)).not.to.be.true;
153153
});
154154
it('returns true when edges line up on the bottom', () => {
155-
let test = new Rectangle(cx, bottom + 10, 10, 10);
155+
let test = new Rectangle(cx, bottom + 10, 20, 20);
156156
expect(base.intersects(test)).to.be.true;
157157
});
158158
it('returns false when edges do not line up on the bottom', () => {

0 commit comments

Comments
 (0)