Skip to content

Fix width and height usage #57

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
Mar 12, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 45 additions & 52 deletions quadtree.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,40 +18,38 @@ class Rectangle {
this.y = y;
this.w = w;
this.h = h;
}

get left() {
return this.x - this.w / 2;
}

get right() {
return this.x + this.w / 2;
}

get top() {
return this.y - this.h / 2;
}

get bottom() {
return this.y + this.h / 2;
this.left = x - w / 2;
this.right = x + w / 2;
this.top = y - h / 2;
this.bottom = y + h / 2;
}

contains(point) {
return (point.x >= this.x - this.w &&
point.x <= this.x + this.w &&
point.y >= this.y - this.h &&
point.y <= this.y + this.h);
return (
this.left <= point.x && point.x <= this.right &&
this.top <= point.y && point.y <= this.bottom
);
}


intersects(range) {
return !(range.x - range.w > this.x + this.w ||
range.x + range.w < this.x - this.w ||
range.y - range.h > this.y + this.h ||
range.y + range.h < this.y - this.h);
return !(
this.right < range.left || range.right < this.left ||
this.bottom < range.top || range.bottom < this.top
);
}


subdivide(quadrant) {
switch (quadrant) {
case 'ne':
return new Rectangle(this.x + this.w / 4, this.y - this.h / 4, this.w / 2, this.h / 2);
case 'nw':
return new Rectangle(this.x - this.w / 4, this.y - this.h / 4, this.w / 2, this.h / 2);
case 'se':
return new Rectangle(this.x + this.w / 4, this.y + this.h / 4, this.w / 2, this.h / 2);
case 'sw':
return new Rectangle(this.x - this.w / 4, this.y + this.h / 4, this.w / 2, this.h / 2);
}
}
}

// circle class for a circle shaped query
Expand Down Expand Up @@ -79,8 +77,8 @@ class Circle {
// radius of the circle
let r = this.r;

let w = range.w;
let h = range.h;
let w = range.w / 2;
let h = range.h / 2;

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

Expand Down Expand Up @@ -195,24 +193,24 @@ class QuadTree {
let h = qt.boundary.h / 2;

if ("ne" in obj) {
qt.northeast = QuadTree.fromJSON(obj.ne, x + w, y - h, w, h, capacity);
qt.northeast = QuadTree.fromJSON(obj.ne, x + w/2, y - h/2, w, h, capacity);
} else {
qt.northeast = new QuadTree(new Rectangle(x + w, y - h, w, h), capacity);
qt.northeast = new QuadTree(qt.boundary.subdivide('ne'), capacity);
}
if ("nw" in obj) {
qt.northwest = QuadTree.fromJSON(obj.nw, x - w, y - h, w, h, capacity);
qt.northwest = QuadTree.fromJSON(obj.nw, x - w/2, y - h/2, w, h, capacity);
} else {
qt.northwest = new QuadTree(new Rectangle(x - w, y - h, w, h), capacity);
qt.northwest = new QuadTree(qt.boundary.subdivide('nw'), capacity);
}
if ("se" in obj) {
qt.southeast = QuadTree.fromJSON(obj.se, x + w, y + h, w, h, capacity);
qt.southeast = QuadTree.fromJSON(obj.se, x + w/2, y + h/2, w, h, capacity);
} else {
qt.southeast = new QuadTree(new Rectangle(x + w, y + h, w, h), capacity);
qt.southeast = new QuadTree(qt.boundary.subdivide('se'), capacity);
}
if ("sw" in obj) {
qt.southwest = QuadTree.fromJSON(obj.sw, x - w, y + h, w, h, capacity);
qt.southwest = QuadTree.fromJSON(obj.sw, x - w/2, y + h/2, w, h, capacity);
} else {
qt.southwest = new QuadTree(new Rectangle(x - w, y + h, w, h), capacity);
qt.southwest = new QuadTree(qt.boundary.subdivide('sw'), capacity);
}

qt.divided = true;
Expand All @@ -221,19 +219,10 @@ class QuadTree {
}

subdivide() {
let x = this.boundary.x;
let y = this.boundary.y;
let w = this.boundary.w / 2;
let h = this.boundary.h / 2;

let ne = new Rectangle(x + w, y - h, w, h);
this.northeast = new QuadTree(ne, this.capacity);
let nw = new Rectangle(x - w, y - h, w, h);
this.northwest = new QuadTree(nw, this.capacity);
let se = new Rectangle(x + w, y + h, w, h);
this.southeast = new QuadTree(se, this.capacity);
let sw = new Rectangle(x - w, y + h, w, h);
this.southwest = new QuadTree(sw, this.capacity);
this.northeast = new QuadTree(this.boundary.subdivide('ne'), this.capacity);
this.northwest = new QuadTree(this.boundary.subdivide('nw'), this.capacity);
this.southeast = new QuadTree(this.boundary.subdivide('se'), this.capacity);
this.southwest = new QuadTree(this.boundary.subdivide('sw'), this.capacity);

this.divided = true;
}
Expand All @@ -252,8 +241,12 @@ class QuadTree {
this.subdivide();
}

return (this.northeast.insert(point) || this.northwest.insert(point) ||
this.southeast.insert(point) || this.southwest.insert(point));
return (
this.northeast.insert(point) ||
this.northwest.insert(point) ||
this.southeast.insert(point) ||
this.southwest.insert(point)
);
}

query(range, found) {
Expand Down Expand Up @@ -299,7 +292,7 @@ class QuadTree {
if (typeof maxDistance === "undefined") {
// A circle as big as the QuadTree's boundary
const outerReach = Math.sqrt(
Math.pow(this.boundary.w, 2) + Math.pow(this.boundary.h, 2)
Math.pow(this.boundary.w / 2, 2) + Math.pow(this.boundary.h / 2, 2)
);
// Distance of query point from center
const pointDistance = Math.sqrt(
Expand Down
4 changes: 2 additions & 2 deletions test/circle.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ describe('Circle', () => {
});
});
describe('intersects', () => {
let circle;
let circle;
let cx = 100;
let cy = 50;
let r = 25;
Expand Down Expand Up @@ -85,4 +85,4 @@ describe('Circle', () => {
expect(circle.intersects(rect)).to.be.true;
});
});
});
});
18 changes: 9 additions & 9 deletions test/quadtree.json.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ describe('QuadTree', () => {
beforeEach(() => {
quadtree = new QuadTree(new Rectangle(0, 0, 40, 40), 2);
points = [
new Point(-20, 20, { index: 0 }),
new Point(-20, -20, { index: 1 }),
new Point(20, 20, { index: 2 }),
new Point(20, -20, { index: 3 }),
new Point(-25, 25, { index: 4 }),
new Point(-25, -25, { index: 5 }),
new Point(25, 25, { index: 6 }),
new Point(25, -25, { index: 7 })
new Point(-10, 10, { index: 0 }),
new Point(-10, -10, { index: 1 }),
new Point(10, 10, { index: 2 }),
new Point(10, -10, { index: 3 }),
new Point(-15, 15, { index: 4 }),
new Point(-15, -15, { index: 5 }),
new Point(15, 15, { index: 6 }),
new Point(15, -15, { index: 7 })
];
points.forEach(point => quadtree.insert(point));
});
Expand Down Expand Up @@ -90,4 +90,4 @@ describe('QuadTree', () => {
expect(test.southwest.y).to.be.equal(quadtree.southwest.y);
});
});
});
});
50 changes: 34 additions & 16 deletions test/quadtree.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -115,10 +115,10 @@ describe('QuadTree', () => {
it('marks subdivided as true', () => {
expect(quadtree.divided).to.be.true;
});
childTests('northwest', cx - w / 2, cy - h / 2);
childTests('northeast', cx + w / 2, cy - h / 2);
childTests('southwest', cx - w / 2, cy + h / 2);
childTests('southeast', cx + w / 2, cy + h / 2);
childTests('northwest', cx - w / 4, cy - h / 4);
childTests('northeast', cx + w / 4, cy - h / 4);
childTests('southwest', cx - w / 4, cy + h / 4);
childTests('southeast', cx + w / 4, cy + h / 4);
});
describe('insert', () => {
let rect;
Expand All @@ -131,9 +131,19 @@ describe('QuadTree', () => {
expect(quadtree.insert(new Point(10, 20))).to.be.false;
});
it('does not add to points array when boundary does not contain point', () => {
quadtree.insert(new Point(10, 20));
quadtree.insert(new Point(89, 200));
quadtree.insert(new Point(111, 200));
quadtree.insert(new Point(100, 174));
quadtree.insert(new Point(100, 226));
expect(quadtree.points).to.have.length(0);
});
it('does add to points array when point is on the boundary', () => {
quadtree.insert(new Point(90, 200));
quadtree.insert(new Point(110, 200));
quadtree.insert(new Point(100, 175));
quadtree.insert(new Point(100, 225));
expect(quadtree.points).to.have.length(4);
});
it('returns true when capacity not hit and boundary does contain point', () => {
expect(quadtree.insert(new Point(100,200))).to.be.true;
});
Expand Down Expand Up @@ -165,11 +175,11 @@ describe('QuadTree', () => {
expect(quadtree.insert(new Point(100 - 10, 200 - 10))).to.be.true;
});
it('correctly adds to northeast', () => {
quadtree.insert(new Point(100 + 10, 200 - 10));
quadtree.insert(new Point(100 + 5, 200 - 10));
expect(quadtree.northeast.points).to.have.length(1);
});
it('returns true when added to northeast', () => {
expect(quadtree.insert(new Point(100 + 10, 200 - 10))).to.be.true;
expect(quadtree.insert(new Point(100 + 5, 200 - 10))).to.be.true;
});
it('correctly adds to southwest', () => {
quadtree.insert(new Point(100 - 10, 200 + 10));
Expand All @@ -179,16 +189,16 @@ describe('QuadTree', () => {
expect(quadtree.insert(new Point(100 - 10, 200 + 10))).to.be.true;
});
it('correctly adds to southeast', () => {
quadtree.insert(new Point(100 + 10, 200 + 10));
quadtree.insert(new Point(100 + 5, 200 + 10));
expect(quadtree.southeast.points).to.have.length(1);
});
it('returns true when added to southeast', () => {
expect(quadtree.insert(new Point(100 + 10, 200 + 10))).to.be.true;
expect(quadtree.insert(new Point(100 + 5, 200 + 10))).to.be.true;
});
it('does not trigger multiple subdivisions', () => {
quadtree.insert(new Point(100 + 10, 200 + 10));
quadtree.insert(new Point(100 + 5, 200 + 10));
let temp = quadtree.northeast;
quadtree.insert(new Point(100 + 10, 200 + 10));
quadtree.insert(new Point(100 + 5, 200 + 10));
expect(quadtree.northeast).to.equal(temp);
});
});
Expand Down Expand Up @@ -231,6 +241,14 @@ describe('QuadTree', () => {
let found = quadtree.query(new Rectangle(25, 25, 10, 10));
expect(found).to.contain(points[3]);
});
it('returns an item on the right edge of the query region', () => {
let found = quadtree.query(new Rectangle(20, 25, 10, 10));
expect(found).to.contain(points[3]);
});
it('returns an item on the bottom edge of the query region', () => {
let found = quadtree.query(new Rectangle(25, 20, 10, 10));
expect(found).to.contain(points[3]);
});
describe('when a subdivision occurs', () => {
beforeEach(() => {
points.push(new Point(-26, -26));
Expand Down Expand Up @@ -280,11 +298,11 @@ describe('QuadTree', () => {
expect(found).to.contain(points[5]);
});
it('returns correct number of points from multiple regions', () => {
let found = quadtree.query(new Rectangle(0, -25, 50, 10));
let found = quadtree.query(new Rectangle(0, -25, 60, 10));
expect(found).to.have.length(4);
});
it('returns correct points from multiple regions', () => {
let found = quadtree.query(new Rectangle(0, -25, 50, 10));
let found = quadtree.query(new Rectangle(0, -25, 60, 10));
expect(found).to.contain(points[0]);
expect(found).to.contain(points[4]);
expect(found).to.contain(points[1]);
Expand Down Expand Up @@ -313,8 +331,8 @@ describe('QuadTree', () => {
points = [
new Point(20, 0),
new Point(40, 0),
new Point(60, 0),
new Point(80, 0)
new Point(-30, 0),
new Point(-40, 0)
];
points.forEach(point => quadtree.insert(point));
});
Expand Down Expand Up @@ -353,7 +371,7 @@ describe('QuadTree', () => {
it('returns correct number of items when far away', () => {
found = quadtree.closest(new Point(-30000, 0), 1);
expect(found).to.have.length(1);
expect(found).to.contain(points[0]);
expect(found).to.contain(points[3]);
});
// Supplied maxDistance
it('limits search to maxDistance', () => {
Expand Down
24 changes: 12 additions & 12 deletions test/rectangle.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,10 @@ describe('Rectangle', () => {
let w = 25;
let h = 30;
rect = new Rectangle(cx, cy, w, h);;
left = cx - w;
right = cx + w;
top = cy - h;
bottom = cy + h;
left = rect.left;
right = rect.right;
top = rect.top;
bottom = rect.bottom;
});
it('returns true when point is in the center', () => {
let point = new Point(cx, cy);
Expand Down Expand Up @@ -109,11 +109,11 @@ describe('Rectangle', () => {
cy = 200;
w = 50;
h = 25;
left = cx - w;
right = cx + w;
top = cy - h;
bottom = cy + h;
base = new Rectangle(cx, cy, w, h);
left = base.left;
right = base.right ;
top = base.top;
bottom = base.bottom;
});
it('returns true when second rectangle is inside first', () => {
let test = new Rectangle(cx, cy, w / 2, h / 2);
Expand All @@ -128,31 +128,31 @@ describe('Rectangle', () => {
expect(base.intersects(test)).to.be.true;
});
it('returns true when edges line up on the left', () => {
let test = new Rectangle(left - 10, cy, 10, 10);
let test = new Rectangle(left - 10, cy, 20, 20);
expect(base.intersects(test)).to.be.true;
});
it('returns false when edges do not line up on the left', () => {
let test = new Rectangle(left - 10 - 1, cy, 10, 10);
expect(base.intersects(test)).not.to.be.true;
});
it('returns true when edges line up on the right', () => {
let test = new Rectangle(right + 10, cy, 10, 10);
let test = new Rectangle(right + 10, cy, 20, 20);
expect(base.intersects(test)).to.be.true;
});
it('returns false when edges do not line up on the right', () => {
let test = new Rectangle(right + 10 + 1, cy, 10, 10);
expect(base.intersects(test)).not.to.be.true;
});
it('returns true when edges line up on the top', () => {
let test = new Rectangle(cx, top - 10, 10, 10);
let test = new Rectangle(cx, top - 10, 20, 20);
expect(base.intersects(test)).to.be.true;
});
it('returns false when edges do not line up on the top', () => {
let test = new Rectangle(cx, top - 10 - 1, 10, 10);
expect(base.intersects(test)).not.to.be.true;
});
it('returns true when edges line up on the bottom', () => {
let test = new Rectangle(cx, bottom + 10, 10, 10);
let test = new Rectangle(cx, bottom + 10, 20, 20);
expect(base.intersects(test)).to.be.true;
});
it('returns false when edges do not line up on the bottom', () => {
Expand Down