package com.opendoorlogistics.core.distances.external; import java.util.AbstractMap; import java.util.ArrayList; import java.util.Collection; import java.util.LinkedList; import java.util.Map; import com.opendoorlogistics.core.distances.external.InsertionOnlySpatialTree.DefaultSpatialTreeNode; import com.opendoorlogistics.core.distances.external.InsertionOnlySpatialTree.SpatialTreeCoord; import com.opendoorlogistics.core.distances.external.InsertionOnlySpatialTree.SpatialTreeManager; import com.opendoorlogistics.core.distances.external.InsertionOnlySpatialTree.SpatialTreeNode; import com.opendoorlogistics.core.distances.external.InsertionOnlySpatialTree.SpatialTreeQuery; public class PointsOctree extends InsertionOnlySpatialTree{ private PointsOctree(SpatialTreeManager mgr) { super(mgr); } public static class OctreeQuery implements SpatialTreeQuery { private ThreeDPoint pnt; private double radius; public ThreeDPoint getPnt() { return pnt; } public void setPnt(ThreeDPoint pnt) { this.pnt = pnt; } public double getRadius() { return radius; } public void setRadius(double radius) { this.radius = radius; } } public static PointsOctree build(Iterable<Map.Entry<ThreeDPoint, Object>> objects, double minBoxSizeLength) { // get max and min ThreeDPoint max = new ThreeDPoint(-Double.MAX_VALUE, -Double.MAX_VALUE, -Double.MAX_VALUE); ThreeDPoint min = new ThreeDPoint(+Double.MAX_VALUE, +Double.MAX_VALUE, +Double.MAX_VALUE); for (Map.Entry<ThreeDPoint, Object> entry : objects) { for (int i = 0; i < 3; i++) { min.set(i, Math.min(min.get(i), entry.getKey().get(i))); max.set(i, Math.max(max.get(i), entry.getKey().get(i))); } } SpatialTreeManager mgr = new SpatialTreeManager() { @Override public boolean isOverlappingNode(SpatialTreeQuery query, SpatialTreeNode node) { DefaultSpatialTreeNode myNode = (DefaultSpatialTreeNode) node; OctreeQuery oQuery = (OctreeQuery) query; ThreeDPoint searchPnt = oQuery.pnt; double r = oQuery.getRadius(); ThreeDPoint min = (ThreeDPoint) myNode.getMin(); ThreeDPoint max = (ThreeDPoint) myNode.getMax(); for (int i = 0; i < 3; i++) { if(searchPnt.get(i) < min.get(i) - r){ return false; } if(searchPnt.get(i) > max.get(i) + r){ return false; } } return true; } @Override public boolean isContained(SpatialTreeQuery query, SpatialTreeCoord coord) { OctreeQuery oQuery = (OctreeQuery) query; ThreeDPoint pnt = (ThreeDPoint) coord; ThreeDPoint diff = ThreeDPoint.subtract(pnt, oQuery.getPnt()); double absSqd = ThreeDPoint.absSqd(diff); double radiusSqd = oQuery.getRadius() * oQuery.getRadius(); return absSqd <= radiusSqd; } @Override public int getMaxObjectsPerNode() { return 10; } @Override public int getChildIndex(SpatialTreeNode parent, SpatialTreeCoord coord) { DefaultSpatialTreeNode node = (DefaultSpatialTreeNode) parent; ThreeDPoint centre = ThreeDPoint.average((ThreeDPoint) node.getMin(), (ThreeDPoint) node.getMax()); int indexMult = 4; int index = 0; ThreeDPoint threeDPoint = (ThreeDPoint) coord; for (int i = 0; i < 3; i++) { if (threeDPoint.get(i) > centre.get(i)) { index += indexMult; } indexMult /= 2; } return index; } @Override public SpatialTreeNode createRoot() { DefaultSpatialTreeNode root = new DefaultSpatialTreeNode(); root.setMin(min); root.setMax(max); return root; } @Override public Collection<SpatialTreeNode> createChildren(SpatialTreeNode parent) { DefaultSpatialTreeNode myNode = (DefaultSpatialTreeNode) parent; ThreeDPoint width = calcWidth(myNode); boolean[] toSplit = new boolean[3]; int nbToSplit = 0; for (int i = 0; i < 3; i++) { if (width.get(i) >= minBoxSizeLength) { toSplit[i] = true; nbToSplit++; } } if (nbToSplit == 0) { throw new RuntimeException("Error building spatial lookup tree."); } ThreeDPoint min = (ThreeDPoint) myNode.getMin(); ThreeDPoint max = (ThreeDPoint) myNode.getMax(); ThreeDPoint centre = ThreeDPoint.average(min, max); ArrayList<SpatialTreeNode> ret = new ArrayList<>(8); for (int i = 0; i < 8; i++) { ret.add(null); } for (int ix = 0; ix <= 1; ix++) { for (int iy = 0; iy <= 1; iy++) { for (int iz = 0; iz <= 1; iz++) { int index = ix * 4 + iy * 2 + iz; ThreeDPoint newNodeMin = new ThreeDPoint(min); ThreeDPoint newNodeMax = new ThreeDPoint(max); if (toSplit[0]) { newNodeMin.x = ix == 0 ? min.x : centre.x; newNodeMax.x = ix == 0 ? centre.x : max.x; } if (toSplit[1]) { newNodeMin.y = iy == 0 ? min.y : centre.y; newNodeMax.y = iy == 0 ? centre.y : max.y; } if (toSplit[2]) { newNodeMin.z = iz == 0 ? min.z : centre.z; newNodeMax.z = iz == 0 ? centre.z : max.z; } DefaultSpatialTreeNode newNode = new DefaultSpatialTreeNode(); newNode.setMin(newNodeMin); newNode.setMax(newNodeMax); ret.set(index, newNode); } } } return ret; } @Override public boolean isSplittable(SpatialTreeNode node) { DefaultSpatialTreeNode myNode = (DefaultSpatialTreeNode) node; ThreeDPoint width = calcWidth(myNode); for (int i = 0; i < 3; i++) { if (width.get(i) >= minBoxSizeLength) { return true; } } return false; } private ThreeDPoint calcWidth(DefaultSpatialTreeNode myNode) { ThreeDPoint width = ThreeDPoint.subtract((ThreeDPoint) myNode.getMax(), (ThreeDPoint) myNode.getMin()); return width; } }; // finally build the tree PointsOctree ret = new PointsOctree(mgr); for (Map.Entry<ThreeDPoint, Object> entry : objects) { ret.insert(entry.getKey(), entry.getValue()); } return ret; } public Collection<Map.Entry<ThreeDPoint,Object>> query(ThreeDPoint pnt, double radius){ OctreeQuery query = new OctreeQuery(); query.setPnt(pnt); query.setRadius(radius); Collection<Map.Entry<SpatialTreeCoord,Object>> results =super.query(query); LinkedList<Map.Entry<ThreeDPoint,Object>> ret = new LinkedList<>(); results.forEach(e->ret.add(new AbstractMap.SimpleEntry<ThreeDPoint,Object>((ThreeDPoint)e.getKey(),e.getValue()))); return ret; } }