119 lines
3.5 KiB
Java
119 lines
3.5 KiB
Java
package com.hypixel.hytale.builtin.portals.utils.spatial;
|
|
|
|
import com.hypixel.hytale.math.shape.Box;
|
|
import com.hypixel.hytale.math.vector.Vector3d;
|
|
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
|
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
|
|
public class OctTree<T> {
|
|
private static final int SIZE = 8;
|
|
private static final int DEFAULT_NODE_CAPACITY = 4;
|
|
private final OctTree<T>.Node root;
|
|
private final int nodeCapacity;
|
|
|
|
public OctTree(double inradius) {
|
|
this(new Box(-inradius, -inradius, -inradius, inradius, inradius, inradius), 4);
|
|
}
|
|
|
|
public OctTree(Box boundary) {
|
|
this(boundary, 4);
|
|
}
|
|
|
|
public OctTree(Box boundary, int nodeCapacity) {
|
|
this.root = new OctTree.Node(boundary);
|
|
this.nodeCapacity = nodeCapacity;
|
|
}
|
|
|
|
public void add(Vector3d pos, T value) {
|
|
this.add(this.root, pos, value);
|
|
}
|
|
|
|
private boolean add(OctTree<T>.Node node, Vector3d pos, T value) {
|
|
if (node != null && node.boundary.containsPosition(pos)) {
|
|
if (node.size() < this.nodeCapacity) {
|
|
node.addPoint(pos, value);
|
|
return true;
|
|
} else {
|
|
if (node.dirs.isEmpty()) {
|
|
this.subdivide(node);
|
|
}
|
|
|
|
for (int i = 0; i < 8; i++) {
|
|
if (this.add(node.dirs.get(i), pos, value)) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private void subdivide(OctTree<T>.Node node) {
|
|
Vector3d min = node.boundary.min;
|
|
double side = node.boundary.width() / 2.0;
|
|
|
|
for (int i = 0; i < 8; i++) {
|
|
Vector3d subMin = new Vector3d(min.x + ((i & 1) > 0 ? side : 0.0), min.y + ((i & 2) > 0 ? side : 0.0), min.z + ((i & 4) > 0 ? side : 0.0));
|
|
Box sub = Box.cube(subMin, side);
|
|
node.dirs.add(new OctTree.Node(sub));
|
|
}
|
|
}
|
|
|
|
public Map<T, Vector3d> getAllPoints() {
|
|
return this.queryRange(this.root.boundary);
|
|
}
|
|
|
|
public Map<T, Vector3d> queryRange(Vector3d position, double inradius) {
|
|
Box range = Box.centeredCube(position, inradius);
|
|
return this.queryRange(range);
|
|
}
|
|
|
|
public Map<T, Vector3d> queryRange(Box range) {
|
|
Map<T, Vector3d> out = new Object2ObjectOpenHashMap();
|
|
this.queryRange(this.root, range, out);
|
|
return out;
|
|
}
|
|
|
|
private void queryRange(OctTree<T>.Node node, Box range, Map<T, Vector3d> out) {
|
|
if (node != null && node.boundary.isIntersecting(range)) {
|
|
for (int i = 0; i < node.size(); i++) {
|
|
Vector3d point = node.points[i];
|
|
if (range.containsPosition(point)) {
|
|
T value = node.values.get(i);
|
|
out.put(value, point);
|
|
}
|
|
}
|
|
|
|
for (OctTree<T>.Node dir : node.dirs) {
|
|
this.queryRange(dir, range, out);
|
|
}
|
|
}
|
|
}
|
|
|
|
private class Node {
|
|
private final Box boundary;
|
|
private final List<OctTree<T>.Node> dirs = new ObjectArrayList(8);
|
|
private final Vector3d[] points = new Vector3d[OctTree.this.nodeCapacity];
|
|
private final List<T> values = new ObjectArrayList(OctTree.this.nodeCapacity);
|
|
private int count;
|
|
|
|
public Node(Box boundary) {
|
|
this.boundary = boundary;
|
|
}
|
|
|
|
public int size() {
|
|
return this.count;
|
|
}
|
|
|
|
public void addPoint(Vector3d pos, T value) {
|
|
this.points[this.count++] = pos;
|
|
this.values.add(value);
|
|
}
|
|
}
|
|
}
|