@timohausmann/quadtree-js
Version:
Another quadtree implementation for javascript
336 lines (252 loc) • 10.6 kB
HTML
<html>
<head>
<title>quadtree-js Retrieve Nodes</title>
<link rel="stylesheet" type="text/css" href="style.css?v=2" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<!-- prism syntax highlighting (https://prismjs.com/) -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.21.0/themes/prism.min.css" integrity="sha512-tN7Ec6zAFaVSG3TpNAKtk4DOHNpSwKHxxrsiw4GHKESGPs5njn/0sMCUMl2svV4wo4BK/rCP7juYz+zx+l6oeQ==" crossorigin="anonymous" />
</head>
<body>
<div class="outer">
<h1><a href="https://github.com/timohausmann/quadtree-js">quadtree-js</a> <small>retrieve nodes</small></h1>
<div id="canvasContainer">
<canvas id="canvas" width="640" height="480"></canvas>
</div>
<p>
<strong>Retrieve Nodes Example</strong>
</p>
<p>
To retrieve that match your query and contain objects you can overwrite the retrieve function like below or use the <a href="https://github.com/timohausmann/quadtree-js/tree/retrieve-nodes">retrieve-nodes branch</a>.
</p>
<p>
Github Issue: <a href="https://github.com/timohausmann/quadtree-js/issues/17">quadtree-js/issues/17</a>
</p>
<pre><code class="language-javascript">Quadtree.prototype.retrieve = function (pRect) {
var indexes = this.getIndex(pRect),
objects = this.objects;
//modification: collect nodes
var nodes = [];
if (this.objects.length) nodes.push(this);
//if we have subnodes, retrieve their objects
if (this.nodes.length) {
for (var i = 0; i < indexes.length; i++) {
//modification: collect objects and nodes
const retrieved = this.nodes[indexes[i]].retrieve(pRect);
objects = objects.concat(retrieved.objects);
nodes = nodes.concat(retrieved.nodes);
}
}
//remove duplicate objects
objects = objects.filter(function (item, index) {
return objects.indexOf(item) >= index;
});
//modification: return objects and nodes
return {
objects: objects,
nodes: nodes
}
};</code></pre>
</div>
<!-- prism syntax highlighting -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.21.0/prism.min.js" integrity="sha512-WkVkkoB31AoI9DAk6SEEEyacH9etQXKUov4JRRuM1Y681VsTq7jYgrRw06cbP6Io7kPsKx+tLFpH/HXZSZ2YEQ==" crossorigin="anonymous"></script>
<!-- quadtree lib and script -->
<script src="https://cdn.jsdelivr.net/npm/@timohausmann/quadtree-js/quadtree.min.js"></script>
<script>
/* ============================= */
/* patch retrieve function START */
/* see https://github.com/timohausmann/quadtree-js/issues/17 */
/* ============================= */
Quadtree.prototype.retrieve = function (pRect) {
var indexes = this.getIndex(pRect),
objects = this.objects;
//modification:
//collect nodes that match the query
var nodes = [];
if (this.objects.length) nodes.push(this);
//if we have subnodes, retrieve their objects
if (this.nodes.length) {
for (var i = 0; i < indexes.length; i++) {
//modification: collect objects and nodes
const retrieved = this.nodes[indexes[i]].retrieve(pRect);
objects = objects.concat(retrieved.objects);
nodes = nodes.concat(retrieved.nodes);
}
}
//remove duplicate objects
objects = objects.filter(function (item, index) {
return objects.indexOf(item) >= index;
});
//modification: return objects and nodes
return {
objects: objects,
nodes: nodes
}
};
/* ============================= */
/* patch retrieve function END */
/* ============================= */
(function (w, M) {
w.requestAnimFrame = (function () {
return w.requestAnimationFrame ||
w.webkitRequestAnimationFrame ||
w.mozRequestAnimationFrame ||
w.oRequestAnimationFrame ||
w.msRequestAnimationFrame ||
function (callback) {
w.setTimeout(callback, 1000 / 60);
};
})();
/*
* the main Quadtree
*/
var myTree = new Quadtree({
x: 0,
y: 0,
width: 640,
height: 480
}, 4);
/*
* our objects will be stored here
*/
var myObjects = [];
for (var i = 0; i < 50; i++) {
var rect = {
x: randMinMax(0, myTree.bounds.width - 32),
y: randMinMax(0, myTree.bounds.height - 32),
width: randMinMax(4, 32, true),
height: randMinMax(4, 32, true),
check: false
};
//store object in our array
myObjects.push(rect);
//insert object in our quadtree
myTree.insert(rect);
};
/*
* our "hero", aka the mouse cursor.
* He is not in the quadtree, we only use this object to retrieve objects from a certain area
*/
var myCursor = {
x: 0,
y: 0,
width: 28,
height: 28
};
var isMouseover = false;
var ctx = document.getElementById('canvas').getContext('2d');
var cnt_total = document.querySelector('#cnt_total'),
cnt_cand = document.querySelector('#cnt_cand'),
cnt_perc = document.querySelector('#cnt_perc');
/*
* position hero at mouse
*/
var handleMousemove = function (e) {
isMouseover = true;
if (!e.offsetX) {
e.offsetX = e.layerX - e.target.offsetLeft;
e.offsetY = e.layerY - e.target.offsetTop;
}
myCursor.x = e.offsetX - (myCursor.width / 2);
myCursor.y = e.offsetY - (myCursor.height / 2);
};
/*
* hide hero
*/
var handleMouseout = function (e) {
isMouseover = false;
};
/*
* draw Quadtree nodes
*/
var drawQuadtree = function (node) {
var bounds = node.bounds;
//no subnodes? draw the current node
if (node.nodes.length === 0) {
if (node.check) {
ctx.strokeStyle = 'rgba(255,255,255,1)';
} else {
ctx.strokeStyle = 'rgba(255,0,0,0.5)';
}
ctx.strokeRect(bounds.x, bounds.y, bounds.width, bounds.height);
//has subnodes? drawQuadtree them!
} else {
for (var i = 0; i < node.nodes.length; i = i + 1) {
drawQuadtree(node.nodes[i]);
}
}
};
/*
* draw all objects
*/
var drawObjects = function () {
var obj;
for (var i = 0; i < myObjects.length; i = i + 1) {
obj = myObjects[i];
if (obj.check) {
ctx.fillStyle = 'rgba(48,255,48,0.5)';
ctx.fillRect(obj.x, obj.y, obj.width, obj.height);
} else {
ctx.strokeStyle = 'rgba(255,255,255,0.5)';
ctx.strokeRect(obj.x, obj.y, obj.width, obj.height);
}
}
};
/*
* our main loop
*/
var loop = function () {
var candidates = [];
ctx.clearRect(0, 0, 640, 480);
//reset myObjects check flag
for (var i = 0; i < myObjects.length; i = i + 1) {
myObjects[i].check = false;
}
//reset nodes check flag
resetFlag(myTree);
function resetFlag(node) {
node.check = false;
for (let i = 0; i < node.nodes.length; i++) {
resetFlag(node.nodes[i]);
}
}
if (isMouseover) {
ctx.fillStyle = 'rgba(255,255,255,0.5)';
ctx.fillRect(myCursor.x, myCursor.y, myCursor.width, myCursor.height);
//retrieve all objects in the bounds of the hero
candidates = myTree.retrieve(myCursor);
//flag retrieved objects
for (i = 0; i < candidates.objects.length; i = i + 1) {
candidates.objects[i].check = true;
}
//flag retrieved nodes
for (i = 0; i < candidates.nodes.length; i = i + 1) {
candidates.nodes[i].check = true;
}
}
drawQuadtree(myTree);
drawObjects();
requestAnimFrame(loop);
};
//init first loop
loop();
//set eventListener for mousemove
document.getElementById('canvas').addEventListener('mousemove', handleMousemove);
document.getElementById('canvas').addEventListener('mouseout', handleMouseout);
/**
* return a random number within given boundaries.
*
* @param {number} min the lowest possible number
* @param {number} max the highest possible number
* @param {boolean} round if true, return integer
* @return {number} a random number
*/
function randMinMax(min, max, round) {
var val = min + (Math.random() * (max - min));
if (round) val = Math.round(val);
return val;
};
})(window, Math);
</script>
</body>
</html>