Java implementation of well-separated pair decomposition












0














I was trying to implement the algorithm of the 3D well-separated pair decomposition WSPD using an octree.



First, I begin by implementing the class OctreeNode as follows:



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;


Every non-leaf Node has exactly 8 children. I implement a constructor for the class which takes a list of points in 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);
}

}


After, I implement the add function of my class:



/**
* @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;
}
}
}
}


I also implement a function distance to compute the distance between two nodes in the Octree which is the distance between the two boxes of the nodes:



/**
* @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);
}
}


Now, I implement the class Octree representing and 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));
}
}


Here I have a question. Do you think my implementation is the best way to do it?



After this part I implement the well-separated pair decomposition class:



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);
}
}
}
}


My implementation works without errors. However, when I tested, it is very slow and for big size tests it gives an outOfMemoryError. How can I improve this implementation?










share|improve this question









New contributor




Youem is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.













This question has an open bounty worth +100
reputation from Youem ending in 7 days.


The question is widely applicable to a large audience. A detailed canonical answer is required to address all the concerns.












  • 1




    The lack of indentation makes parts of the code hard to follow.
    – 1201ProgramAlarm
    2 days ago










  • I think I corrected it. Thank you
    – Youem
    2 days ago
















0














I was trying to implement the algorithm of the 3D well-separated pair decomposition WSPD using an octree.



First, I begin by implementing the class OctreeNode as follows:



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;


Every non-leaf Node has exactly 8 children. I implement a constructor for the class which takes a list of points in 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);
}

}


After, I implement the add function of my class:



/**
* @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;
}
}
}
}


I also implement a function distance to compute the distance between two nodes in the Octree which is the distance between the two boxes of the nodes:



/**
* @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);
}
}


Now, I implement the class Octree representing and 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));
}
}


Here I have a question. Do you think my implementation is the best way to do it?



After this part I implement the well-separated pair decomposition class:



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);
}
}
}
}


My implementation works without errors. However, when I tested, it is very slow and for big size tests it gives an outOfMemoryError. How can I improve this implementation?










share|improve this question









New contributor




Youem is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.













This question has an open bounty worth +100
reputation from Youem ending in 7 days.


The question is widely applicable to a large audience. A detailed canonical answer is required to address all the concerns.












  • 1




    The lack of indentation makes parts of the code hard to follow.
    – 1201ProgramAlarm
    2 days ago










  • I think I corrected it. Thank you
    – Youem
    2 days ago














0












0








0







I was trying to implement the algorithm of the 3D well-separated pair decomposition WSPD using an octree.



First, I begin by implementing the class OctreeNode as follows:



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;


Every non-leaf Node has exactly 8 children. I implement a constructor for the class which takes a list of points in 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);
}

}


After, I implement the add function of my class:



/**
* @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;
}
}
}
}


I also implement a function distance to compute the distance between two nodes in the Octree which is the distance between the two boxes of the nodes:



/**
* @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);
}
}


Now, I implement the class Octree representing and 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));
}
}


Here I have a question. Do you think my implementation is the best way to do it?



After this part I implement the well-separated pair decomposition class:



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);
}
}
}
}


My implementation works without errors. However, when I tested, it is very slow and for big size tests it gives an outOfMemoryError. How can I improve this implementation?










share|improve this question









New contributor




Youem is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.











I was trying to implement the algorithm of the 3D well-separated pair decomposition WSPD using an octree.



First, I begin by implementing the class OctreeNode as follows:



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;


Every non-leaf Node has exactly 8 children. I implement a constructor for the class which takes a list of points in 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);
}

}


After, I implement the add function of my class:



/**
* @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;
}
}
}
}


I also implement a function distance to compute the distance between two nodes in the Octree which is the distance between the two boxes of the nodes:



/**
* @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);
}
}


Now, I implement the class Octree representing and 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));
}
}


Here I have a question. Do you think my implementation is the best way to do it?



After this part I implement the well-separated pair decomposition class:



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);
}
}
}
}


My implementation works without errors. However, when I tested, it is very slow and for big size tests it gives an outOfMemoryError. How can I improve this implementation?







java tree






share|improve this question









New contributor




Youem is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.











share|improve this question









New contributor




Youem is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.









share|improve this question




share|improve this question








edited 12 mins ago









Jamal

30.2k11116226




30.2k11116226






New contributor




Youem is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.









asked 2 days ago









Youem

13




13




New contributor




Youem is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.





New contributor





Youem is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.






Youem is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.






This question has an open bounty worth +100
reputation from Youem ending in 7 days.


The question is widely applicable to a large audience. A detailed canonical answer is required to address all the concerns.








This question has an open bounty worth +100
reputation from Youem ending in 7 days.


The question is widely applicable to a large audience. A detailed canonical answer is required to address all the concerns.










  • 1




    The lack of indentation makes parts of the code hard to follow.
    – 1201ProgramAlarm
    2 days ago










  • I think I corrected it. Thank you
    – Youem
    2 days ago














  • 1




    The lack of indentation makes parts of the code hard to follow.
    – 1201ProgramAlarm
    2 days ago










  • I think I corrected it. Thank you
    – Youem
    2 days ago








1




1




The lack of indentation makes parts of the code hard to follow.
– 1201ProgramAlarm
2 days ago




The lack of indentation makes parts of the code hard to follow.
– 1201ProgramAlarm
2 days ago












I think I corrected it. Thank you
– Youem
2 days ago




I think I corrected it. Thank you
– Youem
2 days ago















active

oldest

votes











Your Answer





StackExchange.ifUsing("editor", function () {
return StackExchange.using("mathjaxEditing", function () {
StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
});
});
}, "mathjax-editing");

StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");

StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "196"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});

function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});


}
});






Youem is a new contributor. Be nice, and check out our Code of Conduct.










draft saved

draft discarded


















StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f210138%2fjava-implementation-of-well-separated-pair-decomposition%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown






























active

oldest

votes













active

oldest

votes









active

oldest

votes






active

oldest

votes








Youem is a new contributor. Be nice, and check out our Code of Conduct.










draft saved

draft discarded


















Youem is a new contributor. Be nice, and check out our Code of Conduct.













Youem is a new contributor. Be nice, and check out our Code of Conduct.












Youem is a new contributor. Be nice, and check out our Code of Conduct.
















Thanks for contributing an answer to Code Review Stack Exchange!


  • Please be sure to answer the question. Provide details and share your research!

But avoid



  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.


Use MathJax to format equations. MathJax reference.


To learn more, see our tips on writing great answers.





Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


Please pay close attention to the following guidance:


  • Please be sure to answer the question. Provide details and share your research!

But avoid



  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.


To learn more, see our tips on writing great answers.




draft saved


draft discarded














StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f210138%2fjava-implementation-of-well-separated-pair-decomposition%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown







Popular posts from this blog

Morgemoulin

Scott Moir

Souastre