Add the ability to limit how deep a quad tree can go
This commit is contained in:
@@ -5,6 +5,7 @@ import com.seibel.lod.core.pos.DhSectionPos;
|
||||
import com.seibel.lod.core.util.LodUtil;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class QuadNode<T>
|
||||
@@ -12,7 +13,8 @@ public class QuadNode<T>
|
||||
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
|
||||
|
||||
|
||||
public DhSectionPos sectionPos;
|
||||
public final DhSectionPos sectionPos;
|
||||
public final byte minimumDetailLevel;
|
||||
public T value;
|
||||
|
||||
|
||||
@@ -45,9 +47,10 @@ public class QuadNode<T>
|
||||
|
||||
|
||||
|
||||
public QuadNode(DhSectionPos sectionPos)
|
||||
public QuadNode(DhSectionPos sectionPos, byte minimumDetailLevel)
|
||||
{
|
||||
this.sectionPos = sectionPos;
|
||||
this.minimumDetailLevel = minimumDetailLevel;
|
||||
}
|
||||
|
||||
|
||||
@@ -116,6 +119,8 @@ public class QuadNode<T>
|
||||
*/
|
||||
private T getOrSetValue(DhSectionPos inputSectionPos, boolean replaceValue, T newValue) throws IllegalArgumentException
|
||||
{
|
||||
// debug validation
|
||||
|
||||
if (!this.sectionPos.contains(inputSectionPos))
|
||||
{
|
||||
LOGGER.error((replaceValue ? "set " : "get ")+inputSectionPos+" center block: "+inputSectionPos.getCenter().getCornerBlockPos()+", this pos: "+this.sectionPos+" this center block: "+this.sectionPos.getCenter().getCornerBlockPos());
|
||||
@@ -132,6 +137,14 @@ public class QuadNode<T>
|
||||
throw new IllegalArgumentException("Node and input detail level are equal, however positions are not; this tree doesn't contain the requested position. Node pos: "+this.sectionPos+", input pos: "+inputSectionPos);
|
||||
}
|
||||
|
||||
if (inputSectionPos.sectionDetailLevel < this.minimumDetailLevel)
|
||||
{
|
||||
throw new IllegalArgumentException("Input position is requesting a detail level lower than what this node can provide. Node minimum detail level: "+this.minimumDetailLevel+", input pos: "+inputSectionPos);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// get/set logic
|
||||
if (inputSectionPos.sectionDetailLevel == this.sectionPos.sectionDetailLevel)
|
||||
{
|
||||
// this node is the requested position
|
||||
@@ -162,7 +175,7 @@ public class QuadNode<T>
|
||||
if (replaceValue && this.nwChild == null)
|
||||
{
|
||||
// if no node exists for this position, but we want to insert a new value at this position, create a new node
|
||||
this.nwChild = new QuadNode<>(nwPos);
|
||||
this.nwChild = new QuadNode<>(nwPos, this.minimumDetailLevel);
|
||||
}
|
||||
childNode = this.nwChild;
|
||||
|
||||
@@ -175,7 +188,7 @@ public class QuadNode<T>
|
||||
if (replaceValue && this.swChild == null)
|
||||
{
|
||||
// if no node exists for this position, but we want to insert a new value at this position, create a new node
|
||||
this.swChild = new QuadNode<>(swPos);
|
||||
this.swChild = new QuadNode<>(swPos, this.minimumDetailLevel);
|
||||
}
|
||||
childNode = this.swChild;
|
||||
|
||||
@@ -188,7 +201,7 @@ public class QuadNode<T>
|
||||
if (replaceValue && this.neChild == null)
|
||||
{
|
||||
// if no node exists for this position, but we want to insert a new value at this position, create a new node
|
||||
this.neChild = new QuadNode<>(nePos);
|
||||
this.neChild = new QuadNode<>(nePos, this.minimumDetailLevel);
|
||||
}
|
||||
childNode = this.neChild;
|
||||
|
||||
@@ -201,7 +214,7 @@ public class QuadNode<T>
|
||||
if (replaceValue && this.seChild == null)
|
||||
{
|
||||
// if no node exists for this position, but we want to insert a new value at this position, create a new node
|
||||
this.seChild = new QuadNode<>(sePos);
|
||||
this.seChild = new QuadNode<>(sePos, this.minimumDetailLevel);
|
||||
}
|
||||
childNode = this.seChild;
|
||||
|
||||
@@ -221,11 +234,15 @@ public class QuadNode<T>
|
||||
* Applies the given consumer to all 4 of this nodes' children. <br>
|
||||
* Note: this will pass in null children.
|
||||
*/
|
||||
public void forEachDirectChild(Consumer<QuadNode<T>> callback)
|
||||
public void forEachDirectChild(BiConsumer<QuadNode<T>, DhSectionPos> callback)
|
||||
{
|
||||
for (int i = 0; i < 4; i++)
|
||||
if (this.sectionPos.sectionDetailLevel != this.minimumDetailLevel)
|
||||
{
|
||||
callback.accept(this.getChildByIndex(i));
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
DhSectionPos childPos = this.sectionPos.getChildByIndex(i);
|
||||
callback.accept(this.getChildByIndex(i), childPos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -235,7 +252,7 @@ public class QuadNode<T>
|
||||
*/
|
||||
public void forAllLeafValues(Consumer<? super T> callback)
|
||||
{
|
||||
if (this.childCount() == 0)
|
||||
if (this.childCount() == 0 || this.sectionPos.sectionDetailLevel == this.minimumDetailLevel)
|
||||
{
|
||||
// base case, bottom leaf node found
|
||||
callback.accept(this.value);
|
||||
@@ -254,6 +271,45 @@ public class QuadNode<T>
|
||||
}
|
||||
}
|
||||
|
||||
public void deleteAllChildren() { this.deleteAllChildren(null); }
|
||||
/** @param removedItemConsumer is only fired for non-null nodes, however the value passed in may be null */
|
||||
public void deleteAllChildren(Consumer<? super T> removedItemConsumer)
|
||||
{
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
QuadNode<T> childNode = this.getChildByIndex(i);
|
||||
if (childNode != null)
|
||||
{
|
||||
childNode.deleteAllChildren(removedItemConsumer);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (nwChild != null)
|
||||
{
|
||||
removedItemConsumer.accept(nwChild.value);
|
||||
}
|
||||
nwChild = null;
|
||||
|
||||
if (neChild != null)
|
||||
{
|
||||
removedItemConsumer.accept(neChild.value);
|
||||
}
|
||||
neChild = null;
|
||||
|
||||
if (seChild != null)
|
||||
{
|
||||
removedItemConsumer.accept(seChild.value);
|
||||
}
|
||||
seChild = null;
|
||||
|
||||
if (swChild != null)
|
||||
{
|
||||
removedItemConsumer.accept(swChild.value);
|
||||
}
|
||||
swChild = null;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() { return "pos: "+this.sectionPos+", value: "+this.value; }
|
||||
|
||||
@@ -20,14 +20,14 @@ import java.util.function.Consumer;
|
||||
*/
|
||||
public class QuadTree<T>
|
||||
{
|
||||
public static final byte TREE_LOWEST_DETAIL_LEVEL = 0;
|
||||
|
||||
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
|
||||
|
||||
|
||||
|
||||
/** The largest number detail level in this tree. */
|
||||
public final byte treeMaxDetailLevel;
|
||||
/** The smallest number detail level in this tree. */
|
||||
public final byte treeMinDetailLevel;
|
||||
|
||||
/** contain the actual data in the quad tree structure */
|
||||
private final MovableGridRingList<QuadNode<T>> topRingList;
|
||||
@@ -42,13 +42,14 @@ public class QuadTree<T>
|
||||
* Constructor of the quadTree
|
||||
* @param widthInBlocks equivalent to the distance between two opposing sides
|
||||
*/
|
||||
public QuadTree(int widthInBlocks, DhBlockPos2D centerBlockPos)
|
||||
public QuadTree(int widthInBlocks, DhBlockPos2D centerBlockPos, byte treeMinDetailLevel)
|
||||
{
|
||||
DetailDistanceUtil.updateSettings(); //TODO: Move this to somewhere else
|
||||
this.centerBlockPos = centerBlockPos;
|
||||
this.widthInBlocks = widthInBlocks;
|
||||
|
||||
this.treeMaxDetailLevel = 10; // TODO in the future we may need to make this dynamic // detail 10 = (2^10) 1024 blocks wide
|
||||
this.treeMinDetailLevel = treeMinDetailLevel;
|
||||
|
||||
int halfSizeInRootNodes = Math.floorDiv(this.widthInBlocks, 2) / BitShiftUtil.powerOfTwo(this.treeMaxDetailLevel);
|
||||
halfSizeInRootNodes = halfSizeInRootNodes + 1; // always add 1 so nodes will always have a parent, even if the tree's center is offset from the root node grid
|
||||
@@ -87,7 +88,7 @@ public class QuadTree<T>
|
||||
return null;
|
||||
}
|
||||
|
||||
topQuadNode = new QuadNode<T>(rootPos);
|
||||
topQuadNode = new QuadNode<T>(rootPos, this.treeMinDetailLevel);
|
||||
boolean successfullyAdded = this.topRingList.set(ringListPosX, ringListPosZ, topQuadNode);
|
||||
LodUtil.assertTrue(successfullyAdded, "Failed to add top quadTree node at position: "+rootPos);
|
||||
}
|
||||
@@ -110,12 +111,21 @@ public class QuadTree<T>
|
||||
int radius = this.diameterInBlocks()/2;
|
||||
DhBlockPos2D minPos = this.getCenterBlockPos().add(new DhBlockPos2D(-radius, -radius));
|
||||
DhBlockPos2D maxPos =this.getCenterBlockPos().add(new DhBlockPos2D(radius, radius));
|
||||
throw new IndexOutOfBoundsException("QuadTree GetOrSet failed. Position out of bounds, min pos: "+minPos+", max pos: "+maxPos+", given Position: "+pos+" = block pos: "+pos.convertToDetailLevel(LodUtil.BLOCK_DETAIL_LEVEL));
|
||||
throw new IndexOutOfBoundsException("QuadTree GetOrSet failed. Position out of bounds, min pos: "+minPos+", max pos: "+maxPos+", min detail level: "+this.treeMinDetailLevel+", max detail level: "+this.treeMaxDetailLevel+". Given Position: "+pos+" = block pos: "+pos.convertToDetailLevel(LodUtil.BLOCK_DETAIL_LEVEL));
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isSectionPosInBounds(DhSectionPos testPos)
|
||||
public boolean isSectionPosInBounds(DhSectionPos testPos)
|
||||
{
|
||||
// check if the testPos is within the detail level limits of the tree
|
||||
boolean detailLevelWithinBounds = this.treeMinDetailLevel <= testPos.sectionDetailLevel && testPos.sectionDetailLevel <= this.treeMaxDetailLevel;
|
||||
if (!detailLevelWithinBounds)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// check if the testPos is within the X,Z boundry of the tree
|
||||
DhBlockPos2D blockCornerOfTree = this.centerBlockPos.add(new DhBlockPos2D(-this.widthInBlocks/2,-this.widthInBlocks/2));
|
||||
DhLodPos cornerOfTreePos = new DhLodPos((byte)0, blockCornerOfTree.x, blockCornerOfTree.z);
|
||||
|
||||
@@ -125,7 +135,7 @@ public class QuadTree<T>
|
||||
|
||||
return DoSquaresOverlap(cornerOfTreePos, this.widthInBlocks, cornerOfInputPos, inputWidth);
|
||||
}
|
||||
public static boolean DoSquaresOverlap(DhLodPos rect1Min, int rect1Width, DhLodPos rect2Min, int rect2Width)
|
||||
private static boolean DoSquaresOverlap(DhLodPos rect1Min, int rect1Width, DhLodPos rect2Min, int rect2Width)
|
||||
{
|
||||
// Determine the coordinates of the rectangles
|
||||
float rect1MinX = rect1Min.x;
|
||||
@@ -157,13 +167,14 @@ public class QuadTree<T>
|
||||
}
|
||||
|
||||
/** root nodes can be null */
|
||||
public void forEachRootNodePos(BiConsumer<QuadNode<T>, Pos2D> consumer)
|
||||
public void forEachRootNodePos(BiConsumer<QuadNode<T>, DhSectionPos> consumer)
|
||||
{
|
||||
this.topRingList.forEachPosOrdered((rootNode, pos2D) ->
|
||||
{
|
||||
if (isSectionPosInBounds(new DhSectionPos(this.treeMaxDetailLevel, pos2D.x, pos2D.y)))
|
||||
DhSectionPos rootPos = new DhSectionPos(this.treeMaxDetailLevel, pos2D.x, pos2D.y);
|
||||
if (isSectionPosInBounds(rootPos))
|
||||
{
|
||||
consumer.accept(rootNode, pos2D);
|
||||
consumer.accept(rootNode, rootPos);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import com.seibel.lod.core.pos.DhBlockPos2D;
|
||||
import com.seibel.lod.core.pos.DhSectionPos;
|
||||
import com.seibel.lod.core.util.BitShiftUtil;
|
||||
import com.seibel.lod.core.util.LodUtil;
|
||||
import com.seibel.lod.core.util.objects.quadTree.QuadNode;
|
||||
import com.seibel.lod.core.util.objects.quadTree.QuadTree;
|
||||
import org.apache.logging.log4j.Level;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
@@ -31,6 +32,7 @@ import org.apache.logging.log4j.core.config.Configurator;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
public class QuadTreeTest
|
||||
@@ -59,7 +61,7 @@ public class QuadTreeTest
|
||||
@Test
|
||||
public void BasicPositiveQuadTreeTest()
|
||||
{
|
||||
QuadTree<Integer> tree = new QuadTree<>(BASIC_TREE_WIDTH_IN_BLOCKS, TREE_CENTER_POS);
|
||||
QuadTree<Integer> tree = new QuadTree<>(BASIC_TREE_WIDTH_IN_BLOCKS, TREE_CENTER_POS, LodUtil.BLOCK_DETAIL_LEVEL);
|
||||
Assert.assertEquals("Incorrect basic tree width", BASIC_TREE_ACTUAL_WIDTH_IN_ROOT_NODES, tree.ringListWidth());
|
||||
|
||||
|
||||
@@ -94,7 +96,7 @@ public class QuadTreeTest
|
||||
@Test
|
||||
public void BasicNegativeQuadTreeTest()
|
||||
{
|
||||
QuadTree<Integer> tree = new QuadTree<>(BASIC_TREE_WIDTH_IN_BLOCKS, TREE_CENTER_POS);
|
||||
QuadTree<Integer> tree = new QuadTree<>(BASIC_TREE_WIDTH_IN_BLOCKS, TREE_CENTER_POS, LodUtil.BLOCK_DETAIL_LEVEL);
|
||||
|
||||
|
||||
// root node //
|
||||
@@ -129,7 +131,7 @@ public class QuadTreeTest
|
||||
@Test
|
||||
public void OutOfBoundsQuadTreeTest()
|
||||
{
|
||||
QuadTree<Integer> tree = new QuadTree<>(BASIC_TREE_WIDTH_IN_BLOCKS, new DhBlockPos2D(0,0));
|
||||
QuadTree<Integer> tree = new QuadTree<>(BASIC_TREE_WIDTH_IN_BLOCKS, new DhBlockPos2D(0,0), LodUtil.BLOCK_DETAIL_LEVEL);
|
||||
Assert.assertEquals("tree diameter incorrect", BASIC_TREE_WIDTH_IN_BLOCKS, tree.diameterInBlocks());
|
||||
|
||||
|
||||
@@ -174,7 +176,7 @@ public class QuadTreeTest
|
||||
{
|
||||
int treeWidthInRootNodes = 8;
|
||||
int treeWidthInBlocks = ROOT_NODE_WIDTH_IN_BLOCKS * treeWidthInRootNodes;
|
||||
QuadTree<Integer> tree = new QuadTree<>(treeWidthInBlocks, TREE_CENTER_POS);
|
||||
QuadTree<Integer> tree = new QuadTree<>(treeWidthInBlocks, TREE_CENTER_POS, LodUtil.BLOCK_DETAIL_LEVEL);
|
||||
|
||||
|
||||
// root nodes //
|
||||
@@ -261,7 +263,7 @@ public class QuadTreeTest
|
||||
@Test
|
||||
public void QuadTreeIterationTest()
|
||||
{
|
||||
QuadTree<Integer> tree = new QuadTree<>(BASIC_TREE_WIDTH_IN_BLOCKS, TREE_CENTER_POS);
|
||||
QuadTree<Integer> tree = new QuadTree<>(BASIC_TREE_WIDTH_IN_BLOCKS, TREE_CENTER_POS, LodUtil.BLOCK_DETAIL_LEVEL);
|
||||
|
||||
|
||||
// root nodes //
|
||||
@@ -298,7 +300,7 @@ public class QuadTreeTest
|
||||
@Test
|
||||
public void CenteredGridListIterationTest()
|
||||
{
|
||||
final QuadTree<Integer> tree = new QuadTree<>(MINIMUM_TREE_WIDTH_IN_BLOCKS, TREE_CENTER_POS);
|
||||
final QuadTree<Integer> tree = new QuadTree<>(MINIMUM_TREE_WIDTH_IN_BLOCKS, TREE_CENTER_POS, LodUtil.BLOCK_DETAIL_LEVEL);
|
||||
testSet(tree, new DhSectionPos(tree.treeMaxDetailLevel, 0, 0), 0);
|
||||
|
||||
// confirm the root node were added
|
||||
@@ -308,10 +310,8 @@ public class QuadTreeTest
|
||||
|
||||
// attempt to get and remove, each node in the tree
|
||||
final AtomicInteger rootNodePosCount = new AtomicInteger(0);
|
||||
tree.forEachRootNodePos((renderBufferNode, pos2d) ->
|
||||
tree.forEachRootNodePos((renderBufferNode, sectionPos) ->
|
||||
{
|
||||
DhSectionPos sectionPos = new DhSectionPos(tree.treeMaxDetailLevel, pos2d.x, pos2d.y);
|
||||
|
||||
testGet(tree, sectionPos, 0);
|
||||
testSet(tree, sectionPos, null);
|
||||
|
||||
@@ -326,14 +326,14 @@ public class QuadTreeTest
|
||||
{
|
||||
|
||||
// offset fully inside (10*0,0)
|
||||
final QuadTree<Integer> fullyInsideTree = new QuadTree<>(MINIMUM_TREE_WIDTH_IN_BLOCKS, TREE_CENTER_POS);
|
||||
final QuadTree<Integer> fullyInsideTree = new QuadTree<>(MINIMUM_TREE_WIDTH_IN_BLOCKS, TREE_CENTER_POS, LodUtil.BLOCK_DETAIL_LEVEL);
|
||||
|
||||
DhBlockPos2D fullyInsideOffsetBlockPos = new DhBlockPos2D(MINIMUM_TREE_WIDTH_IN_BLOCKS, MINIMUM_TREE_WIDTH_IN_BLOCKS);
|
||||
fullyInsideTree.setCenterBlockPos(fullyInsideOffsetBlockPos);
|
||||
|
||||
fullyInsideTree.forEachRootNodePos((rootNode, pos2D) ->
|
||||
fullyInsideTree.forEachRootNodePos((rootNode, sectionPos) ->
|
||||
{
|
||||
testSet(fullyInsideTree, new DhSectionPos(fullyInsideTree.treeMaxDetailLevel, pos2D.x, pos2D.y), 0);
|
||||
testSet(fullyInsideTree, sectionPos, 0);
|
||||
});
|
||||
|
||||
// only 1 root node should be added
|
||||
@@ -345,11 +345,11 @@ public class QuadTreeTest
|
||||
|
||||
|
||||
// offset fully inside (10*0,0)
|
||||
final QuadTree<Integer> borderInsideTree = new QuadTree<>(MINIMUM_TREE_WIDTH_IN_BLOCKS, new DhBlockPos2D(MINIMUM_TREE_WIDTH_IN_BLOCKS * 2, MINIMUM_TREE_WIDTH_IN_BLOCKS * 2));
|
||||
final QuadTree<Integer> borderInsideTree = new QuadTree<>(MINIMUM_TREE_WIDTH_IN_BLOCKS, new DhBlockPos2D(MINIMUM_TREE_WIDTH_IN_BLOCKS * 2, MINIMUM_TREE_WIDTH_IN_BLOCKS * 2), LodUtil.BLOCK_DETAIL_LEVEL);
|
||||
|
||||
borderInsideTree.forEachRootNodePos((rootNode, pos2D) ->
|
||||
borderInsideTree.forEachRootNodePos((rootNode, sectionPos) ->
|
||||
{
|
||||
testSet(borderInsideTree, new DhSectionPos(borderInsideTree.treeMaxDetailLevel, pos2D.x, pos2D.y), 0);
|
||||
testSet(borderInsideTree, sectionPos, 0);
|
||||
});
|
||||
|
||||
// only 1 root node should be added
|
||||
@@ -361,14 +361,14 @@ public class QuadTreeTest
|
||||
|
||||
|
||||
// offset across (10*-1,0) and (10*0,0)
|
||||
final QuadTree<Integer> acrossTree = new QuadTree<>(MINIMUM_TREE_WIDTH_IN_BLOCKS, TREE_CENTER_POS);
|
||||
final QuadTree<Integer> acrossTree = new QuadTree<>(MINIMUM_TREE_WIDTH_IN_BLOCKS, TREE_CENTER_POS, LodUtil.BLOCK_DETAIL_LEVEL);
|
||||
|
||||
DhBlockPos2D acrossOffsetBlockPos = new DhBlockPos2D(-MINIMUM_TREE_WIDTH_IN_BLOCKS/4, MINIMUM_TREE_WIDTH_IN_BLOCKS);
|
||||
acrossTree.setCenterBlockPos(acrossOffsetBlockPos);
|
||||
|
||||
acrossTree.forEachRootNodePos((rootNode, pos2D) ->
|
||||
acrossTree.forEachRootNodePos((rootNode, sectionPos) ->
|
||||
{
|
||||
testSet(acrossTree, new DhSectionPos(acrossTree.treeMaxDetailLevel, pos2D.x, pos2D.y), 0);
|
||||
testSet(acrossTree, sectionPos, 0);
|
||||
});
|
||||
|
||||
// 2 root nodes should be added
|
||||
@@ -381,7 +381,7 @@ public class QuadTreeTest
|
||||
@Test
|
||||
public void TinyGridAlignedTreeTest()
|
||||
{
|
||||
QuadTree<Integer> tree = new QuadTree<>(ROOT_NODE_WIDTH_IN_BLOCKS, TREE_CENTER_POS);
|
||||
QuadTree<Integer> tree = new QuadTree<>(ROOT_NODE_WIDTH_IN_BLOCKS, TREE_CENTER_POS, LodUtil.BLOCK_DETAIL_LEVEL);
|
||||
// minimum size tree should be 3 root nodes wide
|
||||
Assert.assertEquals("incorrect tree node width", 3, tree.ringListWidth());
|
||||
Assert.assertEquals("incorrect tree width", ROOT_NODE_WIDTH_IN_BLOCKS, tree.diameterInBlocks());
|
||||
@@ -404,7 +404,7 @@ public class QuadTreeTest
|
||||
@Test
|
||||
public void TinyGridOffsetTreeTest()
|
||||
{
|
||||
QuadTree<Integer> tree = new QuadTree<>(ROOT_NODE_WIDTH_IN_BLOCKS, new DhBlockPos2D(0, 0));
|
||||
QuadTree<Integer> tree = new QuadTree<>(ROOT_NODE_WIDTH_IN_BLOCKS, new DhBlockPos2D(0, 0), LodUtil.BLOCK_DETAIL_LEVEL);
|
||||
// minimum size tree should be 3 root nodes wide
|
||||
Assert.assertEquals("incorrect tree node width", 3, tree.ringListWidth());
|
||||
Assert.assertEquals("incorrect tree width", ROOT_NODE_WIDTH_IN_BLOCKS, tree.diameterInBlocks());
|
||||
@@ -434,6 +434,96 @@ public class QuadTreeTest
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void TreeDetailLevelLimitTest()
|
||||
{
|
||||
QuadTree<Integer> tree = new QuadTree<>(ROOT_NODE_WIDTH_IN_BLOCKS, new DhBlockPos2D(0, 0), (byte)8);
|
||||
Assert.assertEquals("Test detail level's need to be adjusted. This isn't necessarily a failed test.", 10, tree.treeMaxDetailLevel);
|
||||
|
||||
// valid detail levels
|
||||
testSet(tree, new DhSectionPos((byte)10, 0, 0), 1);
|
||||
testSet(tree, new DhSectionPos((byte)9, 0, 0), 2);
|
||||
testSet(tree, new DhSectionPos((byte)8, 0, 0), 3);
|
||||
|
||||
// detail level too low
|
||||
testSet(tree, new DhSectionPos((byte)7, 0, 0), -1, IndexOutOfBoundsException.class);
|
||||
testSet(tree, new DhSectionPos((byte)6, 0, 0), -1, IndexOutOfBoundsException.class);
|
||||
|
||||
// detail level too high
|
||||
testSet(tree, new DhSectionPos((byte)11, 0, 0), -1, IndexOutOfBoundsException.class);
|
||||
testSet(tree, new DhSectionPos((byte)12, 0, 0), -1, IndexOutOfBoundsException.class);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void QuadNodeDetailLimitTest()
|
||||
{
|
||||
QuadTree<Integer> tree = new QuadTree<>(ROOT_NODE_WIDTH_IN_BLOCKS, new DhBlockPos2D(0, 0), (byte)6);
|
||||
Assert.assertEquals("Test detail level's need to be adjusted. This isn't necessarily a failed test.", 10, tree.treeMaxDetailLevel);
|
||||
|
||||
// create the root node
|
||||
testSet(tree, new DhSectionPos((byte)10, 0, 0), 1);
|
||||
|
||||
// recurse down the tree
|
||||
AtomicInteger minimumDetailLevelReachedRef = new AtomicInteger(tree.treeMaxDetailLevel);
|
||||
tree.forEachRootNode((rootNode) ->
|
||||
{
|
||||
rootNode.forEachDirectChild((quadNode, sectionPos) ->
|
||||
{
|
||||
// all sections will be null
|
||||
rootNode.setValue(sectionPos, 0);
|
||||
});
|
||||
|
||||
rootNode.forEachDirectChild((quadNode, sectionPos) ->
|
||||
{
|
||||
recursivelyCreateNodeChildren(quadNode, tree.treeMinDetailLevel, minimumDetailLevelReachedRef);
|
||||
});
|
||||
});
|
||||
|
||||
// confirm that the tree can and did iterate all the way down to the minimum detail level
|
||||
Assert.assertEquals("Minimum detail level never reached", minimumDetailLevelReachedRef.get(), tree.treeMinDetailLevel);
|
||||
}
|
||||
private void recursivelyCreateNodeChildren(QuadNode<Integer> node, byte minDetailLevel, AtomicInteger minimumDetailLevelReachedRef)
|
||||
{
|
||||
AtomicBoolean childNodesCreatedRef = new AtomicBoolean(false);
|
||||
AtomicBoolean childNodesIteratedRef = new AtomicBoolean(false);
|
||||
|
||||
// fill in the null children
|
||||
node.forEachDirectChild((childNode, childSectionPos) ->
|
||||
{
|
||||
node.setValue(childSectionPos, 0);
|
||||
childNodesCreatedRef.set(true);
|
||||
});
|
||||
|
||||
// attempt to recurse down these new children
|
||||
node.forEachDirectChild((childNode, childSectionPos) ->
|
||||
{
|
||||
Assert.assertTrue("Child node recurred too low. Min detail level: "+minDetailLevel+", node detail level: "+childSectionPos.sectionDetailLevel, childSectionPos.sectionDetailLevel >= minDetailLevel);
|
||||
recursivelyCreateNodeChildren(childNode, minDetailLevel, minimumDetailLevelReachedRef);
|
||||
|
||||
childNodesIteratedRef.set(true);
|
||||
});
|
||||
|
||||
|
||||
// keep track of how far down the tree we have gone
|
||||
if (node.sectionPos.sectionDetailLevel < minimumDetailLevelReachedRef.get())
|
||||
{
|
||||
minimumDetailLevelReachedRef.set(node.sectionPos.sectionDetailLevel);
|
||||
}
|
||||
|
||||
|
||||
// assertions
|
||||
if (childNodesCreatedRef.get())
|
||||
{
|
||||
Assert.assertTrue("node children created below minimum detail level", node.sectionPos.sectionDetailLevel >= minDetailLevel);
|
||||
}
|
||||
if (childNodesIteratedRef.get())
|
||||
{
|
||||
Assert.assertTrue("node children iterated below minimum detail level", node.sectionPos.sectionDetailLevel-1 >= minDetailLevel);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user