我试图使用WSPD来实现3D分离良好的对分解octree的算法。
首先,我将实现类OctreeNode,如下所示
public class OctreeNode {
public static final int BSW = 0;
public static final int BSE = 1;
public static final int BNW = 2;
public static final int BNE = 3;
public static final int ASW = 4;
public static final int ASE = 5;
public static final int ANW = 6;
public static final int ANE = 7;
public int level = 0; // level set to 0.
public OctreeNode[] children = null;
public OctreeNode father = null; // father is null
public Point_3 p = null; //point stored in a leaf
public Point_3 center = null; // point to define the center of the box of the node.
public double height = 0.0, width = 0.0, depth = 0.0;
每个非叶节点都有8个子节点。我为该类实现了一个构造函数,该构造函数采用3D点列表。
/**
* Create the octree for storing an input point cloud.
*/
public OctreeNode(List<Point_3> points) {
// We construct the octree only for non empty point cloud.
assert(points.size() > 0);
/**
* We start by computing a box containing the point cloud.
*/
// The minimum value of x,y,z in the point cloud.
double minX = points.get(0).getX().doubleValue();
double minY = points.get(0).getY().doubleValue();
double minZ = points.get(0).getZ().doubleValue();
// The maximum value of x,y,z in the point cloud.
double maxX = points.get(0).getX().doubleValue();
double maxY = points.get(0).getY().doubleValue();
double maxZ = points.get(0).getZ().doubleValue();
for(Point_3 point : points) {
// update the minimum.
if(minX > point.getX().doubleValue()) {
minX = point.getX().doubleValue();
}
// update the minimum.
if(minY > point.getY().doubleValue()) {
minY = point.getY().doubleValue();
}
// update the minimum.
if(minZ > point.getZ().doubleValue()) {
minZ = point.getZ().doubleValue();
}
// update the maximum.
if(maxX < point.getX().doubleValue()) {
maxX = point.getX().doubleValue();
}
// update the maximum.
if(maxY < point.getY().doubleValue()) {
maxY = point.getY().doubleValue();
}
// update the maximum.
if(maxZ < point.getZ().doubleValue()) {
maxZ = point.getZ().doubleValue();
}
}
this.center = new Point_3((minX + maxX) / 2, (minY + maxY) / 2, (minZ + maxZ) / 2);
this.width = 0.75*(maxX - minX);
this.height = 0.75*(maxY - minY);
this.depth = 0.75*(maxZ - minZ);
for(Point_3 point : points) {
this.add(point);
}
}
之后,我实现类的添加功能
/**
* @return true if the current node is a leaf.
*/
public boolean isLeaf() {
return (this.children == null);
}
/**
* @return true if the current node is empty.
*/
public boolean isEmpty() {
return (this.children == null && this.p == null);
}
/**
* @return true if the current node is single point.
*/
public boolean isSinglePoint() {
return (this.children == null && this.p != null);
}
/**
* @param o an Object.
* @return true if the current node is equals to o
*/
@Override
public boolean equals(Object o) {
OctreeNode n = (OctreeNode) o;
return (n != null) && (this.center.equals(n.center));
}
/**
* @return the diameter of the node : 0 if is empty or single point and the diameter of the box otherwise.
*/
public double diam() {
if(this.isLeaf()) {
return 0.0;
}
return 2*Math.sqrt(this.width*this.width
+ this.height*this.height
+ this.depth*this.depth);
}
/**
* Check if the point is in the boundary of the OctreeNode
* @param p a Point_3.
* @return true if p is in the cube of the OctreeNode
*/
public boolean inBoundary(Point_3 p) {
Vector_3 v = (Vector_3) this.center.minus(p);
return (Math.abs(v.getX().doubleValue()) <= this.width && Math.abs(v.getY().doubleValue()) <= this.height && Math.abs(v.getZ().doubleValue()) <= this.depth);
}
/**
* Add a node into the OctreeNode
*/
public void add(Point_3 p) {
assert(this.center != null);
if(!this.inBoundary(p))
return;
// Check if the current node is a leaf.
if(this.isLeaf()) {
// Check if the current node is empty.
if(this.isEmpty()) {
this.p = p;
return;
}
else {
// Check if the node contains the same point already.
if(this.p.equals(p)) {
return;
}
// The current node contains only one point and the new point
// is different from the point of the current OctreeNode.
// We create the set of children for the current OctreeNode.
this.children = new OctreeNode[8];
// Initialize the children.
for(int i = 0; i < 8; i++) {
this.children[i] = new OctreeNode();
}
// For all children we put the current OctreeNode as father and
// we increment the level.
for(OctreeNode child : this.children) {
child.father = this;
child.level = this.level + 1;
}
// We compute then the center points for every child
Vector_3 vi = new Vector_3(this.width / 2, 0.0, 0.0);
Vector_3 vj = new Vector_3(0.0, this.height / 2, 0.0);
Vector_3 vk = new Vector_3(0.0, 0.0, this.depth / 2);
this.children[BSW].center = this.center.sum(vk.opposite()).sum(vj.opposite()).sum(vi.opposite());
this.children[BSE].center = this.center.sum(vk.opposite()).sum(vj.opposite()).sum(vi);
this.children[BNW].center = this.center.sum(vk.opposite()).sum(vj).sum(vi.opposite());
this.children[BNE].center = this.center.sum(vk.opposite()).sum(vj).sum(vi);
this.children[ASW].center = this.center.sum(vk).sum(vj.opposite()).sum(vi.opposite());
this.children[ASE].center = this.center.sum(vk).sum(vj.opposite()).sum(vi);
this.children[ANW].center = this.center.sum(vk).sum(vj).sum(vi.opposite());
this.children[ANE].center = this.center.sum(vk).sum(vj).sum(vi);
// We put half of the dimensions of the cube for every child.
for(OctreeNode child : children) {
child.width = this.width / 2;
child.depth = this.depth / 2;
child.height = this.height / 2;
}
// Look for the child to add the point of the current OctreeNode.
for(OctreeNode child : children) {
if(child.inBoundary(this.p)) {
child.add(this.p);
break;
}
}
// Look for the child to add the point p.
for(OctreeNode child : children) {
if(child.inBoundary(p)) {
child.add(p);
break;
}
}
// this.p = null;
}
}
else {
// Look for the child to add the point p.
for(OctreeNode child : children) {
if(child.inBoundary(p)) {
child.add(p);
break;
}
}
}
}
我还实现了一个函数距离,以计算Octree中两个节点之间的距离,即节点两个框之间的距离。
/**
* @param o an OctreeNode.
* @param u an OctreeNode.
* @return the distance between the two nodes.
*/
public static double distance(OctreeNode o, OctreeNode u) {
if(o == null || o.isEmpty() || u == null || u.isEmpty()) {
return 0.0;
}
if(u.isLeaf() && o.isLeaf()) {
return u.p.distanceFrom(o.p).doubleValue();
}
double x = 0.0;
double y = 0.0;
double z = 0.0;
Vector_3 v;
if(u.isLeaf()) {
v = (Vector_3) u.p.minus(o.center);
x = Math.min(Math.abs(v.getX().doubleValue()) - o.width, 0.0);
y = Math.min(Math.abs(v.getX().doubleValue()) - o.height, 0.0);
z = Math.min(Math.abs(v.getX().doubleValue()) - o.depth, 0.0);
return Math.sqrt(x*x + y*y + z*z);
}
if(o.isLeaf()) {
return distance(u, o);
}
v = (Vector_3) u.center.minus(o.center);
x = Math.min(Math.abs(v.getX().doubleValue()) - o.width - u.width, 0.0);
y = Math.min(Math.abs(v.getX().doubleValue()) - o.height - u.height, 0.0);
z = Math.min(Math.abs(v.getX().doubleValue()) - o.depth - u.depth, 0.0);
return Math.sqrt(x*x + y*y + z*z);
}
}
现在,我实现了Octree表示和Octree类
public class Octree {
public OctreeNode root;
/**
* Create an octree from the array points.
*/
public Octree(Point_3[] points){
/**
* We use the constructor provided by the class OctreeNode to
* construct the root.
*/
this.root = new OctreeNode(Arrays.asList(points));
}
}
我在这里有个问题。您是否认为实施是最好的方法?
这部分之后,我实现了分离良好的对分解类
public class WellSeparatedPairDecomposition {
class Pair<X,Y> {
X x; Y y;
Pair(X xx, Y yy) {
x = xx;
y = yy;
}
public X getFirst() {
return x;
}
public Y getSecond() {
return y;
}
@Override
public boolean equals(Object o) {
Pair<X,Y> p = (Pair<X,Y>) o;
return (p!=null && p.getFirst() == x && p.getSecond() == y);
}
}
/**
* Compute the WSPD from an octree and a threshold s.
* @param T the Octree,
* @param s threshold.
* @return the pairs of WSPD.
*/
public OctreeNode[][] wspd (Octree T, double epsilon) {
Set<Pair<OctreeNode, OctreeNode>> H = new HashSet<Pair<OctreeNode, OctreeNode>>();
wspd(T.root, T.root, epsilon, H);
int n = H.size();
OctreeNode[][] result = new OctreeNode[n][2];
int i = 0;
for(Pair<OctreeNode, OctreeNode> pair : H) {
result[i][0] = pair.getFirst();
result[i][1] = pair.getSecond();
i++;
}
return result;
}
boolean isWS(OctreeNode u, OctreeNode v, double epsilon) {
if(u == null || v == null || u.isEmpty() || v.isEmpty()) {
return false;
}
double distance = OctreeNode.distance(u, v);
return (u.diam() < epsilon*distance && v.diam() < epsilon*distance);
}
void wspd(OctreeNode u, OctreeNode v, double epsilon, Set<Pair<OctreeNode, OctreeNode>> H) {
if(u.isEmpty() || v.isEmpty() || (v.isLeaf() && u.isLeaf() && v.equals(u)))
return;
if(isWS(u, v, epsilon)) {
H.add(new Pair<OctreeNode, OctreeNode>(u, v));
return;
}
if(u.level > v.level) {
OctreeNode temp = u;
u = v;
v = temp;
}
if(!u.isLeaf()) {
for(OctreeNode uchild : u.children) {
wspd(uchild, v, epsilon, H);
}
}
}
}
我的实现与算法的描述完全相同。但是,当我测试时它非常慢。我该如何改善这种实现?