--- /dev/null
+package org.simantics.g3d.datastructures;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.vecmath.Point3d;
+
+public class OcTree<T> {
+
+ Point3d center;
+ Set<T> contains;
+ double dx,dy,dz;
+
+ boolean leaf;
+
+ OcTree<T>[] children;
+
+ /**
+ * Creates a octree
+ * @param center center of the octree area
+ * @param dx size of the area along x-axis.
+ * @param dy size of the area along y-axis.
+ * @param dz size of the area along z-axis.
+ * @param depth depth of the tree structure.
+ */
+ public OcTree(Point3d center, double dx, double dy, double dz, int depth) {
+ this.center = center;
+ this.dx = dx;
+ this.dy = dy;
+ this.dz = dz;
+ this.leaf = true;
+ split(depth);
+ }
+
+ private OcTree(Point3d center, double dx, double dy, double dz) {
+ this.center = center;
+ this.dx = dx;
+ this.dy = dy;
+ this.dz = dz;
+ this.leaf = true;
+ }
+
+ /**
+ * Insets an object into the tree
+ * @param object
+ * @param bounds
+ */
+ public void insert(T object, Box bounds) {
+ if (leaf) {
+ contains.add(object);
+ } else {
+
+ boolean ins[] = getAccessArr(bounds);
+ for (int i = 0; i < 8; i++) {
+ if (ins[i])
+ children[i].insert(object, bounds);
+ }
+ }
+ }
+
+ private boolean[] getAccessArr(Box bounds) {
+ boolean pX = bounds.getMin().getX() > center.getX();
+ boolean nX = bounds.getMax().getX() < center.getX();
+ boolean pY = bounds.getMin().getY() > center.getY();
+ boolean nY = bounds.getMax().getY() < center.getY();
+ boolean pZ = bounds.getMin().getZ() > center.getZ();
+ boolean nZ = bounds.getMax().getZ() < center.getZ();
+
+ boolean ins[] = new boolean[8];
+ for (int i = 0; i < 8; i++)
+ ins[i] = true;
+
+ if (pX) {
+ for (int i = 1; i < 8; i+=2) {
+ ins[i] = false;
+ }
+ } else if (nX) {
+ for (int i = 0; i < 8; i+=2) {
+ ins[i] = false;
+ }
+ }
+
+ if (pY) {
+ for (int i = 2; i < 8; ) {
+ ins[i++] = false;
+ ins[i++] = false;
+ i+=2;
+ }
+ } else if (nY) {
+ for (int i = 0; i < 8; ) {
+ ins[i++] = false;
+ ins[i++] = false;
+ i+=2;
+ }
+ }
+
+ if (pZ) {
+ for (int i = 4; i < 8; i++) {
+ ins[i] = false;
+ }
+ } else if (nZ) {
+ for (int i = 0; i < 4; i++) {
+ ins[i] = false;
+ }
+ }
+ return ins;
+ }
+
+ /**
+ * Removes an object from the tree
+ * @param object
+ */
+ public void remove(T object) {
+ if (leaf) {
+ contains.remove(object);
+ } else {
+ for (OcTree<T> n : children) {
+ n.remove(object);
+ }
+ }
+ }
+
+ /**
+ * Returns objects within the given area.
+ * @param bounds
+ * @param set
+ */
+ public void get(Box bounds, Set<T> set) {
+ if (leaf) {
+ set.addAll(contains);
+ } else {
+
+ boolean ins[] = getAccessArr(bounds);
+ for (int i = 0; i < 8; i++) {
+ if (ins[i])
+ children[i].get(bounds, set);
+ }
+ }
+ }
+
+ public void clear() {
+ if (leaf) {
+ contains.clear();
+ } else {
+ for (OcTree<T> n : children)
+ n.clear();
+ }
+ }
+
+ private void split(int depth) {
+ if (!leaf) {
+ throw new IllegalStateException("Node is already split");
+ }
+ if (depth <= 0) {
+ this.contains = new HashSet<>();
+ return;
+ }
+ split();
+ depth--;
+ for (OcTree<T> n : children) {
+ n.split(depth);
+ }
+ }
+
+
+ private void split() {
+ double _dx = dx * 0.5;
+ double _dy = dy * 0.5;
+ double _dz = dz * 0.5;
+ double xd2 = _dx * 0.5;
+ double yd2 = _dy * 0.5;
+ double zd2 = _dz * 0.5;
+ children = new OcTree[8];
+ children[0] = new OcTree<T>(new Point3d(center.x+xd2, center.y+yd2, center.z+zd2),_dx,_dy,_dz);
+ children[1] = new OcTree<T>(new Point3d(center.x-xd2, center.y+yd2, center.z+zd2),_dx,_dy,_dz);
+ children[2] = new OcTree<T>(new Point3d(center.x+xd2, center.y-yd2, center.z+zd2),_dx,_dy,_dz);
+ children[3] = new OcTree<T>(new Point3d(center.x-xd2, center.y-yd2, center.z+zd2),_dx,_dy,_dz);
+ children[4] = new OcTree<T>(new Point3d(center.x+xd2, center.y+yd2, center.z-zd2),_dx,_dy,_dz);
+ children[5] = new OcTree<T>(new Point3d(center.x-xd2, center.y+yd2, center.z-zd2),_dx,_dy,_dz);
+ children[6] = new OcTree<T>(new Point3d(center.x+xd2, center.y-yd2, center.z-zd2),_dx,_dy,_dz);
+ children[7] = new OcTree<T>(new Point3d(center.x-xd2, center.y-yd2, center.z-zd2),_dx,_dy,_dz);
+ leaf = false;
+ }
+
+
+ public void dispose() {
+ if (leaf) {
+ this.contains = null;
+ } else {
+ for (OcTree<T> n : children) {
+ n.dispose();
+ }
+ children = null;
+ }
+ }
+ }