From 85a0e757861e46ed88b480554dae43221eba6244 Mon Sep 17 00:00:00 2001 From: James Seibel Date: Sat, 8 Apr 2023 11:01:35 -0500 Subject: [PATCH] Refactor QuadTree DoSquaresOverlap() and add UnitTest --- .../core/util/objects/quadTree/QuadTree.java | 44 +++++----- core/src/test/java/tests/QuadTreeTest.java | 81 +++++++++++++++++++ 2 files changed, 105 insertions(+), 20 deletions(-) diff --git a/core/src/main/java/com/seibel/lod/core/util/objects/quadTree/QuadTree.java b/core/src/main/java/com/seibel/lod/core/util/objects/quadTree/QuadTree.java index b91fffc81..55c21c1fa 100644 --- a/core/src/main/java/com/seibel/lod/core/util/objects/quadTree/QuadTree.java +++ b/core/src/main/java/com/seibel/lod/core/util/objects/quadTree/QuadTree.java @@ -146,30 +146,34 @@ public class QuadTree // check if the testPos is within the X,Z boundary 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); + DhBlockPos2D treeBlockCorner = this.centerBlockPos.add(new DhBlockPos2D(-this.widthInBlocks/2,-this.widthInBlocks/2)); + DhLodPos treeCornerPos = new DhLodPos((byte)0, treeBlockCorner.x, treeBlockCorner.z); - DhSectionPos sectionCornerOfInput = testPos.convertToDetailLevel((byte)0); - DhLodPos cornerOfInputPos = new DhLodPos((byte)0, sectionCornerOfInput.sectionX, sectionCornerOfInput.sectionZ); - int inputWidth = BitShiftUtil.powerOfTwo(testPos.sectionDetailLevel); + DhSectionPos inputSectionCorner = testPos.convertToDetailLevel((byte)0); + DhLodPos inputCornerPos = new DhLodPos((byte)0, inputSectionCorner.sectionX, inputSectionCorner.sectionZ); + int inputBlockWidth = BitShiftUtil.powerOfTwo(testPos.sectionDetailLevel); - return DoSquaresOverlap(cornerOfTreePos, this.widthInBlocks, cornerOfInputPos, inputWidth); + return DoSquaresOverlap(treeCornerPos, this.widthInBlocks, inputCornerPos, inputBlockWidth); } - private static boolean DoSquaresOverlap(DhLodPos rect1Min, int rect1Width, DhLodPos rect2Min, int rect2Width) + private static boolean DoSquaresOverlap(DhLodPos square1Min, int square1Width, DhLodPos square2Min, int square2Width) { - // Determine the coordinates of the rectangles - float rect1MinX = rect1Min.x; - float rect1MaxX = rect1Min.x + rect1Width; - float rect1MinZ = rect1Min.z; - float rect1MaxZ = rect1Min.z + rect1Width; - - float rect2MinX = rect2Min.x; - float rect2MaxX = rect2Min.x + rect2Width; - float rect2MinZ = rect2Min.z; - float rect2MaxZ = rect2Min.z + rect2Width; - - // Check if the rectangles overlap - return rect1MinX < rect2MaxX && rect1MaxX > rect2MinX && rect1MinZ < rect2MaxZ && rect1MaxZ > rect2MinZ; + // Determine the coordinates of the squares (the variables say rect[angle] because this logic would also work there and was simplified to work for squares) + float rect1MinX = square1Min.x; + float rect1MaxX = square1Min.x + square1Width; + float rect1MinZ = square1Min.z; + float rect1MaxZ = square1Min.z + square1Width; + + float rect2MinX = square2Min.x; + float rect2MaxX = square2Min.x + square2Width; + float rect2MinZ = square2Min.z; + float rect2MaxZ = square2Min.z + square2Width; + + // Check if the squares overlap + return + rect1MinX < rect2MaxX && + rect1MaxX > rect2MinX && + rect1MinZ < rect2MaxZ && + rect1MaxZ > rect2MinZ; } diff --git a/core/src/test/java/tests/QuadTreeTest.java b/core/src/test/java/tests/QuadTreeTest.java index a12dd942e..bb2cd546c 100644 --- a/core/src/test/java/tests/QuadTreeTest.java +++ b/core/src/test/java/tests/QuadTreeTest.java @@ -165,6 +165,27 @@ public class QuadTreeTest Assert.assertEquals("incorrect leaf node count", 4, tree.leafNodeCount()); } + @Test + public void outOfBoundsInTreeTest() + { + // very specific tree parameters to match test results + QuadTree tree = new QuadTree<>(512, new DhBlockPos2D(125, -516), (byte)6); + Assert.assertEquals("Test may need to be re-calculated for different max detail level.", 9, tree.treeMaxDetailLevel); + + + DhSectionPos rootPos = new DhSectionPos((byte)9, 0, -1); + testSet(tree, rootPos, 1); + + // pos is in tree, but out of range + DhSectionPos midPos = new DhSectionPos((byte)8, 0, -1); + testSet(tree, midPos, 2, IndexOutOfBoundsException.class); + + // pos is in tree, but out of range + DhSectionPos leafPos = new DhSectionPos((byte)7, 0, -2); + testSet(tree, leafPos, 3, IndexOutOfBoundsException.class); + + } + @Test public void QuadTreeRootAlignedMovingTest() @@ -738,6 +759,66 @@ public class QuadTreeTest } + // null node auto-deletion not yet implemented + //@Test + public void autoDeleteNullQuadNodeChildTest() + { + QuadNode rootNode = new QuadNode<>(new DhSectionPos((byte)10, 0, 0), LodUtil.BLOCK_DETAIL_LEVEL); + + + rootNode.setValue(new DhSectionPos((byte)10, 0, 0), 0); + + DhSectionPos midNodePos = new DhSectionPos((byte)9, 0, 0); + //rootNode.setValue(midNodePos, null); // holds detail 8 + rootNode.setValue(new DhSectionPos((byte)9, 1, 0), 1); + rootNode.setValue(new DhSectionPos((byte)9, 0, 1), 1); + rootNode.setValue(new DhSectionPos((byte)9, 1, 1), 1); + + rootNode.setValue(new DhSectionPos((byte)8, 0, 0), 2); + rootNode.setValue(new DhSectionPos((byte)8, 1, 0), 2); + rootNode.setValue(new DhSectionPos((byte)8, 0, 1), 2); + rootNode.setValue(new DhSectionPos((byte)8, 1, 1), 2); + + + + // validate nodes were added + Assert.assertEquals(4, rootNode.getChildValueCount()); + Assert.assertEquals(4, rootNode.getNode(midNodePos).getChildValueCount()); + + + + // test removing nodes // + + // remove two leaf nodes from the root + DhSectionPos leafPos = new DhSectionPos((byte)9, 1, 1); + rootNode.setValue(leafPos, null); + Assert.assertEquals(3, rootNode.getChildValueCount()); + Assert.assertNull("Node wasn't deleted", rootNode.getNode(leafPos)); + + leafPos = new DhSectionPos((byte)9, 0, 1); + rootNode.setValue(leafPos, null); + Assert.assertEquals(2, rootNode.getChildValueCount()); + Assert.assertNull("Node wasn't deleted", rootNode.getNode(leafPos)); + + + // remove + + // remove all child nodes + Assert.assertEquals(4, rootNode.getNode(midNodePos).getChildValueCount()); + + // remove all but one, mid-node should still be present + rootNode.setValue(new DhSectionPos((byte)8, 0, 0), null); + rootNode.setValue(new DhSectionPos((byte)8, 0, 1), null); + rootNode.setValue(new DhSectionPos((byte)8, 1, 0), null); + Assert.assertEquals(1, rootNode.getNode(midNodePos).getChildValueCount()); + + // remove last mid-node child, mid-node should now be removed + rootNode.setValue(new DhSectionPos((byte)8, 1, 1), null); + Assert.assertNull("Mid node not deleted.", rootNode.getNode(midNodePos)); + Assert.assertEquals(3, rootNode.getChildValueCount()); + + } +