/// <summary>
 /// Add a new child to this node at the given
 /// </summary>
 /// <param name="octant">the octant position to add the child node to</param>
 /// <param name="newValue">the value of the new child node</param>
 /// <returns></returns>
 OctreeNode <TType> addChild(Octants.Octant octant, TType newValue)
 {
     children[octant.Value] = new OctreeNode <TType>(
         getChildOctantCoordinate(octant),
         size / 2,
         newValue,
         this
         );
     return(children[octant.Value]);
 }
        /// <summary>
        /// Set this block's vertex 'is solid' flag for the given octant
        /// </summary>
        /// <param name="value"></param>
        /// <param name="octant"></param>
        /// <param name="toTrue"></param>
        /// <returns></returns>
        public static int SetVertexMaskForOctant(this int value, Octants.Octant octant, bool toTrue = true)
        {
            int mask = 1 << (octant.Value + 8);

            if (toTrue)
            {
                return(value | mask);
            }

            return(value & ~mask);
        }
 /// <summary>
 /// Get if this block's given vertex is within the isosurface and is 'solid'
 /// </summary>
 /// <param name="value"></param>
 /// <param name="octant"></param>
 /// <returns>If the vertex's value is solid</returns>
 public static bool BlockVertexIsSolid(this int value, Octants.Octant octant)
 {
     // Get the mask of neighboring block data from the int
     return((
                // trim the neighbor's mask off of the full block data int value
                ((byte)(value >> 8))
                // create a mask (ex: 000100) with the 1 in the spot = to the value of the
                // direction we want to test for, and & compare them.
                & (1 << (octant.Value))
                // if it is not = 000000 after the and, the bit is set.
                ) != 0);
 }
            /// <summary>
            /// Get the position of the 0,0 of the requested child octant, given this octants size and position
            /// </summary>
            /// <param name="childOctant"></param>
            /// <returns></returns>
            Coordinate getChildOctantCoordinate(Octants.Octant childOctant)
            {
                int        childOctantSize = size / 2;
                Coordinate childPosition   = position;

                if (childOctant.IsEastern)
                {
                    childPosition.x += childOctantSize;
                }
                if (childOctant.IsNorthern)
                {
                    childPosition.z += childOctantSize;
                }
                if (childOctant.IsUpper)
                {
                    childPosition.y += childOctantSize;
                }
                return(childPosition);
            }
            /// <summary>
            /// Set the value of the node given the position.
            /// </summary>
            /// <param name="nodePosition">the position of the node to edit, the target</param>
            /// <param name="newValue">the new value to set at the positon</param>
            /// <param name="nodeSize">if you want to set an area of the tree at the node level, for edge cases. must be a valid node size for this octeee to work</param>
            public void setValueAt(Coordinate nodePosition, TType newValue, int nodeSize = 1)
            {
                // if we're at the correct size, set this and all below.
                if (size == nodeSize)
                {
                    value = newValue;
                    clearChildren();
                    return;
                }
                OctreeNode <TType> childNode;

                // if this node is a solid chunk of the wrong size split it, create the first node below, and recurse the set on it
                if (isSolid)
                {
                    // if this is solid, and the position is somewhere within, we can stop if the whole this is already the right value
                    if (value.Equals(newValue))
                    {
                        return;
                    }
                    childNode = splitAndAddChildNode(
                        getChildOctantFor(nodePosition),
                        value
                        );
                    // if this node is a broken chunk of the wrong size, grab/add the child node , then recurse it.
                }
                else
                {
                    Octants.Octant childOctant = getChildOctantFor(nodePosition);
                    childNode = getChild(childOctant);
                    if (childNode == null)
                    {
                        childNode = addChild(childOctant, value);
                    }
                }
                childNode.setValueAt(nodePosition, newValue, nodeSize);
                // cleanup after recursion, when we come up a level, check to see if this node's children are all the same, and solidify it if they are.
                tryToSolidify();
            }
 /// <summary>
 /// Get the uncle octant to the given direction of the current octant
 /// (uncle being an octant bordeing the parent sharing a parent.)
 /// </summary>
 /// <param name="direction"></param>
 /// <param name="uncleValue">Get the value of the uncle, can be used in case no node is returned.</param>
 /// <returns></returns>
 OctreeNode <TType> getUncleToThe(Directions.Direction direction, out TType uncleValue)
 {
     // we want to make sure we aren't too far up for uncles
     if (!isRoot && !parent.isRoot)
     {
         Octants.Octant currentParentOctant = parent.parent.getChildOctantFor(parent.position);
         Octants.Octant uncleOctant         = currentParentOctant.toThe(direction);
         // if we have an uncle at this level, return the node and or value from the grandparent
         if (uncleOctant != null)
         {
             OctreeNode <TType> uncleNode = parent.parent.getChild((Octants.Octant)uncleOctant);
             uncleValue = uncleNode == null ? parent.parent.value : uncleNode.value;
             return(uncleNode);
             // if we don't, we want to see if this node's parent has the uncle we need.
         }
         else
         {
             return(parent.getUncleToThe(direction, out uncleValue));
         }
     }
     // there's no uncles, only brothers and parents.
     uncleValue = default;
     return(null);
 }
            /// <summary>
            /// Check if the neigboring node(s) in the given direction is(are) solid against this node.
            /// </summary>
            /// <param name="direction"></param>
            /// <returns></returns>
            TType[] getValuesToThe(Directions.Direction direction)
            {
                if (isRoot)
                {
                    return(new TType[1]);
                }
                TType defaultValue;

                Octants.Octant currentOctant = parent.getChildOctantFor(position);
                Octants.Octant brotherOctant = currentOctant.toThe(direction);
                // if the octant shares a parent, move downwards into the found brother
                OctreeNode <TType> neighborNode;

                if (brotherOctant != null)
                {
                    neighborNode = parent.getChild(brotherOctant);
                    defaultValue = parent.value;
                    // else we have to get the cousin
                }
                else
                {
                    neighborNode = getCousinToThe(direction, out defaultValue);
                }
                // if we found a node at all, collect the values of that node.
                if (neighborNode != null)
                {
                    return(collectBorderValues(neighborNode, direction.Reverse));
                    // if there is no neighboring node, return the value we generated for the position instead
                }
                else
                {
                    return(new TType[1] {
                        defaultValue
                    });
                }
            }
 /// <summary>
 /// Split this node up and set just one octant to a new value
 /// </summary>
 /// <param name="octant"></param>
 /// <param name="newValue"></param>
 OctreeNode <TType> splitAndAddChildNode(Octants.Octant updatedOctant, TType newValue)
 {
     children = new OctreeNode <TType> [8];
     return(addChild(updatedOctant, newValue));
 }
 /// <summary>
 /// Get the child by octant
 /// </summary>
 /// <param name="octant"></param>
 /// <returns></returns>
 OctreeNode <TType> getChild(Octants.Octant octant)
 {
     return(isSolid ? null : children[octant.Value]);
 }
 /// <summary>
 /// Helper function to update just the vertex mask of a given block
 /// </summary>
 /// <param name="location">the x,y,z of the block to set</param>
 /// <param name="vertex">the vertex to toggle</param>
 /// <param name="setSolid">whether to set the vertex flag to solid or empty</param>
 protected void updateBlockVertexMask(Coordinate location, Octants.Octant vertex, bool setSolid = true)
 {
     setBlock(location, getBlock(location).SetVertexMaskForOctant(vertex, setSolid));
 }