Skip to content

Add saving and loading to JSON #31

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 6 commits into from
Feb 26, 2019
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
92 changes: 83 additions & 9 deletions quadtree.js
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,83 @@ class QuadTree {
this.divided = false;
}

toJSON(isChild) {
let obj = { points: this.points };
if (this.divided) {
if (this.northeast.points.length > 0) {
obj.ne = this.northeast.toJSON(true);
}
if (this.northwest.points.length > 0) {
obj.nw = this.northwest.toJSON(true);
}
if (this.southeast.points.length > 0) {
obj.se = this.southeast.toJSON(true);
}
if (this.southwest.points.length > 0) {
obj.sw = this.southwest.toJSON(true);
}
}
if (!isChild) {
obj.capacity = this.capacity;
obj.x = this.boundary.x;
obj.y = this.boundary.y;
obj.w = this.boundary.w;
obj.h = this.boundary.h;
}
return obj;
}

static fromJSON(obj, x, y, w, h, capacity) {
if (typeof x === "undefined") {
if ("x" in obj) {
x = obj.x;
y = obj.y;
w = obj.w;
h = obj.h;
capacity = obj.capacity;
} else {
throw TypeError("JSON missing boundary information");
}
}
let qt = new QuadTree(new Rectangle(x, y, w, h), capacity);
qt.points = obj.points;
if (
"ne" in obj ||
"nw" in obj ||
"se" in obj ||
"sw" in obj
) {
let x = qt.boundary.x;
let y = qt.boundary.y;
let w = qt.boundary.w / 2;
let h = qt.boundary.h / 2;

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

qt.divided = true;
}
return qt;
}

subdivide() {
let x = this.boundary.x;
let y = this.boundary.y;
Expand Down Expand Up @@ -185,15 +262,12 @@ class QuadTree {
startingSize = 1;
}

if (!this.divided) {
// Empty
if (this.points.length == 0) {
return [];
}
// Limit to number of points in this QuadTree
if (this.points.length < count) {
count = this.points.length;
}
// Limit to number of points in this QuadTree
if (this.length == 0) {
return [];
}
if (this.length < count) {
return this.points;
}

// optimized, expanding binary search
Expand Down
49 changes: 45 additions & 4 deletions test/quadtree.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ describe('QuadTree', () => {
expect(found).to.contain(points[3]);
expect(found).to.contain(points[7]);
});
it('returns correct number of southhwest points', () => {
it('returns correct number of southwest points', () => {
let found = quadtree.query(new Rectangle(-25, -25, 10, 10));
expect(found).to.have.length(2);
});
Expand Down Expand Up @@ -305,6 +305,48 @@ describe('QuadTree', () => {
});
});
});
describe('json operations', () => {
let 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 })
];
points.forEach(point => quadtree.insert(point));
});
it('throws exception when JSON has no position data', () => {
expect(() => { new QuadTree.fromJSON({ points: [] }) }).to.throw(TypeError);
});
it('saves all data to a JSON object', () => {
const obj = quadtree.toJSON();
expect(obj.x).to.equal(quadtree.boundary.x);
expect(obj.y).to.equal(quadtree.boundary.y);
expect(obj.w).to.equal(quadtree.boundary.w);
expect(obj.h).to.equal(quadtree.boundary.h);
expect(obj.capacity).to.equal(quadtree.capacity);
expect(obj.ne.points.length).to.equal(quadtree.northeast.points.length);
expect(obj.ne.points[0].userData.index).to.equal(quadtree.northeast.points[0].userData.index);
expect(obj.ne.divided).to.be.undefined;
expect(obj.nw).to.be.undefined;
});
it('loads properly from a JSON object', () => {
const obj = quadtree.toJSON();
const test = QuadTree.fromJSON(obj);
expect(test.boundary.x).to.equal(quadtree.boundary.x);
expect(test.boundary.y).to.equal(quadtree.boundary.y);
expect(test.boundary.w).to.equal(quadtree.boundary.w);
expect(test.boundary.h).to.equal(quadtree.boundary.h);
expect(test.capacity).to.equal(quadtree.capacity);
expect(test.northeast.boundary.x).to.equal(quadtree.northeast.boundary.x);
expect(test.northeast.points[0].userData.index).to.equal(quadtree.northeast.points[0].userData.index);
expect(test.northwest.divided).to.be.equal(quadtree.northwest.divided);
expect(test.southeast.x).to.be.equal(quadtree.southeast.x);
expect(test.southwest.y).to.be.equal(quadtree.southwest.y);
});
});
describe('closest', () => {
let quadtree;
let points;
Expand Down Expand Up @@ -344,8 +386,7 @@ describe('QuadTree', () => {
expect(found).to.have.length(1);
expect(found).to.contain(points[0]);
});
// no total count of items
it.skip('returns all items when number requested exceeds QuadTree contents', () => {
it('returns all items when number requested exceeds QuadTree contents', () => {
found = quadtree.closest(new Point(0, 0), 10);
expect(found).to.have.length(4);
});
Expand Down Expand Up @@ -388,7 +429,7 @@ describe('QuadTree', () => {
points = [];
for (let idx = 0; idx < 10; ++idx) {
points.push(new Point(
bound.left + bound.w * Math.random(),
bound.left + bound.w * Math.random(),
bound.top + bound.h * Math.random()));
}
points.forEach(point => quadtree.insert(point));
Expand Down