added methods for expanding trees to include points outside current bounds

This commit is contained in:
BuildTools 2017-02-22 17:53:44 -05:00
parent ba8a2eee0a
commit 5ece1f35eb
3 changed files with 802 additions and 93 deletions

View file

@ -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<Octree> newEditor()
public boolean contains(int... coords)
{
return new TreeEditor<Octree>(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;
}
}

View file

@ -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<Quadtree> newEditor()
public boolean contains(int... coords)
{
return new TreeEditor<Quadtree>(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;
}
}

View file

@ -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.<p>
* 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."<p>
*
* 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."<p>
* Octree application can be visualized like this:<p>
*
* The idea works like this:<p>
* 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.<p>
*
* 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.<p>
* 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.<p>
*
* 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
* (<tt>true</tt>) and completely empty (<tt>false</tt>) cubes remain.<p>
*
* 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
* <tt>{@link #getByte(Node, Node, Node, Node) getByte(children)}</tt> for each encountered
* node in the tree, skipping childless nodes.<p>
*
* Assumes an OutputStream that appends with each write.
* Appends to the OutputStream the result of invoking <tt>{@link #getByte(Node, Node, Node, Node)
* getByte(children)}</tt> 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<? extends Tree> 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 <tt>true</tt> 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<? extends Tree> 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);
}