Skip to content

Commit 9600369

Browse files
author
Chris Hallberg
authored
Merge pull request #31 from crhallberg/save-load
Add saving and loading to JSON
2 parents bb6f7a8 + 84664e3 commit 9600369

File tree

2 files changed

+128
-13
lines changed

2 files changed

+128
-13
lines changed

quadtree.js

Lines changed: 83 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,83 @@ class QuadTree {
117117
this.divided = false;
118118
}
119119

120+
toJSON(isChild) {
121+
let obj = { points: this.points };
122+
if (this.divided) {
123+
if (this.northeast.points.length > 0) {
124+
obj.ne = this.northeast.toJSON(true);
125+
}
126+
if (this.northwest.points.length > 0) {
127+
obj.nw = this.northwest.toJSON(true);
128+
}
129+
if (this.southeast.points.length > 0) {
130+
obj.se = this.southeast.toJSON(true);
131+
}
132+
if (this.southwest.points.length > 0) {
133+
obj.sw = this.southwest.toJSON(true);
134+
}
135+
}
136+
if (!isChild) {
137+
obj.capacity = this.capacity;
138+
obj.x = this.boundary.x;
139+
obj.y = this.boundary.y;
140+
obj.w = this.boundary.w;
141+
obj.h = this.boundary.h;
142+
}
143+
return obj;
144+
}
145+
146+
static fromJSON(obj, x, y, w, h, capacity) {
147+
if (typeof x === "undefined") {
148+
if ("x" in obj) {
149+
x = obj.x;
150+
y = obj.y;
151+
w = obj.w;
152+
h = obj.h;
153+
capacity = obj.capacity;
154+
} else {
155+
throw TypeError("JSON missing boundary information");
156+
}
157+
}
158+
let qt = new QuadTree(new Rectangle(x, y, w, h), capacity);
159+
qt.points = obj.points;
160+
if (
161+
"ne" in obj ||
162+
"nw" in obj ||
163+
"se" in obj ||
164+
"sw" in obj
165+
) {
166+
let x = qt.boundary.x;
167+
let y = qt.boundary.y;
168+
let w = qt.boundary.w / 2;
169+
let h = qt.boundary.h / 2;
170+
171+
if ("ne" in obj) {
172+
qt.northeast = QuadTree.fromJSON(obj.ne, x + w, y - h, w, h, capacity);
173+
} else {
174+
qt.northeast = new QuadTree(new Rectangle(x + w, y - h, w, h), capacity);
175+
}
176+
if ("nw" in obj) {
177+
qt.northwest = QuadTree.fromJSON(obj.nw, x - w, y - h, w, h, capacity);
178+
} else {
179+
qt.northwest = new QuadTree(new Rectangle(x - w, y - h, w, h), capacity);
180+
}
181+
if ("se" in obj) {
182+
qt.southeast = QuadTree.fromJSON(obj.se, x + w, y + h, w, h, capacity);
183+
} else {
184+
qt.southeast = new QuadTree(new Rectangle(x + w, y + h, w, h), capacity);
185+
}
186+
if ("sw" in obj) {
187+
qt.southwest = QuadTree.fromJSON(obj.sw, x - w, y + h, w, h, capacity);
188+
} else {
189+
qt.southwest = new QuadTree(new Rectangle(x - w, y + h, w, h), capacity);
190+
}
191+
192+
qt.divided = true;
193+
}
194+
return qt;
195+
}
196+
120197
subdivide() {
121198
let x = this.boundary.x;
122199
let y = this.boundary.y;
@@ -185,15 +262,12 @@ class QuadTree {
185262
startingSize = 1;
186263
}
187264

188-
if (!this.divided) {
189-
// Empty
190-
if (this.points.length == 0) {
191-
return [];
192-
}
193-
// Limit to number of points in this QuadTree
194-
if (this.points.length < count) {
195-
count = this.points.length;
196-
}
265+
// Limit to number of points in this QuadTree
266+
if (this.length == 0) {
267+
return [];
268+
}
269+
if (this.length < count) {
270+
return this.points;
197271
}
198272

199273
// optimized, expanding binary search

test/quadtree.spec.js

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,7 @@ describe('QuadTree', () => {
261261
expect(found).to.contain(points[3]);
262262
expect(found).to.contain(points[7]);
263263
});
264-
it('returns correct number of southhwest points', () => {
264+
it('returns correct number of southwest points', () => {
265265
let found = quadtree.query(new Rectangle(-25, -25, 10, 10));
266266
expect(found).to.have.length(2);
267267
});
@@ -305,6 +305,48 @@ describe('QuadTree', () => {
305305
});
306306
});
307307
});
308+
describe('json operations', () => {
309+
let quadtree;
310+
beforeEach(() => {
311+
quadtree = new QuadTree(new Rectangle(0, 0, 40, 40), 2);
312+
points = [
313+
new Point(-20, 20, { index: 0 }),
314+
new Point(-20, -20, { index: 1 }),
315+
new Point( 20, 20, { index: 2 }),
316+
new Point( 20, -20, { index: 3 })
317+
];
318+
points.forEach(point => quadtree.insert(point));
319+
});
320+
it('throws exception when JSON has no position data', () => {
321+
expect(() => { new QuadTree.fromJSON({ points: [] }) }).to.throw(TypeError);
322+
});
323+
it('saves all data to a JSON object', () => {
324+
const obj = quadtree.toJSON();
325+
expect(obj.x).to.equal(quadtree.boundary.x);
326+
expect(obj.y).to.equal(quadtree.boundary.y);
327+
expect(obj.w).to.equal(quadtree.boundary.w);
328+
expect(obj.h).to.equal(quadtree.boundary.h);
329+
expect(obj.capacity).to.equal(quadtree.capacity);
330+
expect(obj.ne.points.length).to.equal(quadtree.northeast.points.length);
331+
expect(obj.ne.points[0].userData.index).to.equal(quadtree.northeast.points[0].userData.index);
332+
expect(obj.ne.divided).to.be.undefined;
333+
expect(obj.nw).to.be.undefined;
334+
});
335+
it('loads properly from a JSON object', () => {
336+
const obj = quadtree.toJSON();
337+
const test = QuadTree.fromJSON(obj);
338+
expect(test.boundary.x).to.equal(quadtree.boundary.x);
339+
expect(test.boundary.y).to.equal(quadtree.boundary.y);
340+
expect(test.boundary.w).to.equal(quadtree.boundary.w);
341+
expect(test.boundary.h).to.equal(quadtree.boundary.h);
342+
expect(test.capacity).to.equal(quadtree.capacity);
343+
expect(test.northeast.boundary.x).to.equal(quadtree.northeast.boundary.x);
344+
expect(test.northeast.points[0].userData.index).to.equal(quadtree.northeast.points[0].userData.index);
345+
expect(test.northwest.divided).to.be.equal(quadtree.northwest.divided);
346+
expect(test.southeast.x).to.be.equal(quadtree.southeast.x);
347+
expect(test.southwest.y).to.be.equal(quadtree.southwest.y);
348+
});
349+
});
308350
describe('closest', () => {
309351
let quadtree;
310352
let points;
@@ -344,8 +386,7 @@ describe('QuadTree', () => {
344386
expect(found).to.have.length(1);
345387
expect(found).to.contain(points[0]);
346388
});
347-
// no total count of items
348-
it.skip('returns all items when number requested exceeds QuadTree contents', () => {
389+
it('returns all items when number requested exceeds QuadTree contents', () => {
349390
found = quadtree.closest(new Point(0, 0), 10);
350391
expect(found).to.have.length(4);
351392
});
@@ -388,7 +429,7 @@ describe('QuadTree', () => {
388429
points = [];
389430
for (let idx = 0; idx < 10; ++idx) {
390431
points.push(new Point(
391-
bound.left + bound.w * Math.random(),
432+
bound.left + bound.w * Math.random(),
392433
bound.top + bound.h * Math.random()));
393434
}
394435
points.forEach(point => quadtree.insert(point));

0 commit comments

Comments
 (0)