private IEnumerator TestSplitAlongAxis(Vector3Int offset, Axis relativeZ, int relXIndex)
    {
        Utils.GetOtherAxes(relativeZ, out Axis relativeX, out Axis relativeY);

        Vector3Int dimensions = new Vector3Int(5, 5, 5);
        int        width      = dimensions.Get(relativeX);
        int        height     = dimensions.Get(relativeY);
        int        depth      = dimensions.Get(relativeZ);

        yield return(TestFindClusters(offset, dimensions, ManipulateVoxels, ValidateResults));

        void ManipulateVoxels(Octree <bool> voxelMap, Queue <Vector3Int> dirtyVoxels)
        {
            for (int relYIndex = 0; relYIndex < height; relYIndex++)
            {
                for (int relZIndex = 0; relZIndex < depth; relZIndex++)
                {
                    Vector3Int pos = Vector3Int.zero;

                    pos = pos.Set(relativeX, relXIndex);
                    pos = pos.Set(relativeY, relYIndex);
                    pos = pos.Set(relativeZ, relZIndex);

                    VoxelGrid.TryRemoveVoxel(pos, voxelMap, dirtyVoxels);
                }
            }
        }

        void ValidateResults(List <Octree <bool> > foundClusters)
        {
            if (relXIndex == 0 || relXIndex == width - 1)
            {
                Assert.AreEqual(1, foundClusters.Count);

                Octree <bool> cluster = foundClusters[0];

                Assert.AreEqual(width - 1, cluster.Dimensions.Get(relativeX));
                Assert.AreEqual(height, cluster.Dimensions.Get(relativeY));
                Assert.AreEqual(depth, cluster.Dimensions.Get(relativeZ));

                for (int z = 0; z < cluster.Dimensions.z; z++)
                {
                    for (int y = 0; y < cluster.Dimensions.y; y++)
                    {
                        for (int x = 0; x < cluster.Dimensions.x; x++)
                        {
                            Assert.IsTrue(cluster.TryGetValue(new Vector3Int(x, y, z), out bool value));
                            Assert.IsTrue(value);
                        }
                    }
                }
            }
            else
            {
                Assert.AreEqual(2, foundClusters.Count);

                bool          isZeroSmaller  = foundClusters[0].Dimensions.Get(relativeX) < foundClusters[1].Dimensions.Get(relativeX);
                Octree <bool> smallerCluster = isZeroSmaller ? foundClusters[0] : foundClusters[1];
                Octree <bool> biggerCluster  = isZeroSmaller ? foundClusters[1] : foundClusters[0];

                Assert.AreEqual(relXIndex, smallerCluster.Dimensions.Get(relativeX));
                Assert.AreEqual(height, smallerCluster.Dimensions.Get(relativeY));
                Assert.AreEqual(depth, smallerCluster.Dimensions.Get(relativeZ));

                Assert.AreEqual(width - 1 - relXIndex, biggerCluster.Dimensions.Get(relativeX));
                Assert.AreEqual(height, biggerCluster.Dimensions.Get(relativeY));
                Assert.AreEqual(depth, biggerCluster.Dimensions.Get(relativeZ));
            }
        }
    }
    private static IEnumerator TestDiagonalOnAxis(Vector3Int offset, Axis relativeZ)
    {
        Utils.GetOtherAxes(relativeZ, out Axis relativeX, out Axis relativeY);

        Vector3Int dimensions = new Vector3Int(6, 6, 6);
        int        width      = dimensions.Get(relativeX);
        int        height     = dimensions.Get(relativeY);
        int        depth      = dimensions.Get(relativeZ);

        int relXMid = width / 2;
        int relYMid = height / 2;

        Octree <bool> clusterToManipulateNext = null;

        {
            yield return(TestFindClusters(offset, dimensions, ManipulateVoxels, ValidateResults));

            void ManipulateVoxels(Octree <bool> voxelMap, Queue <Vector3Int> dirtyVoxels)
            {
                for (int relYIndex = 0; relYIndex < height; relYIndex++)
                {
                    for (int relXIndex = 0; relXIndex < width; relXIndex++)
                    {
                        if (relXIndex < relXMid || relYIndex >= relYMid)
                        {
                            continue;
                        }

                        FireBeam(relXIndex, relYIndex, relativeX, relativeY, relativeZ, voxelMap, dirtyVoxels);
                    }
                }
            }

            void ValidateResults(List <Octree <bool> > foundClusters)
            {
                Assert.AreEqual(1, foundClusters.Count);

                Octree <bool> cluster = foundClusters[0];

                Assert.AreEqual(width, cluster.Dimensions.Get(relativeX));
                Assert.AreEqual(height, cluster.Dimensions.Get(relativeY));
                Assert.AreEqual(depth, cluster.Dimensions.Get(relativeZ));

                for (int relZIndex = 0; relZIndex < depth; relZIndex++)
                {
                    for (int relYIndex = 0; relYIndex < height; relYIndex++)
                    {
                        for (int relXIndex = 0; relXIndex < width; relXIndex++)
                        {
                            if (relXIndex >= relXMid && relYIndex < relYMid)
                            {
                                continue;
                            }

                            Vector3Int pos = new Vector3Int();
                            pos = pos.Set(relativeX, relXIndex);
                            pos = pos.Set(relativeY, relYIndex);
                            pos = pos.Set(relativeZ, relZIndex);

                            Assert.IsTrue(cluster.TryGetValue(pos, out bool value));
                            Assert.IsTrue(value);
                        }
                    }
                }

                clusterToManipulateNext = cluster;
            }
        }

        Assert.NotNull(clusterToManipulateNext);

        {
            yield return(TestFindClusters(clusterToManipulateNext, ManipulateVoxels, ValidateResults));

            void ManipulateVoxels(Octree <bool> voxelMap, Queue <Vector3Int> dirtyVoxels)
            {
                for (int relYIndex = 0; relYIndex < height; relYIndex++)
                {
                    for (int relXIndex = 0; relXIndex < width; relXIndex++)
                    {
                        if (relXIndex >= relXMid || relYIndex < relYMid)
                        {
                            continue;
                        }

                        FireBeam(relXIndex, relYIndex, relativeX, relativeY, relativeZ, voxelMap, dirtyVoxels);
                    }
                }
            }

            void ValidateResults(List <Octree <bool> > foundClusters)
            {
                Assert.AreEqual(2, foundClusters.Count);

                Octree <bool> cluster_0 = foundClusters[0];

                Assert.AreEqual(relXMid, cluster_0.Dimensions.Get(relativeX));
                Assert.AreEqual(relYMid, cluster_0.Dimensions.Get(relativeY));
                Assert.AreEqual(depth, cluster_0.Dimensions.Get(relativeZ));

                for (int z = 0; z < cluster_0.Dimensions.z; z++)
                {
                    for (int y = 0; y < cluster_0.Dimensions.y; y++)
                    {
                        for (int x = 0; x < cluster_0.Dimensions.x; x++)
                        {
                            Assert.IsTrue(cluster_0.TryGetValue(new Vector3Int(x, y, z), out bool value));
                            Assert.IsTrue(value);
                        }
                    }
                }

                Octree <bool> cluster_1 = foundClusters[1];

                Assert.AreEqual(relXMid, cluster_1.Dimensions.Get(relativeX));
                Assert.AreEqual(relYMid, cluster_1.Dimensions.Get(relativeY));
                Assert.AreEqual(depth, cluster_1.Dimensions.Get(relativeZ));

                for (int z = 0; z < cluster_1.Dimensions.z; z++)
                {
                    for (int y = 0; y < cluster_1.Dimensions.y; y++)
                    {
                        for (int x = 0; x < cluster_1.Dimensions.x; x++)
                        {
                            Assert.IsTrue(cluster_1.TryGetValue(new Vector3Int(x, y, z), out bool value));
                            Assert.IsTrue(value);
                        }
                    }
                }
            }
        }
    }
    private static IEnumerator TestDisappearingVoxelBug(Axis relativeZ)
    {
        Utils.GetOtherAxes(relativeZ, out Axis relativeX, out Axis relativeY);

        Vector3Int dimensions = new Vector3Int(4, 8, 4);
        int        width      = dimensions.Get(relativeX);
        int        height     = dimensions.Get(relativeY);
        int        depth      = dimensions.Get(relativeZ);


        Octree <bool> clusterToManipulateNext = null;

        {
            yield return(TestFindClusters(Vector3Int.zero, dimensions, ManipulateVoxels, ValidateResults));

            void ManipulateVoxels(Octree <bool> voxelMap, Queue <Vector3Int> dirtyVoxels)
            {
                FireBeam(0, 2, relativeX, relativeY, relativeZ, voxelMap, dirtyVoxels);
                FireBeam(1, 2, relativeX, relativeY, relativeZ, voxelMap, dirtyVoxels);
                FireBeam(2, 1, relativeX, relativeY, relativeZ, voxelMap, dirtyVoxels);
                FireBeam(3, 1, relativeX, relativeY, relativeZ, voxelMap, dirtyVoxels);
            }

            void ValidateResults(List <Octree <bool> > foundClusters)
            {
                Assert.AreEqual(2, foundClusters.Count);

                Octree <bool> cluster_0 = foundClusters[0];

                Assert.AreEqual(width, cluster_0.Dimensions.Get(relativeX));
                Assert.AreEqual(6, cluster_0.Dimensions.Get(relativeY));
                Assert.AreEqual(depth, cluster_0.Dimensions.Get(relativeZ));

                Octree <bool> cluster_1 = foundClusters[1];

                Assert.AreEqual(width, cluster_1.Dimensions.Get(relativeX));
                Assert.AreEqual(2, cluster_1.Dimensions.Get(relativeY));
                Assert.AreEqual(depth, cluster_1.Dimensions.Get(relativeZ));

                clusterToManipulateNext = cluster_0;
            }
        }

        Assert.NotNull(clusterToManipulateNext);

        {
            yield return(TestFindClusters(clusterToManipulateNext, ManipulateVoxels, ValidateResults));

            void ManipulateVoxels(Octree <bool> voxelMap, Queue <Vector3Int> dirtyVoxels)
            {
                FireBeam(2, 1, relativeX, relativeY, relativeZ, voxelMap, dirtyVoxels);
            }

            void ValidateResults(List <Octree <bool> > foundClusters)
            {
                Assert.AreEqual(1, foundClusters.Count);

                Octree <bool> cluster = foundClusters[0];

                Assert.AreEqual(width, cluster.Dimensions.Get(relativeX));
                Assert.AreEqual(6, cluster.Dimensions.Get(relativeY));
                Assert.AreEqual(depth, cluster.Dimensions.Get(relativeZ));

                for (int z = 0; z < 4; z++)
                {
                    for (int y = 0; y < 6; y++)
                    {
                        for (int x = 0; x < 4; x++)
                        {
                            if (x < 2 && y == 0 || x == 2 && y == 1)
                            {
                                Assert.IsTrue(cluster.TryGetValue(new Vector3Int(x, y, z), out bool value));
                                Assert.IsFalse(value);
                            }
                            else
                            {
                                Assert.IsTrue(cluster.TryGetValue(new Vector3Int(x, y, z), out bool value));
                                Assert.IsTrue(value);
                            }
                        }
                    }
                }
            }
        }
    }
    private static IEnumerator TestDoubleSplitOnAxis(Vector3Int offset, Axis relativeZ, int relXIndex, int relYIndex)
    {
        Utils.GetOtherAxes(relativeZ, out Axis relativeX, out Axis relativeY);

        Vector3Int dimensions = new Vector3Int(4, 4, 4);
        int        width      = dimensions.Get(relativeX);
        int        height     = dimensions.Get(relativeY);
        int        depth      = dimensions.Get(relativeZ);


        if (relXIndex == 0 || relYIndex == 0 || relXIndex == width - 1 || relYIndex == height - 1)
        {
            throw new NotImplementedException();
        }

        Octree <bool> clusterToManipulateNext = null;

        {
            yield return(TestFindClusters(offset, dimensions, ManipulateVoxels, ValidateResults));

            void ManipulateVoxels(Octree <bool> voxelMap, Queue <Vector3Int> dirtyVoxels)
            {
                for (int i = 0; i < height; i++)
                {
                    FireBeam(relXIndex, i, relativeX, relativeY, relativeZ, voxelMap, dirtyVoxels);
                }
            }

            void ValidateResults(List <Octree <bool> > foundClusters)
            {
                Assert.AreEqual(2, foundClusters.Count);

                Octree <bool> cluster_0 = foundClusters[0];

                Assert.AreEqual(width - 1 - relXIndex, cluster_0.Dimensions.Get(relativeX));
                Assert.AreEqual(height, cluster_0.Dimensions.Get(relativeY));
                Assert.AreEqual(depth, cluster_0.Dimensions.Get(relativeZ));

                Octree <bool> cluster_1 = foundClusters[1];

                Assert.AreEqual(relXIndex, cluster_1.Dimensions.Get(relativeX));
                Assert.AreEqual(height, cluster_1.Dimensions.Get(relativeY));
                Assert.AreEqual(depth, cluster_1.Dimensions.Get(relativeZ));

                clusterToManipulateNext = cluster_0.Dimensions.Get(relativeX) < cluster_1.Dimensions.Get(relativeX) ? cluster_0 : cluster_1;
            }
        }

        {
            yield return(TestFindClusters(clusterToManipulateNext, ManipulateVoxels, ValidateResults));

            void ManipulateVoxels(Octree <bool> voxelMap, Queue <Vector3Int> dirtyVoxels)
            {
                for (int i = 0; i < height; i++)
                {
                    FireBeam(i, relYIndex, relativeX, relativeY, relativeZ, voxelMap, dirtyVoxels);
                }
            }

            void ValidateResults(List <Octree <bool> > foundClusters)
            {
                Assert.AreEqual(2, foundClusters.Count);

                bool          isZeroSmaller = foundClusters[0].Dimensions.Get(relativeY) < foundClusters[1].Dimensions.Get(relativeY);
                Octree <bool> smallCluster  = isZeroSmaller ? foundClusters[0] : foundClusters[1];
                Octree <bool> largeCluster  = isZeroSmaller ? foundClusters[1] : foundClusters[0];

                int expectedWidth = relXIndex < width / 2 ? relXIndex : width - 1 - relXIndex;

                Assert.AreEqual(expectedWidth, smallCluster.Dimensions.Get(relativeX));
                Assert.AreEqual(1, smallCluster.Dimensions.Get(relativeY));
                Assert.AreEqual(4, smallCluster.Dimensions.Get(relativeZ));

                Assert.AreEqual(expectedWidth, largeCluster.Dimensions.Get(relativeX));
                Assert.AreEqual(2, largeCluster.Dimensions.Get(relativeY));
                Assert.AreEqual(4, largeCluster.Dimensions.Get(relativeZ));
            }
        }
    }