Refactor QuadTree DoSquaresOverlap() and add UnitTest

This commit is contained in:
James Seibel
2023-04-08 11:01:35 -05:00
parent df646bb87c
commit 85a0e75786
2 changed files with 105 additions and 20 deletions
@@ -146,30 +146,34 @@ public class QuadTree<T>
// 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;
}
@@ -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<Integer> 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<Integer> 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());
}