Quadtreeee

Run Settings
LanguageTypeScript
Language Version
Run Command
// Geometry.ts interface Point { x: number; y: number; } /** * Box Representation, provides: * left, right, top bottom * width, height * half width, half height * center */ class Bounds { w: number; h: number; hw: number; hh: number; c: Point; constructor( public l: number, public r: number, public t: number, public b: number ) { this.w = r - l; this.h = t - b; this.hw = this.w * 0.5; this.hh = this.h * 0.5; this.c = { x: this.l + this.w, y: this.b + this.h }; } contains(p: Point) { return p.x >= this.l && p.x < this.r && p.y < this.t && p.y >= this.b; } intersects(c: Bounds) { return !(this.l > c.r || this.r < c.l || this.b > c.t || this.t < c.b); } } //Quadtree.ts class QuadTree { points: Point[] = []; children: QuadTree[] = []; isLeafNode: boolean = true; constructor(public bounds: Bounds, public maxPoints = 1) {} addPoint(p: Point) { // If the point isn't a member of our set if (!this.bounds.contains(p)) return false; // Are we a leaf node? If not, pass the point along if (!this.isLeafNode) { this.children.map(qt => qt.addPoint(p)); return true; } // Point is a member of our set this.points.push(p); // Do we have too many points? if (this.points.length > this.maxPoints) { // Split node and distribute points to children this.split(); return true; } } /** * Splits quadtree node into 4 and disperses current points */ private split() { const { l, r, t, b, hw, hh } = this.bounds; const halfX = l + hw; const halfY = b + hh; // Create four sub cells const ul = new QuadTree(new Bounds(l, r - hw, t, b + hh)); const ur = new QuadTree(new Bounds(l + hw, r, t, b + hh)); const bl = new QuadTree(new Bounds(l, r - hw, t - hh, b)); const br = new QuadTree(new Bounds(l + hw, r, t - hh, b)); // Distribute our points to the children this.points.map(p => { if (p.x > halfX) { p.y > halfY ? ur.points.push(p) : br.points.push(p); } else { p.y > halfY ? ul.points.push(p) : bl.points.push(p); } }); // Store child nodes this.children = [ul, ur, bl, br]; // Delete points this.points = []; // Turn into a normal node this.isLeafNode = false; } getPoints(b: Bounds): Point[] { // If we arn't a leaf node, recurse through tree concatenating any // points we find then returning. Trimming nodes not in the region if (!this.isLeafNode) { const init: Point[] = []; return this.children .filter(c => c.bounds.intersects(b)) .reduce((points, node) => { return points.concat(node.getPoints(b)); }, init); } else { return this.points.filter(p => b.contains(p)); } } } // TEST BULLSHIT // VVVVVVV function test() { let s = new Bounds(0, 200, 200, 0); let c = new Bounds(39, 40, 80, 79); const pointArray: Point[] = []; const qt = new QuadTree(s, 1); function randPoint(b: Bounds) { return { x: Math.random() * b.w + b.l, y: Math.random() * b.h + b.b }; } for (let i = 0; i < 300000; i++) { const newPoint = randPoint(s); qt.addPoint(newPoint); pointArray.push(newPoint); } const quadResults: number[] = []; const arrayResults: number[] = []; function runTest() { const x = Math.random() * s.w + s.l; const y = Math.random() * s.h + s.b; const c = new Bounds(x - 1, x, y, y - 1); quadResults.push(calcQuadAccess(qt, c)); arrayResults.push(calcArrayAccess(pointArray, c)); } for (let i = 0; i < 500; i++) { runTest(); } console.log('quad', average(quadResults)); console.log('array', average(arrayResults)); } function average(a: number[]): number { const s = a.reduce((a, b) => a + b, 0); return s / a.length; } function timeDiff(start: Date, end: Date): number { return ( end.getSeconds() * 1000 + end.getMilliseconds() - start.getSeconds() * 1000 - start.getMilliseconds() ); } function calcQuadAccess(qt: QuadTree, b: Bounds) { const start = new Date(); const cBounds = qt.getPoints(b); const end = new Date(); return timeDiff(start, end); } function calcArrayAccess(pointArray: Point[], b: Bounds) { const startLinear = new Date(); const cPoints = []; for (let i = 0; i < pointArray.length; i++) { if (b.contains(pointArray[i])) cPoints.push(pointArray[i]); } const endLinear = new Date(); return timeDiff(startLinear, endLinear); } // 300,000 points, grabs a small random region 1000 times // Averages access times for point set. test();
Editor Settings
Theme
Key bindings
Full width
Lines