From 7b795af7b3a2c40fd784423f5d4d2eba9c22c32e Mon Sep 17 00:00:00 2001 From: James Seibel Date: Sat, 25 Mar 2023 11:45:04 -0500 Subject: [PATCH] update the quadTree tests to support the dynamical detail tree --- core/src/test/java/tests/QuadTreeTest.java | 196 +++++++++++---------- 1 file changed, 105 insertions(+), 91 deletions(-) diff --git a/core/src/test/java/tests/QuadTreeTest.java b/core/src/test/java/tests/QuadTreeTest.java index 03899b6dc..be8d20084 100644 --- a/core/src/test/java/tests/QuadTreeTest.java +++ b/core/src/test/java/tests/QuadTreeTest.java @@ -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.MathUtil; import com.seibel.lod.core.util.objects.quadTree.QuadNode; import com.seibel.lod.core.util.objects.quadTree.QuadTree; import org.apache.logging.log4j.Level; @@ -39,20 +40,10 @@ public class QuadTreeTest { private static final Logger LOGGER = DhLoggerBuilder.getLogger(); - private static final DhBlockPos2D TREE_CENTER_POS = new DhBlockPos2D(BitShiftUtil.powerOfTwo(10)/2, BitShiftUtil.powerOfTwo(10)/2); - - private static final int ROOT_NODE_WIDTH_IN_BLOCKS = BitShiftUtil.powerOfTwo(10); - /** needs to be an odd number to function correctly */ - private static final int BASIC_TREE_INPUT_WIDTH_IN_ROOT_NODES = 9; - private static final int BASIC_TREE_WIDTH_IN_BLOCKS = ROOT_NODE_WIDTH_IN_BLOCKS * BASIC_TREE_INPUT_WIDTH_IN_ROOT_NODES; - - /** the tree should be slightly larger to account for offset centers */ - private static final int BASIC_TREE_ACTUAL_WIDTH_IN_ROOT_NODES = BASIC_TREE_INPUT_WIDTH_IN_ROOT_NODES + 2; - - private static final int MINIMUM_TREE_WIDTH_IN_BLOCKS = 32; static { + // by default Log4J doesn't log Info's, which can be a problem when debugging Configurator.setRootLevel(Level.ALL); } @@ -61,11 +52,12 @@ public class QuadTreeTest @Test public void BasicPositiveQuadTreeTest() { - QuadTree 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()); + AbstractTestTreeParams treeParams = new LargeTestTree(); + QuadTree tree = new QuadTree<>(treeParams.getWidthInBlocks(), treeParams.getPositiveEdgeCenterPos(), LodUtil.BLOCK_DETAIL_LEVEL); + Assert.assertTrue("Tree min/max detail level out of expected bounds: "+tree, tree.treeMaxDetailLevel >= 10 && tree.treeMinDetailLevel <= 10 - 4); - // root node // + // (pseudo) root node // testSet(tree, new DhSectionPos((byte)10, 0, 0), 0); // first child (0,0) // @@ -96,7 +88,8 @@ public class QuadTreeTest @Test public void BasicNegativeQuadTreeTest() { - QuadTree tree = new QuadTree<>(BASIC_TREE_WIDTH_IN_BLOCKS, TREE_CENTER_POS, LodUtil.BLOCK_DETAIL_LEVEL); + AbstractTestTreeParams treeParams = new LargeTestTree(); + QuadTree tree = new QuadTree<>(treeParams.getWidthInBlocks(), DhBlockPos2D.ZERO, LodUtil.BLOCK_DETAIL_LEVEL); // root node // @@ -131,41 +124,42 @@ public class QuadTreeTest @Test public void OutOfBoundsQuadTreeTest() { - QuadTree 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()); + AbstractTestTreeParams treeParams = new LargeTestTree(); + QuadTree tree = new QuadTree<>(treeParams.getWidthInBlocks(), new DhBlockPos2D(0,0), LodUtil.BLOCK_DETAIL_LEVEL); + Assert.assertEquals("tree diameter incorrect", treeParams.getWidthInBlocks(), tree.diameterInBlocks()); // wrong detail level on purpose, if the detail level was 0 (block) this should work - DhSectionPos outOfBoundsPos = new DhSectionPos(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, ROOT_NODE_WIDTH_IN_BLOCKS, 0); + DhSectionPos outOfBoundsPos = new DhSectionPos(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, (treeParams.getWidthInBlocks()/2), 0); testSet(tree, outOfBoundsPos, -1, IndexOutOfBoundsException.class); Assert.assertEquals("incorrect leaf node count", 0, tree.leafNodeCount()); // out of bounds // - outOfBoundsPos = new DhSectionPos(LodUtil.BLOCK_DETAIL_LEVEL, (BASIC_TREE_WIDTH_IN_BLOCKS/2) + 1, 0); + outOfBoundsPos = new DhSectionPos(LodUtil.BLOCK_DETAIL_LEVEL, (treeParams.getWidthInBlocks()/2) + 1, 0); testSet(tree, outOfBoundsPos, -1, IndexOutOfBoundsException.class); Assert.assertEquals("incorrect leaf node count", 0, tree.leafNodeCount()); - outOfBoundsPos = new DhSectionPos(LodUtil.BLOCK_DETAIL_LEVEL, (BASIC_TREE_WIDTH_IN_BLOCKS/2), 0); + outOfBoundsPos = new DhSectionPos(LodUtil.BLOCK_DETAIL_LEVEL, (treeParams.getWidthInBlocks()/2), 0); testSet(tree, outOfBoundsPos, -1, IndexOutOfBoundsException.class); Assert.assertEquals("incorrect leaf node count", 0, tree.leafNodeCount()); // in bounds // - outOfBoundsPos = new DhSectionPos(LodUtil.BLOCK_DETAIL_LEVEL, (BASIC_TREE_WIDTH_IN_BLOCKS/2)-1, 0); + outOfBoundsPos = new DhSectionPos(LodUtil.BLOCK_DETAIL_LEVEL, (treeParams.getWidthInBlocks()/2)-1, 0); testSet(tree, outOfBoundsPos, 0); Assert.assertEquals("incorrect leaf node count", 1, tree.leafNodeCount()); - outOfBoundsPos = new DhSectionPos(LodUtil.BLOCK_DETAIL_LEVEL, (BASIC_TREE_WIDTH_IN_BLOCKS/2)-3, 0); + outOfBoundsPos = new DhSectionPos(LodUtil.BLOCK_DETAIL_LEVEL, (treeParams.getWidthInBlocks()/2)-3, 0); testSet(tree, outOfBoundsPos, 0); Assert.assertEquals("incorrect leaf node count", 2, tree.leafNodeCount()); // TODO this position probably has trouble with getting the center. - outOfBoundsPos = new DhSectionPos(LodUtil.BLOCK_DETAIL_LEVEL, (BASIC_TREE_WIDTH_IN_BLOCKS/2)-2, 0); + outOfBoundsPos = new DhSectionPos(LodUtil.BLOCK_DETAIL_LEVEL, (treeParams.getWidthInBlocks()/2)-2, 0); testSet(tree, outOfBoundsPos, 0); Assert.assertEquals("incorrect leaf node count", 3, tree.leafNodeCount()); - outOfBoundsPos = new DhSectionPos(LodUtil.BLOCK_DETAIL_LEVEL, (BASIC_TREE_WIDTH_IN_BLOCKS/2)-4, 0); + outOfBoundsPos = new DhSectionPos(LodUtil.BLOCK_DETAIL_LEVEL, (treeParams.getWidthInBlocks()/2)-4, 0); testSet(tree, outOfBoundsPos, 0); Assert.assertEquals("incorrect leaf node count", 4, tree.leafNodeCount()); @@ -174,12 +168,13 @@ public class QuadTreeTest @Test public void QuadTreeRootAlignedMovingTest() { - int treeWidthInRootNodes = 8; - int treeWidthInBlocks = ROOT_NODE_WIDTH_IN_BLOCKS * treeWidthInRootNodes; - QuadTree tree = new QuadTree<>(treeWidthInBlocks, TREE_CENTER_POS, LodUtil.BLOCK_DETAIL_LEVEL); + AbstractTestTreeParams treeParams = new LargeTestTree(); + QuadTree tree = new QuadTree<>(treeParams.getWidthInBlocks(), treeParams.getPositiveEdgeCenterPos(), LodUtil.BLOCK_DETAIL_LEVEL); + + int pseudoRootNodeWidthInBlocks = BitShiftUtil.powerOfTwo(10); - // root nodes // + // (pseudo) root nodes // testSet(tree, new DhSectionPos((byte)10, 0, 0), 1); // first child (0,0) // @@ -207,7 +202,7 @@ public class QuadTreeTest // small move // - DhBlockPos2D smallMoveBlockPos = new DhBlockPos2D(ROOT_NODE_WIDTH_IN_BLOCKS*2, 0); // move enough that the original root nodes aren't touching the same grid squares they were before, but not far enough as to be garbage collected (TODO reword) + DhBlockPos2D smallMoveBlockPos = new DhBlockPos2D(pseudoRootNodeWidthInBlocks*2, 0); // move enough that the original root nodes aren't touching the same grid squares they were before, but not far enough as to be garbage collected (TODO reword) tree.setCenterBlockPos(smallMoveBlockPos); Assert.assertEquals("Tree center incorrect", smallMoveBlockPos, tree.getCenterBlockPos()); @@ -220,8 +215,8 @@ public class QuadTreeTest - // big move // - DhBlockPos2D bigMoveBlockPos = new DhBlockPos2D(treeWidthInBlocks * 2, 0); + // very large move // + DhBlockPos2D bigMoveBlockPos = new DhBlockPos2D(treeParams.getWidthInBlocks() * 2, 0); tree.setCenterBlockPos(bigMoveBlockPos); Assert.assertEquals("Tree center incorrect", bigMoveBlockPos, tree.getCenterBlockPos()); @@ -242,20 +237,19 @@ public class QuadTreeTest Assert.assertEquals("Tree center incorrect", DhBlockPos2D.ZERO, tree.getCenterBlockPos()); // on the negative X edge - DhSectionPos edgePos = new DhSectionPos(LodUtil.BLOCK_DETAIL_LEVEL, -treeWidthInBlocks/2, 0); + DhSectionPos edgePos = new DhSectionPos(LodUtil.BLOCK_DETAIL_LEVEL, -treeParams.getWidthInBlocks()/2, 0); testSet(tree, edgePos, 1); Assert.assertEquals("incorrect leaf node count", 1, tree.leafNodeCount()); // +1 root node from the negative X edge - DhSectionPos adjacentEdgePos = new DhSectionPos(LodUtil.BLOCK_DETAIL_LEVEL, (-treeWidthInBlocks/2)+ROOT_NODE_WIDTH_IN_BLOCKS, 0); + DhSectionPos adjacentEdgePos = new DhSectionPos(LodUtil.BLOCK_DETAIL_LEVEL, (-treeParams.getWidthInBlocks()/2)+pseudoRootNodeWidthInBlocks, 0); testSet(tree, adjacentEdgePos, 2); Assert.assertEquals("incorrect leaf node count", 2, tree.leafNodeCount()); // move so only the root nodes exactly on the X edge remain - DhBlockPos2D edgeMoveBlockPos = new DhBlockPos2D(ROOT_NODE_WIDTH_IN_BLOCKS - (BASIC_TREE_INPUT_WIDTH_IN_ROOT_NODES*ROOT_NODE_WIDTH_IN_BLOCKS), 0); + DhBlockPos2D edgeMoveBlockPos = new DhBlockPos2D(pseudoRootNodeWidthInBlocks - (treeParams.getWidthInRootNodes()*pseudoRootNodeWidthInBlocks), 0); tree.setCenterBlockPos(edgeMoveBlockPos); Assert.assertEquals("Tree center incorrect", edgeMoveBlockPos, tree.getCenterBlockPos()); - Assert.assertEquals("incorrect leaf node count", 2, tree.leafNodeCount()); } @@ -263,10 +257,11 @@ public class QuadTreeTest @Test public void QuadTreeIterationTest() { - QuadTree tree = new QuadTree<>(BASIC_TREE_WIDTH_IN_BLOCKS, TREE_CENTER_POS, LodUtil.BLOCK_DETAIL_LEVEL); + AbstractTestTreeParams treeParams = new LargeTestTree(); + QuadTree tree = new QuadTree<>(treeParams.getWidthInBlocks(), treeParams.getPositiveEdgeCenterPos(), LodUtil.BLOCK_DETAIL_LEVEL); - // root nodes // + // (pseudo) root nodes // testSet(tree, new DhSectionPos((byte)10, 0, 0), 1); testSet(tree, new DhSectionPos((byte)10, 1, 0), 2); @@ -291,7 +286,7 @@ public class QuadTreeTest }); }); - Assert.assertEquals("incorrect root count", 2, rootNodeCount.get()); + Assert.assertEquals("incorrect root count", 1, rootNodeCount.get()); Assert.assertEquals("incorrect leaf count", 5, leafCount.get()); Assert.assertEquals("incorrect leaf value sum", 20, leafValueSum.get()); @@ -300,7 +295,8 @@ public class QuadTreeTest @Test public void CenteredGridListIterationTest() { - final QuadTree tree = new QuadTree<>(MINIMUM_TREE_WIDTH_IN_BLOCKS, TREE_CENTER_POS, LodUtil.BLOCK_DETAIL_LEVEL); + AbstractTestTreeParams treeParams = new TinyTestTree(); + final QuadTree tree = new QuadTree<>(treeParams.getWidthInBlocks(), treeParams.getPositiveEdgeCenterPos(), LodUtil.BLOCK_DETAIL_LEVEL); testSet(tree, new DhSectionPos(tree.treeMaxDetailLevel, 0, 0), 0); // confirm the root node were added @@ -324,67 +320,48 @@ public class QuadTreeTest @Test public void OffsetGridListIterationTest() { + AbstractTestTreeParams treeParams = new TinyTestTree(); - // offset fully inside (10*0,0) - final QuadTree fullyInsideTree = new QuadTree<>(MINIMUM_TREE_WIDTH_IN_BLOCKS, TREE_CENTER_POS, LodUtil.BLOCK_DETAIL_LEVEL); + // exactly inside (5*0,0) + testGridListRootCount(treeParams.getWidthInBlocks(), treeParams.getPositiveEdgeCenterPos(), 1); - DhBlockPos2D fullyInsideOffsetBlockPos = new DhBlockPos2D(MINIMUM_TREE_WIDTH_IN_BLOCKS, MINIMUM_TREE_WIDTH_IN_BLOCKS); - fullyInsideTree.setCenterBlockPos(fullyInsideOffsetBlockPos); + // offset across (5*-1,0) and (5*0,0) + testGridListRootCount(treeParams.getWidthInBlocks(), new DhBlockPos2D(-treeParams.getWidthInBlocks() / 4, treeParams.getPositiveEdgeCenterPos().z), 2); - fullyInsideTree.forEachRootNodePos((rootNode, sectionPos) -> + // offset across the origin: (5*0,0), (5*-1,0), (5*0,-1), and (5*-1,-1) + testGridListRootCount(treeParams.getWidthInBlocks(), DhBlockPos2D.ZERO, 4); + + } + private static void testGridListRootCount(int treeWidth, DhBlockPos2D treeMovePos, int expectedRootNodeCount) + { + final QuadTree tree = new QuadTree<>(treeWidth, DhBlockPos2D.ZERO, LodUtil.BLOCK_DETAIL_LEVEL); + Assert.assertEquals("tree creation failed, incorrect initial position", DhBlockPos2D.ZERO, tree.getCenterBlockPos()); + + tree.setCenterBlockPos(treeMovePos); + Assert.assertEquals("tree move failed, incorrect position after move", treeMovePos, tree.getCenterBlockPos()); + + tree.forEachRootNodePos((rootNode, sectionPos) -> { - testSet(fullyInsideTree, sectionPos, 0); + testSet(tree, sectionPos, 0); }); - // only 1 root node should be added - final AtomicInteger fullyInsideRootNodeCount = new AtomicInteger(0); - fullyInsideTree.forEachRootNode((rootNode) -> { fullyInsideRootNodeCount.addAndGet(1); }); - Assert.assertEquals("incorrect root count", 1, fullyInsideRootNodeCount.get()); - - - - - // offset fully inside (10*0,0) - final QuadTree 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, sectionPos) -> - { - testSet(borderInsideTree, sectionPos, 0); + // 4 root nodes should be added + final AtomicInteger rootNodeCount = new AtomicInteger(0); + tree.forEachRootNode((rootNode) -> + { + rootNodeCount.addAndGet(1); }); - - // only 1 root node should be added - final AtomicInteger borderInsideRootNodeCount = new AtomicInteger(0); - borderInsideTree.forEachRootNode((rootNode) -> { borderInsideRootNodeCount.addAndGet(1); }); - Assert.assertEquals("incorrect root count", 1, borderInsideRootNodeCount.get()); - - - - - // offset across (10*-1,0) and (10*0,0) - final QuadTree 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, sectionPos) -> - { - testSet(acrossTree, sectionPos, 0); - }); - - // 2 root nodes should be added - final AtomicInteger acrossRootNodeCount = new AtomicInteger(0); - acrossTree.forEachRootNode((rootNode) -> { acrossRootNodeCount.addAndGet(1); }); - Assert.assertEquals("incorrect root count", 2, acrossRootNodeCount.get()); - + Assert.assertEquals("incorrect root count", expectedRootNodeCount, rootNodeCount.get()); } @Test public void TinyGridAlignedTreeTest() { - QuadTree tree = new QuadTree<>(ROOT_NODE_WIDTH_IN_BLOCKS, TREE_CENTER_POS, LodUtil.BLOCK_DETAIL_LEVEL); + AbstractTestTreeParams treeParams = new MediumTestTree(); + QuadTree tree = new QuadTree<>(treeParams.getWidthInBlocks(), treeParams.getPositiveEdgeCenterPos(), 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()); + Assert.assertEquals("incorrect tree width", treeParams.getWidthInBlocks(), tree.diameterInBlocks()); testSet(tree, new DhSectionPos(tree.treeMaxDetailLevel, 0, 0), 0); @@ -404,10 +381,11 @@ public class QuadTreeTest @Test public void TinyGridOffsetTreeTest() { - QuadTree tree = new QuadTree<>(ROOT_NODE_WIDTH_IN_BLOCKS, new DhBlockPos2D(0, 0), LodUtil.BLOCK_DETAIL_LEVEL); + AbstractTestTreeParams treeParams = new MediumTestTree(); + QuadTree tree = new QuadTree<>(treeParams.getWidthInBlocks(), 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()); + Assert.assertEquals("incorrect tree width", treeParams.getWidthInBlocks(), tree.diameterInBlocks()); // 2x2 valid positions (overlap the tree's width) @@ -437,7 +415,8 @@ public class QuadTreeTest @Test public void TreeDetailLevelLimitTest() { - QuadTree tree = new QuadTree<>(ROOT_NODE_WIDTH_IN_BLOCKS, new DhBlockPos2D(0, 0), (byte)8); + AbstractTestTreeParams treeParams = new MediumTestTree(); + QuadTree tree = new QuadTree<>(treeParams.getWidthInBlocks(), 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 @@ -458,7 +437,8 @@ public class QuadTreeTest @Test public void QuadNodeDetailLimitTest() { - QuadTree tree = new QuadTree<>(ROOT_NODE_WIDTH_IN_BLOCKS, new DhBlockPos2D(0, 0), (byte)6); + AbstractTestTreeParams treeParams = new MediumTestTree(); + QuadTree tree = new QuadTree<>(treeParams.getWidthInBlocks(), 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 @@ -547,7 +527,8 @@ public class QuadTreeTest @Test public void toStringTest() { - QuadTree tree = new QuadTree<>(ROOT_NODE_WIDTH_IN_BLOCKS, new DhBlockPos2D(0, 0), (byte)6); + AbstractTestTreeParams treeParams = new MediumTestTree(); + QuadTree tree = new QuadTree<>(treeParams.getWidthInBlocks(), new DhBlockPos2D(0, 0), (byte)6); String treeString = tree.toString(); Assert.assertNotNull(treeString); @@ -604,4 +585,37 @@ public class QuadTreeTest } + + //================// + // helper classes // + //================// + + private abstract static class AbstractTestTreeParams + { + public abstract int getWidthInBlocks(); + + /** the tree should be slightly larger than the width in blocks to account for offset centers */ + public int getWidthInRootNodes() { return MathUtil.log2(this.getWidthInBlocks()) + 2; } + public byte getMaxDetailLevel() { return (byte) MathUtil.log2(this.getWidthInBlocks()); } + /** @return the block pos so that the tree's negative corner lines up with (0,0) */ + public DhBlockPos2D getPositiveEdgeCenterPos() { return new DhBlockPos2D(BitShiftUtil.powerOfTwo(this.getMaxDetailLevel())/2, BitShiftUtil.powerOfTwo(this.getMaxDetailLevel())/2); } + } + + private static class LargeTestTree extends AbstractTestTreeParams + { + public int getWidthInBlocks() { return 8192; } + } + + private static class MediumTestTree extends AbstractTestTreeParams + { + public int getWidthInBlocks() { return 1024; } + + } + + private static class TinyTestTree extends AbstractTestTreeParams + { + public int getWidthInBlocks() { return 32; } + } + + }