Add minimal octree implementation.
This commit is contained in:
parent
d6da882d9e
commit
8937cafdfd
27
source/geometry/box3d.js
Normal file
27
source/geometry/box3d.js
Normal file
@ -0,0 +1,27 @@
|
||||
OV.Box3D = class
|
||||
{
|
||||
constructor (min, max)
|
||||
{
|
||||
this.min = min;
|
||||
this.max = max;
|
||||
}
|
||||
|
||||
GetMin ()
|
||||
{
|
||||
return this.min;
|
||||
}
|
||||
|
||||
GetMax ()
|
||||
{
|
||||
return this.max;
|
||||
}
|
||||
|
||||
GetCenter ()
|
||||
{
|
||||
return new OV.Coord3D (
|
||||
(this.min.x + this.max.x) / 2.0,
|
||||
(this.min.y + this.max.y) / 2.0,
|
||||
(this.min.z + this.max.z) / 2.0
|
||||
);
|
||||
}
|
||||
};
|
||||
@ -17,6 +17,16 @@ OV.IsGreater = function (a, b)
|
||||
return a - b > OV.Eps;
|
||||
};
|
||||
|
||||
OV.IsLowerOrEqual = function (a, b)
|
||||
{
|
||||
return b - a > -OV.Eps;
|
||||
};
|
||||
|
||||
OV.IsGreaterOrEqual = function (a, b)
|
||||
{
|
||||
return a - b > -OV.Eps;
|
||||
};
|
||||
|
||||
OV.IsEqual = function (a, b)
|
||||
{
|
||||
return Math.abs (b - a) < OV.Eps;
|
||||
|
||||
156
source/geometry/octree.js
Normal file
156
source/geometry/octree.js
Normal file
@ -0,0 +1,156 @@
|
||||
OV.OctreeNode = class
|
||||
{
|
||||
constructor (boundingBox, level)
|
||||
{
|
||||
this.boundingBox = boundingBox;
|
||||
this.level = level;
|
||||
this.pointItems = [];
|
||||
this.childNodes = [];
|
||||
}
|
||||
|
||||
AddPoint (point, data, options)
|
||||
{
|
||||
let node = this.FindNodeForPoint (point);
|
||||
if (node === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (node.FindPointDirectly (point) !== null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (node.pointItems.length < options.maxPointsPerNode || node.level >= options.maxTreeDepth) {
|
||||
node.AddPointDirectly (point, data);
|
||||
return true;
|
||||
} else {
|
||||
node.CreateChildNodes ();
|
||||
let oldPointItems = node.pointItems;
|
||||
node.pointItems = [];
|
||||
for (let i = 0; i < oldPointItems.length; i++) {
|
||||
let pointItem = oldPointItems[i];
|
||||
if (!node.AddPoint (pointItem.point, pointItem.data, options)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return node.AddPoint (point, data, options);
|
||||
}
|
||||
}
|
||||
|
||||
FindPoint (point)
|
||||
{
|
||||
let node = this.FindNodeForPoint (point);
|
||||
if (node === null) {
|
||||
return null;
|
||||
}
|
||||
return node.FindPointDirectly (point);
|
||||
}
|
||||
|
||||
AddPointDirectly (point, data)
|
||||
{
|
||||
this.pointItems.push ({
|
||||
point : point,
|
||||
data : data
|
||||
});
|
||||
}
|
||||
|
||||
FindPointDirectly (point)
|
||||
{
|
||||
for (let i = 0; i < this.pointItems.length; i++) {
|
||||
let pointItem = this.pointItems[i];
|
||||
if (OV.CoordIsEqual3D (point, pointItem.point)) {
|
||||
return pointItem.data;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
FindNodeForPoint (point)
|
||||
{
|
||||
if (!this.IsPointInBounds (point)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (this.childNodes.length === 0) {
|
||||
return this;
|
||||
}
|
||||
|
||||
for (let i = 0; i < this.childNodes.length; i++) {
|
||||
let childNode = this.childNodes[i];
|
||||
let foundNode = childNode.FindNodeForPoint (point);
|
||||
if (foundNode !== null) {
|
||||
return foundNode;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
CreateChildNodes ()
|
||||
{
|
||||
function AddChildNode (node, minX, minY, minZ, sizeX, sizeY, sizeZ)
|
||||
{
|
||||
let box = new OV.Box3D (
|
||||
new OV.Coord3D (minX, minY, minZ),
|
||||
new OV.Coord3D (minX + sizeX, minY + sizeY, minZ + sizeZ)
|
||||
);
|
||||
node.childNodes.push (new OV.OctreeNode (box, node.level + 1, node.options));
|
||||
}
|
||||
|
||||
let min = this.boundingBox.min;
|
||||
let center = this.boundingBox.GetCenter ();
|
||||
let sizeX = (this.boundingBox.max.x - this.boundingBox.min.x) / 2.0;
|
||||
let sizeY = (this.boundingBox.max.y - this.boundingBox.min.y) / 2.0;
|
||||
let sizeZ = (this.boundingBox.max.z - this.boundingBox.min.z) / 2.0;
|
||||
|
||||
AddChildNode (this, min.x, min.y, min.z, sizeX, sizeY, sizeZ);
|
||||
AddChildNode (this, center.x, min.y, min.z, sizeX, sizeY, sizeZ);
|
||||
AddChildNode (this, min.x, center.y, min.z, sizeX, sizeY, sizeZ);
|
||||
AddChildNode (this, center.x, center.y, min.z, sizeX, sizeY, sizeZ);
|
||||
AddChildNode (this, min.x, min.y, center.z, sizeX, sizeY, sizeZ);
|
||||
AddChildNode (this, center.x, min.y, center.z, sizeX, sizeY, sizeZ);
|
||||
AddChildNode (this, min.x, center.y, center.z, sizeX, sizeY, sizeZ);
|
||||
AddChildNode (this, center.x, center.y, center.z, sizeX, sizeY, sizeZ);
|
||||
}
|
||||
|
||||
IsPointInBounds (point)
|
||||
{
|
||||
let isEqual =
|
||||
OV.IsGreaterOrEqual (point.x, this.boundingBox.min.x) &&
|
||||
OV.IsGreaterOrEqual (point.y, this.boundingBox.min.y) &&
|
||||
OV.IsGreaterOrEqual (point.z, this.boundingBox.min.z) &&
|
||||
OV.IsLowerOrEqual (point.x, this.boundingBox.max.x) &&
|
||||
OV.IsLowerOrEqual (point.y, this.boundingBox.max.y) &&
|
||||
OV.IsLowerOrEqual (point.z, this.boundingBox.max.z);
|
||||
return isEqual;
|
||||
}
|
||||
};
|
||||
|
||||
OV.Octree = class
|
||||
{
|
||||
constructor (boundingBox, options)
|
||||
{
|
||||
this.options = {
|
||||
maxPointsPerNode : 10,
|
||||
maxTreeDepth : 10
|
||||
};
|
||||
if (options !== undefined) {
|
||||
if (options.maxPointsPerNode !== undefined) {
|
||||
this.options.maxPointsPerNode = options.maxPointsPerNode;
|
||||
}
|
||||
if (options.maxTreeDepth !== undefined) {
|
||||
this.options.maxTreeDepth = options.maxTreeDepth;
|
||||
}
|
||||
}
|
||||
this.rootNode = new OV.OctreeNode (boundingBox, 0, this.options);
|
||||
}
|
||||
|
||||
AddPoint (point, data)
|
||||
{
|
||||
return this.rootNode.AddPoint (point, data, this.options);
|
||||
}
|
||||
|
||||
FindPoint (point)
|
||||
{
|
||||
return this.rootNode.FindPoint (point);
|
||||
}
|
||||
};
|
||||
@ -1,5 +1,11 @@
|
||||
var assert = require ('assert');
|
||||
|
||||
function SeededRandom (from, to, seed)
|
||||
{
|
||||
var random = ((seed * 9301 + 49297) % 233280) / 233280;
|
||||
return random * (to - from) + from;
|
||||
}
|
||||
|
||||
function CreateYRot90Quaternion ()
|
||||
{
|
||||
let angle = Math.PI / 2.0;
|
||||
@ -15,6 +21,37 @@ function CreateYRot90Quaternion ()
|
||||
return quaternion;
|
||||
}
|
||||
|
||||
describe ('Comparison', function () {
|
||||
it ('IsGreater', function () {
|
||||
assert (OV.IsEqual (1.0, 1.0));
|
||||
assert (OV.IsEqual (1.0, 1.000000001));
|
||||
assert (!OV.IsEqual (1.0, 1.0001));
|
||||
});
|
||||
|
||||
it ('IsGreater', function () {
|
||||
assert (OV.IsGreater (1.0, 0.0));
|
||||
assert (OV.IsGreater (1.0001, 1.0));
|
||||
assert (!OV.IsGreater (1.000000001, 1.0));
|
||||
|
||||
assert (OV.IsGreaterOrEqual (1.0001, 1.0));
|
||||
assert (OV.IsGreaterOrEqual (1.000000001, 1.0));
|
||||
assert (OV.IsGreaterOrEqual (0.999999999, 1.0));
|
||||
assert (!OV.IsGreaterOrEqual (0.999, 1.0));
|
||||
});
|
||||
|
||||
it ('IsLower', function () {
|
||||
assert (OV.IsLower (0.0, 1.0));
|
||||
assert (OV.IsLower (1.0, 1.0001));
|
||||
assert (!OV.IsLower (1.0, 1.000000001));
|
||||
|
||||
assert (OV.IsLowerOrEqual (1.0, 1.0001));
|
||||
assert (OV.IsLowerOrEqual (1.0, 1.000000001));
|
||||
assert (OV.IsLowerOrEqual (1.0, 0.999999999));
|
||||
assert (!OV.IsLowerOrEqual (1.0, 0.999));
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe ('Coord', function () {
|
||||
it ('Length', function () {
|
||||
var c = new OV.Coord3D (2.0, 0.0, 0.0);
|
||||
@ -144,3 +181,101 @@ describe ('Tween', function() {
|
||||
assert (OV.CoordIsEqual3D (segments[segments.length - 1], end));
|
||||
});
|
||||
});
|
||||
|
||||
describe ('Octree', function() {
|
||||
it ('Add Point', function () {
|
||||
let octree = new OV.Octree (new OV.Box3D (
|
||||
new OV.Coord3D (-10.0, -10.0, -10.0),
|
||||
new OV.Coord3D (10.0, 10.0, 10.0)
|
||||
));
|
||||
|
||||
let p1 = new OV.Coord3D (0.0, 0.0, 0.0);
|
||||
assert (octree.AddPoint (p1, 'p1'));
|
||||
assert (!octree.AddPoint (p1, 'p2'));
|
||||
assert.strictEqual (octree.FindPoint (p1), 'p1')
|
||||
});
|
||||
|
||||
it ('Add Points', function () {
|
||||
let octree = new OV.Octree (new OV.Box3D (
|
||||
new OV.Coord3D (-10.0, -10.0, -10.0),
|
||||
new OV.Coord3D (10.0, 10.0, 10.0)
|
||||
));
|
||||
|
||||
let p1 = new OV.Coord3D (0.0, 0.0, 0.0);
|
||||
let p2 = new OV.Coord3D (1.0, 1.0, 1.0);
|
||||
let p3 = new OV.Coord3D (-1.0, 1.0, 1.0);
|
||||
let p4 = new OV.Coord3D (-1.0, -1.0, 1.0);
|
||||
let p5 = new OV.Coord3D (-1.0, -1.0, -1.0);
|
||||
let p6 = new OV.Coord3D (2.0, 2.0, 2.0);
|
||||
let p7 = new OV.Coord3D (-2.0, 2.0, 2.0);
|
||||
let p8 = new OV.Coord3D (-2.0, -2.0, 2.0);
|
||||
let p9 = new OV.Coord3D (-2.0, -2.0, -2.0);
|
||||
|
||||
assert (octree.AddPoint (p1, 'p1'));
|
||||
assert (octree.AddPoint (p2, 'p2'));
|
||||
assert (octree.AddPoint (p3, 'p3'));
|
||||
assert (octree.AddPoint (p4, 'p4'));
|
||||
assert (octree.AddPoint (p5, 'p5'));
|
||||
assert (octree.AddPoint (p6, 'p6'));
|
||||
assert (octree.AddPoint (p7, 'p7'));
|
||||
assert (octree.AddPoint (p8, 'p8'));
|
||||
assert (octree.AddPoint (p9, 'p9'));
|
||||
|
||||
assert.strictEqual (octree.FindPoint (p1), 'p1');
|
||||
assert.strictEqual (octree.FindPoint (p2), 'p2');
|
||||
assert.strictEqual (octree.FindPoint (p3), 'p3');
|
||||
assert.strictEqual (octree.FindPoint (p4), 'p4');
|
||||
assert.strictEqual (octree.FindPoint (p5), 'p5');
|
||||
assert.strictEqual (octree.FindPoint (p6), 'p6');
|
||||
assert.strictEqual (octree.FindPoint (p7), 'p7');
|
||||
assert.strictEqual (octree.FindPoint (p8), 'p8');
|
||||
assert.strictEqual (octree.FindPoint (p9), 'p9');
|
||||
});
|
||||
|
||||
it ('Add Points On Boundaries', function () {
|
||||
let octree = new OV.Octree (new OV.Box3D (
|
||||
new OV.Coord3D (-10.0, -10.0, -10.0),
|
||||
new OV.Coord3D (10.0, 10.0, 10.0)
|
||||
));
|
||||
|
||||
let p1 = new OV.Coord3D (10.0, 10.0, 10.0);
|
||||
let p2 = new OV.Coord3D (-10.0, -10.0, -10.0);
|
||||
let p3 = new OV.Coord3D (20.0, 20.0, 20.0);
|
||||
|
||||
assert (octree.AddPoint (p1, 'p1'));
|
||||
assert (octree.AddPoint (p2, 'p2'));
|
||||
assert (!octree.AddPoint (p3, 'p3'));
|
||||
|
||||
assert.strictEqual (octree.FindPoint (p1), 'p1');
|
||||
assert.strictEqual (octree.FindPoint (p2), 'p2');
|
||||
assert.strictEqual (octree.FindPoint (p3), null);
|
||||
});
|
||||
|
||||
it ('Stress Test', function () {
|
||||
let box = new OV.Box3D (
|
||||
new OV.Coord3D (-10.0, -10.0, -10.0),
|
||||
new OV.Coord3D (10.0, 10.0, 10.0)
|
||||
);
|
||||
|
||||
let octree = new OV.Octree (box);
|
||||
|
||||
let count = 1000;
|
||||
let seed = 1;
|
||||
let points = [];
|
||||
for (let i = 0; i < count; i++) {
|
||||
let x = SeededRandom (-10.0, 10.0, seed++);
|
||||
let y = SeededRandom (-10.0, 10.0, seed++);
|
||||
let z = SeededRandom (-10.0, 10.0, seed++);
|
||||
let point = new OV.Coord3D (x, y, z);
|
||||
points.push (point);
|
||||
}
|
||||
for (let i = 0; i < count; i++) {
|
||||
let point = points[i];
|
||||
assert (octree.AddPoint (point, i.toString ()));
|
||||
}
|
||||
for (let i = 0; i < count; i++) {
|
||||
let point = points[i];
|
||||
assert.strictEqual (octree.FindPoint (point), i.toString ());
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@ -10,6 +10,8 @@
|
||||
"source/geometry/geometry.js",
|
||||
"source/geometry/coord2d.js",
|
||||
"source/geometry/coord3d.js",
|
||||
"source/geometry/box3d.js",
|
||||
"source/geometry/octree.js",
|
||||
"source/geometry/matrix.js",
|
||||
"source/geometry/transformation.js",
|
||||
"source/geometry/tween.js",
|
||||
|
||||
Loading…
Reference in New Issue
Block a user