diff --git a/src/regions/Octree.java b/src/regions/Octree.java index 6fdb31b..3d07cef 100644 --- a/src/regions/Octree.java +++ b/src/regions/Octree.java @@ -4,6 +4,9 @@ import java.io.DataInputStream; import java.io.File; import java.io.IOException; import java.io.OutputStream; +import java.util.BitSet; + +import regions.Tree.Node; public class Octree extends Tree { @@ -25,16 +28,16 @@ public class Octree extends Tree return a == -1 || b == -1 ? new Node( false ) : - new Node( new Node[] { parseBytes(input, (a >>> 6 & 3)), - parseBytes(input, (a >>> 4 & 3)), - parseBytes(input, (a >>> 2 & 3)), - parseBytes(input, (a & 3)), - - parseBytes(input, (b >>> 6 & 3)), - parseBytes(input, (b >>> 4 & 3)), - parseBytes(input, (b >>> 2 & 3)), - parseBytes(input, (b & 3)) - }); + new Node( parseBytes(input, (a >>> 6 & 3)), + parseBytes(input, (a >>> 4 & 3)), + parseBytes(input, (a >>> 2 & 3)), + parseBytes(input, (a & 3)), + + parseBytes(input, (b >>> 6 & 3)), + parseBytes(input, (b >>> 4 & 3)), + parseBytes(input, (b >>> 2 & 3)), + parseBytes(input, (b & 3)) + ); } /*---------------------------------------------------------------------------- @@ -46,17 +49,17 @@ public class Octree extends Tree @Override public void writeBytes(Node node, OutputStream output) throws IOException { - output.write( getByte( node.children[0], - node.children[1], - node.children[2], - node.children[3] - )); + output.write( getByte( node.children[0], + node.children[1], + node.children[2], + node.children[3] + )); - output.write( getByte( node.children[4], - node.children[5], - node.children[6], - node.children[7] - )); + output.write( getByte( node.children[4], + node.children[5], + node.children[6], + node.children[7] + )); for (Node child : node.children) if (child.children.length > 0) @@ -73,29 +76,338 @@ public class Octree extends Tree { super(file); } + + + /* + ╔══════════════════════════════════════════════════════════════════════════════════════════════╗ + ║ ╔══════════════════════════════════════════════════════════════════════════════════════════╗ ║ + ║ ║ ║ ║ + ║ ║ EVALUATE ║ ║ + ║ ║ ║ ║ + ║ ╚══════════════════════════════════════════════════════════════════════════════════════════╝ ║ + ╚══════════════════════════════════════════════════════════════════════════════════════════════╝ */ - /*------------------------------------- - Methods used by constructors. - -------------------------------------*/ - @Override - protected final TreeEditor newEditor() + public boolean contains(int... coords) { - return new TreeEditor(this, min[0], min[1], min[2]); + } + /* + ╔══════════════════════════════════════════════════════════════════════════════════════════════╗ + ║ ╔══════════════════════════════════════════════════════════════════════════════════════════╗ ║ + ║ ║ ║ ║ + ║ ║ ADD VOLUME ║ ║ + ║ ║ ║ ║ + ║ ╚══════════════════════════════════════════════════════════════════════════════════════════╝ ║ + ╚══════════════════════════════════════════════════════════════════════════════════════════════╝ */ + + /*---------------------------------------------------------------------------- + ------------------------------------------------------------------------------ + expand() + ------------------------------------------------------------------------------ + ----------------------------------------------------------------------------*/ + /** + * + * @param xMinExpansion + * @param zMinExpansion + * @param yMinExpansion + * @param xMaxExpansion + * @param zMaxExpansion + * @param yMaxExpansion + */ + protected void expand(double xMinExpansion, double zMinExpansion, double yMinExpansion, + double xMaxExpansion, double zMaxExpansion, double yMaxExpansion + ) + { + int xMinRounded = (int) Math.ceil(xMinExpansion), + zMinRounded = (int) Math.ceil(zMinExpansion), + yMinRounded = (int) Math.ceil(yMinExpansion), + xMaxRounded = (int) Math.ceil(xMaxExpansion), + zMaxRounded = (int) Math.ceil(zMaxExpansion), + yMaxRounded = (int) Math.ceil(yMaxExpansion), + + size = nextPowerOfTwo( xMinRounded + xMaxRounded + 1, + zMinRounded + zMaxRounded + 1, + yMinRounded + yMaxRounded + 1 + ), + + xMargin = size - (xMinRounded + xMaxRounded + 1), + zMargin = size - (zMinRounded + zMaxRounded + 1), + yMargin = size - (yMinRounded + yMaxRounded + 1), + + xMarginHalf = xMargin / 2, + zMarginHalf = zMargin / 2, + yMarginHalf = yMargin / 2; + + + + xMinRounded += xMarginHalf; + zMinRounded += zMarginHalf; + yMinRounded += yMarginHalf; + xMaxRounded += xMarginHalf; + zMaxRounded += zMarginHalf; + yMaxRounded += yMarginHalf; + + /* if margin is odd, add 1 + * to the more close-fitting side + */ + if (xMargin % 2 == 1) + if (xMinRounded - xMinExpansion > xMaxRounded - xMaxExpansion) xMinRounded++; else xMaxRounded++; + + if (zMargin % 2 == 1) + if (zMinRounded - zMinExpansion > zMaxRounded - zMaxExpansion) zMinRounded++; else zMaxRounded++; + + if (yMargin % 2 == 1) + if (yMinRounded - yMinExpansion > yMaxRounded - yMaxExpansion) yMinRounded++; else yMaxRounded++; + + + + int index; + Node[] children; + Node[] newRootChildren = children = Node.emptyNodeArray(8); + + while(true) + { + size >>>= 1; + index = 0; + + if (xMinRounded >= size) + xMinRounded -= size; + else + { + xMaxRounded -= size; + index += 1; + } + if (zMinRounded >= size) + zMinRounded -= size; + else + { + zMaxRounded -= size; + index += 2; + } + if (yMinRounded >= size) + yMinRounded -= size; + else + { + yMaxRounded -= size; + index += 4; + } + + if (size > 1) + children[index].children = children = Node.emptyNodeArray(8); + else + { + children[index].children = root.children; + break; + } + } + root.children = newRootChildren; + } + + + /*---------------------------------------------------------------------------- ------------------------------------------------------------------------------ - CALCULATIONS + expandAsNeeded() ------------------------------------------------------------------------------ ----------------------------------------------------------------------------*/ + @Override - public Node[] getNodes(Node parentNode, int[][] regionBounds) + protected void expandAsNeeded(int... coords) + { + int sideLength = max[0] - min[0] + 1; + + double xMinExpansion = 0, + zMinExpansion = 0, + yMinExpansion = 0, + xMaxExpansion = 0, + zMaxExpansion = 0, + yMaxExpansion = 0; + + if (coords[0] < min[0]) xMinExpansion = (min[0] - coords[0]) / sideLength; + else if (coords[0] > max[0]) xMaxExpansion = (coords[0] - max[0]) / sideLength; + + if (coords[1] < min[1]) xMinExpansion = (min[1] - coords[1]) / sideLength; + else if (coords[1] > max[1]) xMaxExpansion = (coords[1] - max[1]) / sideLength; + + if (coords[2] < min[2]) xMinExpansion = (min[2] - coords[2]) / sideLength; + else if (coords[2] > max[2]) xMaxExpansion = (coords[2] - max[2]) / sideLength; + + if (xMinExpansion != 0 || + zMinExpansion != 0 || + yMinExpansion != 0 || + xMaxExpansion != 0 || + zMaxExpansion != 0 || + yMaxExpansion != 0 + ) + expand(xMinExpansion, + zMinExpansion, + yMinExpansion, + xMaxExpansion, + zMaxExpansion, + yMaxExpansion); + } + + + @Override + public void expandAsNeeded(int[]... bounds) + { + int sideLength = max[0] - min[0] + 1; + + double xMinExpansion = 0, + zMinExpansion = 0, + yMinExpansion = 0, + xMaxExpansion = 0, + zMaxExpansion = 0, + yMaxExpansion = 0; + + if (bounds[0][0] < min[0]) xMinExpansion = (min[0] - bounds[0][0]) / sideLength; + if (bounds[0][1] > max[0]) xMaxExpansion = (bounds[0][1] - max[0]) / sideLength; + + if (bounds[1][0] < min[1]) xMinExpansion = (min[1] - bounds[1][0]) / sideLength; + if (bounds[1][1] > max[1]) xMaxExpansion = (bounds[1][1] - max[1]) / sideLength; + + if (bounds[2][0] < min[2]) xMinExpansion = (min[2] - bounds[2][0]) / sideLength; + if (bounds[2][1] > max[2]) xMaxExpansion = (bounds[2][1] - max[2]) / sideLength; + + if (xMinExpansion != 0 || + zMinExpansion != 0 || + yMinExpansion != 0 || + xMaxExpansion != 0 || + zMaxExpansion != 0 || + yMaxExpansion != 0 + ) + expand(xMinExpansion, + zMinExpansion, + yMinExpansion, + xMaxExpansion, + zMaxExpansion, + yMaxExpansion); + } + + + + /*---------------------------------------------------------------------------- + ------------------------------------------------------------------------------ + add() + ------------------------------------------------------------------------------ + ----------------------------------------------------------------------------*/ + + + @Override + public void add(int... coords) + { +expandAsNeeded(coords); + + Node node = root; + + int[] min = this.min; + int[] max = this.max; + + int size = max[0] - min[0] + 1; + int half = size / 2; + int index = 0; + + outerloop: + while (true) + { + if (node.full) return; + if (node.children.length == 0) node.children = Node.emptyNodeArray(8); + + if ((min[0] + half) > coords[0]) + max[0] -= half; + else + { + min[0] += half; + index += 1; + } + if ((min[1] + half) > coords[1]) + max[1] -= half; + else + { + min[1] += half; + index += 2; + } + if ((min[2] + half) > coords[2]) + max[2] -= half; + else + { + min[2] += half; + index += 4; + } + + if ((size >>>= 1) > 1) + { + node = node.children[index]; + half = size / 2; + } + else + { + node.children[index].full = true; + + for (Node child : node.children) + { + if (!child.full) + { + break outerloop; + } + } + node.full = true; + node.children = new Node[0]; + } + } + } + + @Override + public void add(int[]... bounds) + { + expandAsNeeded(bounds); + } + + @Override + public void add(BitSet blocks, int[]... bounds) + { + expandAsNeeded(bounds); + } + + + + /* + ╔══════════════════════════════════════════════════════════════════════════════════════════════╗ + ║ ╔══════════════════════════════════════════════════════════════════════════════════════════╗ ║ + ║ ║ ║ ║ + ║ ║ REMOVE VOLUME ║ ║ + ║ ║ ║ ║ + ║ ╚══════════════════════════════════════════════════════════════════════════════════════════╝ ║ + ╚══════════════════════════════════════════════════════════════════════════════════════════════╝ */ + + + @Override + public void trimAsNeeded() + { + // TODO Auto-generated method stub + } + + + @Override + public void remove(int... coords) + { + // TODO Auto-generated method stub + } + + @Override + public void remove(int[]... bounds) + { + // TODO Auto-generated method stub + } + + @Override + public void remove(BitSet blocks, int[]... bounds) { // TODO Auto-generated method stub - return null; } } diff --git a/src/regions/Quadtree.java b/src/regions/Quadtree.java index 9ea2d1a..e6f6f38 100644 --- a/src/regions/Quadtree.java +++ b/src/regions/Quadtree.java @@ -4,6 +4,7 @@ import java.io.DataInputStream; import java.io.File; import java.io.IOException; import java.io.OutputStream; +import java.util.BitSet; public class Quadtree extends Tree { @@ -24,11 +25,11 @@ public class Quadtree extends Tree return a == -1 ? new Node( false ) : - new Node( new Node[] { parseBytes(input, (a >>> 6 & 3)), - parseBytes(input, (a >>> 4 & 3)), - parseBytes(input, (a >>> 2 & 3)), - parseBytes(input, (a & 3)) - }); + new Node( parseBytes(input, (a >>> 6 & 3)), + parseBytes(input, (a >>> 4 & 3)), + parseBytes(input, (a >>> 2 & 3)), + parseBytes(input, (a & 3)) + ); } /*---------------------------------------------------------------------------- @@ -40,11 +41,11 @@ public class Quadtree extends Tree @Override public void writeBytes(Node node, OutputStream output) throws IOException { - output.write( getByte( node.children[0], - node.children[1], - node.children[2], - node.children[3] - )); + output.write( getByte( node.children[0], + node.children[1], + node.children[2], + node.children[3] + )); for (Node child : node.children) if (child.children.length > 0) @@ -64,27 +65,287 @@ public class Quadtree extends Tree } - /*------------------------------------- - Methods used by constructors. - -------------------------------------*/ + /* + ╔══════════════════════════════════════════════════════════════════════════════════════════════╗ + ║ ╔══════════════════════════════════════════════════════════════════════════════════════════╗ ║ + ║ ║ ║ ║ + ║ ║ EVALUATE ║ ║ + ║ ║ ║ ║ + ║ ╚══════════════════════════════════════════════════════════════════════════════════════════╝ ║ + ╚══════════════════════════════════════════════════════════════════════════════════════════════╝ */ + @Override - protected final TreeEditor newEditor() + public boolean contains(int... coords) { - return new TreeEditor(this, min[0], min[1]); + } + /* + ╔══════════════════════════════════════════════════════════════════════════════════════════════╗ + ║ ╔══════════════════════════════════════════════════════════════════════════════════════════╗ ║ + ║ ║ ║ ║ + ║ ║ ADD VOLUME ║ ║ + ║ ║ ║ ║ + ║ ╚══════════════════════════════════════════════════════════════════════════════════════════╝ ║ + ╚══════════════════════════════════════════════════════════════════════════════════════════════╝ */ + + /*---------------------------------------------------------------------------- + ------------------------------------------------------------------------------ + expand() + ------------------------------------------------------------------------------ + ----------------------------------------------------------------------------*/ + + /** + * + * @param xMinExpansion + * @param zMinExpansion + * @param xMaxExpansion + * @param zMaxExpansion + */ + protected void expand(double xMinExpansion, double zMinExpansion, double xMaxExpansion, double zMaxExpansion) + { + int xMinRounded = (int) Math.ceil(xMinExpansion), + zMinRounded = (int) Math.ceil(zMinExpansion), + xMaxRounded = (int) Math.ceil(xMaxExpansion), + zMaxRounded = (int) Math.ceil(zMaxExpansion), + + size = nextPowerOfTwo( xMinRounded + xMaxRounded + 1, + zMinRounded + zMaxRounded + 1 + ), + + xMargin = size - (xMinRounded + xMaxRounded + 1), + zMargin = size - (zMinRounded + zMaxRounded + 1), + + xMarginHalf = xMargin / 2, + zMarginHalf = zMargin / 2; + + + + xMinRounded += xMarginHalf; + zMinRounded += zMarginHalf; + xMaxRounded += xMarginHalf; + zMaxRounded += zMarginHalf; + + /* if margin is odd, add 1 + * to the more close-fitting side + */ + if (xMargin % 2 == 1) + if (xMinRounded - xMinExpansion > xMaxRounded - xMaxExpansion) xMinRounded++; else xMaxRounded++; + + if (zMargin % 2 == 1) + if (zMinRounded - zMinExpansion > zMaxRounded - zMaxExpansion) zMinRounded++; else zMaxRounded++; + + + + int index; + Node[] children; + Node[] newRootChildren = children = Node.emptyNodeArray(4); + + while(true) + { + size >>>= 1; + index = 0; + + if (xMinRounded >= size) + xMinRounded -= size; + else + { + xMaxRounded -= size; + index += 1; + } + if (zMinRounded >= size) + zMinRounded -= size; + else + { + zMaxRounded -= size; + index += 2; + } + + if (size > 1) + children[index].children = children = Node.emptyNodeArray(4); + else + { + children[index].children = root.children; + break; + } + } + root.children = newRootChildren; + } + + + /*---------------------------------------------------------------------------- ------------------------------------------------------------------------------ - CALCULATIONS + expandAsNeeded() ------------------------------------------------------------------------------ ----------------------------------------------------------------------------*/ + @Override - public Node[] getNodes(Node parentNode, int[][] regionBounds) + protected void expandAsNeeded(int... coords) + { + int sideLength = max[0] - min[0] + 1; + + double xMinExpansion = 0, + zMinExpansion = 0, + xMaxExpansion = 0, + zMaxExpansion = 0; + + if (coords[0] < min[0]) xMinExpansion = (min[0] - coords[0]) / sideLength; + else if (coords[0] > max[0]) xMaxExpansion = (coords[0] - max[0]) / sideLength; + + if (coords[1] < min[1]) xMinExpansion = (min[1] - coords[1]) / sideLength; + else if (coords[1] > max[1]) xMaxExpansion = (coords[1] - max[1]) / sideLength; + + if (xMinExpansion != 0 || + zMinExpansion != 0 || + xMaxExpansion != 0 || + zMaxExpansion != 0 + ) + expand(xMinExpansion, + zMinExpansion, + xMaxExpansion, + zMaxExpansion); + } + + + @Override + public void expandAsNeeded(int[]... bounds) + { + int sideLength = max[0] - min[0] + 1; + + double xMinExpansion = 0, + zMinExpansion = 0, + xMaxExpansion = 0, + zMaxExpansion = 0; + + if (bounds[0][0] < min[0]) xMinExpansion = (min[0] - bounds[0][0]) / sideLength; + if (bounds[0][1] > max[0]) xMaxExpansion = (bounds[0][1] - max[0]) / sideLength; + + if (bounds[1][0] < min[1]) xMinExpansion = (min[1] - bounds[1][0]) / sideLength; + if (bounds[1][1] > max[1]) xMaxExpansion = (bounds[1][1] - max[1]) / sideLength; + + if (xMinExpansion != 0 || + zMinExpansion != 0 || + xMaxExpansion != 0 || + zMaxExpansion != 0 + ) + expand(xMinExpansion, + zMinExpansion, + xMaxExpansion, + zMaxExpansion); + } + + + + /*---------------------------------------------------------------------------- + ------------------------------------------------------------------------------ + add() + ------------------------------------------------------------------------------ + ----------------------------------------------------------------------------*/ + + + @Override + public void add(int... coords) + { + expandAsNeeded(coords); + + Node node = root; + + int[] min = this.min; + int[] max = this.max; + + int size = max[0] - min[0] + 1; + int half = size / 2; + int index = 0; + + outerloop: + while (true) + { + if (node.full) return; + if (node.children.length == 0) node.children = Node.emptyNodeArray(4); + + if ((min[0] + half) > coords[0]) + max[0] -= half; + else + { + min[0] += half; + index += 1; + } + if ((min[1] + half) > coords[1]) + max[1] -= half; + else + { + min[1] += half; + index += 2; + } + + if ((size >>>= 1) > 1) + { + node = node.children[index]; + half = size / 2; + } + else + { + node.children[index].full = true; + + for (Node child : node.children) + { + if (!child.full) + { + break outerloop; + } + } + node.full = true; + node.children = new Node[0]; + } + } + } + + + @Override + public void add(int[]... bounds) + { + expandAsNeeded(bounds); + } + + @Override + public void add(BitSet blocks, int[]... bounds) + { + expandAsNeeded(bounds); + } + + + + /*------------------------------------- + REMOVE VOLUME + -------------------------------------*/ + + + @Override + public void trimAsNeeded() + { + // TODO Auto-generated method stub + } + + + @Override + public void remove(int... coords) + { + // TODO Auto-generated method stub + } + + @Override + public void remove(int[]... bounds) + { + // TODO Auto-generated method stub + } + + @Override + public void remove(BitSet blocks, int[]... bounds) { // TODO Auto-generated method stub - return null; } } diff --git a/src/regions/Tree.java b/src/regions/Tree.java index f997e28..68ce427 100644 --- a/src/regions/Tree.java +++ b/src/regions/Tree.java @@ -7,27 +7,23 @@ import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; +import java.util.BitSet; /** - * This is a superclass for octrees, quadtrees, and any other spatial trees.

+ * A superclass for octrees and quadtrees. Concept credit to Don Meagher, who first named and + * described Octrees in his 1980 paper, "Octree Encoding: A New Technique for the Representation, + * Manipulation and Display of Arbitrary 3-D Objects by Computer."

* - * concept credit to Don Meagher, who first named and described Octrees in his 1980 paper, - * "Octree Encoding: A New Technique for the Representation, Manipulation and Display of - * Arbitrary 3-D Objects by Computer."

+ * Octree application can be visualized like this:

* - * The idea works like this:

+ * For any arbitrary 3-D object, find the bounding cube of the shape (or the smallest cube it will + * fit inside of). Divide this bounding cube into 8 sub-cubes, or octants.

* - * For some arbitrary shape, you first define the bounding box of the shape (or the - * smallest box your shape will fit inside of). This box outlines the minimum and maximum - * x, y, and z dimensions of the shape.

+ * With the space thus divided, evaluate which octants the object fills, which it avoids, and which + * it intersects. Divide intersected octants again. Continue dividing until no partial octants remain, + * or until the desired resolution is reached.

* - * Next, you divide this cube evenly into 8 more cubes, determining which of these cubes - * your shape fills, which it leaves empty, and which it intersects. Intersected cubes are - * divided again into 8 more cubes, and the process repeats until only completely full - * (true) and completely empty (false) cubes remain.

- * - * With this tree, you can quickly determine whether any arbitrary point is inside or outside - * your shape by navigating the tree to reach the smallest cube containing your point. + * Useful for testing whether an arbitrary 3-D object contains any given point. * * @author Kevin Mathewson * @@ -47,11 +43,28 @@ public abstract class Tree this.full = full; this.children = new Node[0]; } - public Node(Node[] nodes) + public Node(Node... nodes) { this.full = false; this.children = nodes; } + + + /** + * Returns an array containing the given number of empty, childless nodes + * + * @param length desired size of array + */ + public static Node[] emptyNodeArray(int length) + { + Node[] array = new Node[length]; + + for (int i = 0; i < length; i++) + { + array[i] = new Node(false); + } + return array; + } } @@ -170,14 +183,12 @@ public abstract class Tree /** - * Parses the tree below this node, appending in depth-first order the result of invoking - * {@link #getByte(Node, Node, Node, Node) getByte(children)} for each encountered - * node in the tree, skipping childless nodes.

- * - * Assumes an OutputStream that appends with each write. + * Appends to the OutputStream the result of invoking {@link #getByte(Node, Node, Node, Node) + * getByte(children)} for each node, skipping childless nodes. Assumes an OutputStream that + * appends with each write. Traverses depth-first. * * @param node the node to be parsed - * @return a byte array representing the node and all its child nodes + * @param output the OutputStream to write to * @throws IOException */ public abstract void writeBytes(Node node, OutputStream output) throws IOException; @@ -266,7 +277,6 @@ public abstract class Tree public final File file; public final Node root; - public final TreeEditor editor; /** * Create a Tree from the given binary file. Invokes {@link #parseBytes(File)} @@ -280,7 +290,19 @@ public abstract class Tree this.file = file; this.root = parseBytes(file); - this.editor = newEditor(); + } + + /** + * + * + * @param file The source file, and save destination, for this Tree + * @param blocks BitSet representing all true points in the given volume + * @param bounds Min and max coordinates of the bounding box + */ + public Tree(File file, BitSet blocks, int[][] bounds) + { + this.file = file; + this.root = null; } @@ -298,21 +320,12 @@ public abstract class Tree * * @param file the source file to examine */ - private static void setBoundsFromFilename(File file) + private void setBoundsFromFilename(File file) { //TODO finish setBoundsFromFilename() method } - /** - * Abstract method, returns a new object - * extending the abstract class TreeEditor - * - * @return a new TreeEditor - */ - abstract TreeEditor newEditor(); - - /* ╔══════════════════════════════════════════════════════════════════════════════════════════════╗ @@ -323,23 +336,146 @@ public abstract class Tree ║ ╚══════════════════════════════════════════════════════════════════════════════════════════╝ ║ ╚══════════════════════════════════════════════════════════════════════════════════════════════╝ */ + /*---------------------------------------------------------------------------- + ------------------------------------------------------------------------------ + STATIC CALCULATIONS + ------------------------------------------------------------------------------ + ----------------------------------------------------------------------------*/ + /*------------------------------------- + OVERLOADS : nextPowerOfTwo() + -------------------------------------*/ /** * - * @param parentNode - * @param regionBounds + * @param a * @return */ - public abstract Node[] getNodes(Node parentNode, int[][] regionBounds); - - - /** - * - * @param regionBounds - * @return - */ - public Node[] getNodes(int[][] regionBounds) + public static int nextPowerOfTwo(int a) { - return getNodes(root, regionBounds); + return java.lang.Integer.highestOneBit(a) << 1; } + + /** + * + * @param a + * @param b + * @return + */ + public static int nextPowerOfTwo(int a, int b) + { + return java.lang.Integer.highestOneBit(Math.max(a, b)) << 1; + } + + /** + * + * @param a + * @param b + * @param c + * @return + */ + public static int nextPowerOfTwo(int a, int b, int c) + { + return java.lang.Integer.highestOneBit(Math.max(Math.max(a, b), c)) << 1; + } + + + + /*---------------------------------------------------------------------------- + ------------------------------------------------------------------------------ + EVALUATE + ------------------------------------------------------------------------------ + ----------------------------------------------------------------------------*/ + + + /** + * + * @param coords + * @return + */ + public abstract boolean contains(int... coords); + + + /*---------------------------------------------------------------------------- + ------------------------------------------------------------------------------ + ADD VOLUME + ------------------------------------------------------------------------------ + ----------------------------------------------------------------------------*/ + + + /** + * + * @param coordinates + */ + protected abstract void expandAsNeeded(int...coords); + + + /** + * + * @param bounds + */ + protected abstract void expandAsNeeded(int[]...bounds); + + + /** + * + * @param coordinates + * @return + */ + public abstract void add(int...coords); + + + /** + * + * @param bounds + * @return + */ + public abstract void add(int[]...bounds); + + + /** + * + * @param blocks + * @param bounds + * @return + */ + public abstract void add(BitSet blocks, int[]...bounds); + + + + /*---------------------------------------------------------------------------- + ------------------------------------------------------------------------------ + REMOVE VOLUME + ------------------------------------------------------------------------------ + ----------------------------------------------------------------------------*/ + + + /** + * + */ + public abstract void trimAsNeeded(); + + + /** + * + * @param coordinates + * @return + */ + public abstract void remove(int...coords); + + + /** + * + * @param bounds + * @return + */ + public abstract void remove(int[]...bounds); + + + /** + * + * @param blocks + * @param bounds + * @return + */ + public abstract void remove(BitSet blocks, int[]...bounds); }