/**
*
*/
package wblut.geom;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import wblut.WB_Epsilon;
import wblut.hemesh.HEMC_SplitMesh;
import wblut.hemesh.HE_Mesh;
import javolution.util.FastList;
// TODO: Auto-generated Javadoc
/**
* The Class WB_BSPTree.
*
* @author Frederik Vanhoutte, W:Blut
*/
public class WB_BSPTree {
/** The root. */
private WB_BSPNode root;
/**
* Instantiates a new w b_ bsp tree.
*/
public WB_BSPTree() {
root = null;
}
/**
* Builds the.
*
* @param tree the tree
* @param polygons the polygons
*/
private void build(final WB_BSPNode tree, final List<WB_ExplicitPolygon> polygons) {
if (polygons.size() > 0) {
WB_ExplicitPolygon cpol = null;
final Iterator<WB_ExplicitPolygon> PItr = polygons.iterator();
if (PItr.hasNext()) {
cpol = PItr.next();
}
tree.partition = cpol.getPlane();
final FastList<WB_ExplicitPolygon> _pols = new FastList<WB_ExplicitPolygon>();
_pols.add(cpol);
final FastList<WB_ExplicitPolygon> pos_list = new FastList<WB_ExplicitPolygon>();
final FastList<WB_ExplicitPolygon> neg_list = new FastList<WB_ExplicitPolygon>();
WB_ExplicitPolygon pol = null;
while (PItr.hasNext()) {
pol = PItr.next();
final WB_ClassifyPolygonToPlane result = tree.partition
.classifyPolygonToPlane(pol);
if (result == WB_ClassifyPolygonToPlane.POLYGON_IN_FRONT_OF_PLANE) {
pos_list.add(pol);
} else if (result == WB_ClassifyPolygonToPlane.POLYGON_BEHIND_PLANE) {
neg_list.add(pol);
} else if (result == WB_ClassifyPolygonToPlane.POLYGON_STRADDLING_PLANE) { /* spanning */
final WB_ExplicitPolygon frontPoly = new WB_ExplicitPolygon();
final WB_ExplicitPolygon backPoly = new WB_ExplicitPolygon();
pol.splitPolygonInto(tree.partition, frontPoly, backPoly);
if (frontPoly.n > 2) {
pos_list.add(frontPoly);
}
if (backPoly.n > 2) {
neg_list.add(backPoly);
}
} else if (result == WB_ClassifyPolygonToPlane.POLYGON_ON_PLANE) {
_pols.add(pol);
}
}
if (!pos_list.isEmpty()) {
tree.pos = new WB_BSPNode();
build(tree.pos, pos_list);
}
if (!neg_list.isEmpty()) {
tree.neg = new WB_BSPNode();
build(tree.neg, neg_list);
}
if (tree.polygons != null) {
tree.polygons.clear();
}
tree.polygons.addAll(_pols);
}
}
/**
* Builds the.
*
* @param tree the tree
* @param polygons the polygons
*/
private void build(final WB_BSPNode tree, final WB_ExplicitPolygon[] polygons) {
if (polygons.length > 0) {
final WB_ExplicitPolygon cpol = polygons[0];
tree.partition = cpol.getPlane();
final FastList<WB_ExplicitPolygon> _pols = new FastList<WB_ExplicitPolygon>();
_pols.add(cpol);
final FastList<WB_ExplicitPolygon> pos_list = new FastList<WB_ExplicitPolygon>();
final FastList<WB_ExplicitPolygon> neg_list = new FastList<WB_ExplicitPolygon>();
WB_ExplicitPolygon pol = null;
for (int i = 1; i < polygons.length; i++) {
pol = polygons[i];
final WB_ClassifyPolygonToPlane result = tree.partition
.classifyPolygonToPlane(pol);
if (result == WB_ClassifyPolygonToPlane.POLYGON_IN_FRONT_OF_PLANE) {
pos_list.add(pol);
} else if (result == WB_ClassifyPolygonToPlane.POLYGON_BEHIND_PLANE) {
neg_list.add(pol);
} else if (result == WB_ClassifyPolygonToPlane.POLYGON_STRADDLING_PLANE) { /* spanning */
final WB_ExplicitPolygon frontPoly = new WB_ExplicitPolygon();
final WB_ExplicitPolygon backPoly = new WB_ExplicitPolygon();
pol.splitPolygonInto(tree.partition, frontPoly, backPoly);
if (frontPoly.n > 2) {
pos_list.add(frontPoly);
}
if (backPoly.n > 2) {
neg_list.add(backPoly);
}
} else if (result == WB_ClassifyPolygonToPlane.POLYGON_ON_PLANE) {
_pols.add(pol);
}
}
if (!pos_list.isEmpty()) {
tree.pos = new WB_BSPNode();
build(tree.pos, pos_list);
}
if (!neg_list.isEmpty()) {
tree.neg = new WB_BSPNode();
build(tree.neg, neg_list);
}
if (tree.polygons != null) {
tree.polygons.clear();
}
tree.polygons.addAll(_pols);
}
}
/**
* Builds the.
*
* @param polygons the polygons
*/
public void build(final List<WB_ExplicitPolygon> polygons) {
if (root == null) {
root = new WB_BSPNode();
}
build(root, polygons);
}
/**
* Builds the.
*
* @param polygons the polygons
*/
public void build(final WB_ExplicitPolygon[] polygons) {
if (root == null) {
root = new WB_BSPNode();
}
build(root, polygons);
}
/**
* Builds the.
*
* @param mesh the mesh
*/
public void build(final HE_Mesh mesh) {
if (root == null) {
root = new WB_BSPNode();
}
build(root, mesh.getPolygons());
}
/**
* Point location.
*
* @param p the p
* @return the int
*/
public int pointLocation(final WB_Point3d p) {
return pointLocation(root, p);
}
/**
* Point location.
*
* @param x the x
* @param y the y
* @param z the z
* @return the int
*/
public int pointLocation(final double x, final double y, final double z) {
return pointLocation(root, new WB_Point3d(x, y, z));
}
/**
* Point location.
*
* @param node the node
* @param p the p
* @return the int
*/
private int pointLocation(final WB_BSPNode node, final WB_Point3d p) {
final WB_ClassifyPointToPlane type = node.partition
.classifyPointToPlane(p);
if (type == WB_ClassifyPointToPlane.POINT_IN_FRONT_OF_PLANE) {
if (node.pos != null) {
return pointLocation(node.pos, p);
} else {
return 1;
}
} else if (type == WB_ClassifyPointToPlane.POINT_BEHIND_PLANE) {
if (node.neg != null) {
return pointLocation(node.neg, p);
} else {
return -1;
}
} else {
for (int i = 0; i < node.polygons.size(); i++) {
if (WB_Epsilon.isZeroSq(WB_Distance.sqDistance(p,
node.polygons.get(i)))) {
return 0;
}
}
if (node.pos != null) {
return pointLocation(node.pos, p);
} else if (node.neg != null) {
return pointLocation(node.neg, p);
} else {
return 0;
}
}
}
/**
* Partition polygon.
*
* @param P the p
* @param pos the pos
* @param neg the neg
* @param coSame the co same
* @param coDiff the co diff
*/
public void partitionPolygon(final WB_ExplicitPolygon P,
final List<WB_ExplicitPolygon> pos, final List<WB_ExplicitPolygon> neg,
final List<WB_ExplicitPolygon> coSame, final List<WB_ExplicitPolygon> coDiff) {
partitionPolygon(root, P, pos, neg, coSame, coDiff);
}
/**
* Partition polygon.
*
* @param node the node
* @param P the p
* @param pos the pos
* @param neg the neg
* @param coSame the co same
* @param coDiff the co diff
*/
private void partitionPolygon(final WB_BSPNode node, final WB_ExplicitPolygon P,
final List<WB_ExplicitPolygon> pos, final List<WB_ExplicitPolygon> neg,
final List<WB_ExplicitPolygon> coSame, final List<WB_ExplicitPolygon> coDiff) {
final WB_ClassifyPolygonToPlane type = node.partition
.classifyPolygonToPlane(P);
if (type == WB_ClassifyPolygonToPlane.POLYGON_STRADDLING_PLANE) {
final WB_ExplicitPolygon frontPoly = new WB_ExplicitPolygon();
final WB_ExplicitPolygon backPoly = new WB_ExplicitPolygon();
P.splitPolygonInto(node.partition, frontPoly, backPoly);
if (frontPoly.n > 2) {
getPolygonPosPartition(node, frontPoly, pos, neg, coSame,
coDiff);
}
if (backPoly.n > 2) {
getPolygonNegPartition(node, backPoly, pos, neg, coSame, coDiff);
}
} else if (type == WB_ClassifyPolygonToPlane.POLYGON_IN_FRONT_OF_PLANE) {
getPolygonPosPartition(node, P, pos, neg, coSame, coDiff);
} else if (type == WB_ClassifyPolygonToPlane.POLYGON_BEHIND_PLANE) {
getPolygonNegPartition(node, P, pos, neg, coSame, coDiff);
} else if (type == WB_ClassifyPolygonToPlane.POLYGON_ON_PLANE) {
partitionCoincidentPolygons(node, P, pos, neg, coSame, coDiff);
}
}
/**
* Partition coincident polygons.
*
* @param node the node
* @param P the p
* @param pos the pos
* @param neg the neg
* @param coSame the co same
* @param coDiff the co diff
*/
private void partitionCoincidentPolygons(final WB_BSPNode node,
final WB_ExplicitPolygon P, final List<WB_ExplicitPolygon> pos,
final List<WB_ExplicitPolygon> neg, final List<WB_ExplicitPolygon> coSame,
final List<WB_ExplicitPolygon> coDiff) {
/*
* FastList<WB_Polygon> partSegments = new FastList<WB_Polygon>();
* partSegments.add(S); WB_Polygon thisS, otherS; final WB_Line2D L =
* node.partition; for (int i = 0; i < node.segments.size(); i++) {
* final FastList<WB_Polygon> newpartSegments = new
* FastList<WB_Polygon>(); otherS = node.segments.get(i); final double
* v0 = L.getT(otherS.origin()); final double v1 = L.getT(otherS.end());
* for (int j = 0; j < partSegments.size(); j++) { thisS =
* partSegments.get(j); final double u0 = L.getT(thisS.origin()); final
* double u1 = L.getT(thisS.end()); double[] intersection; if (u0 <= u1)
* { intersection = WB_Intersection2D.intervalIntersection(u0, u1, v0,
* v1); if (intersection[0] == 2) { final WB_XY pi =
* L.getPoint(intersection[1]); final WB_XY pj =
* L.getPoint(intersection[2]); if (u0 < intersection[1]) {
* newpartSegments.add(new WB_Polygon(thisS.origin(), pi)); }
* coSame.add(new WB_Polygon(pi, pj)); if (u1 > intersection[2]) {
* newpartSegments .add(new WB_Polygon(pj, thisS.end())); } } else {//
* this segment doesn't coincide with an edge
* newpartSegments.add(thisS); } } else { intersection =
* WB_Intersection2D.intervalIntersection(u1, u0, v0, v1); if
* (intersection[0] == 2) { final WB_XY pi =
* L.getPoint(intersection[1]); final WB_XY pj =
* L.getPoint(intersection[2]); if (u1 < intersection[1]) {
* newpartSegments .add(new WB_Polygon(pi, thisS.end())); }
* coDiff.add(new WB_Polygon(pj, pi)); if (u0 > intersection[2]) {
* newpartSegments.add(new WB_Polygon(thisS.origin(), pj)); } } else {
* newpartSegments.add(thisS); } } } partSegments = newpartSegments; }
* for (int i = 0; i < partSegments.size(); i++) {
* getSegmentPosPartition(node, partSegments.get(i), pos, neg, coSame,
* coDiff); getSegmentNegPartition(node, partSegments.get(i), pos, neg,
* coSame, coDiff); }
*/
}
/**
* Gets the polygon pos partition.
*
* @param node the node
* @param P the p
* @param pos the pos
* @param neg the neg
* @param coSame the co same
* @param coDiff the co diff
* @return the polygon pos partition
*/
private void getPolygonPosPartition(final WB_BSPNode node,
final WB_ExplicitPolygon P, final List<WB_ExplicitPolygon> pos,
final List<WB_ExplicitPolygon> neg, final List<WB_ExplicitPolygon> coSame,
final List<WB_ExplicitPolygon> coDiff) {
if (node.pos != null) {
partitionPolygon(node.pos, P, pos, neg, coSame, coDiff);
} else {
pos.add(P);
}
}
/**
* Gets the polygon neg partition.
*
* @param node the node
* @param P the p
* @param pos the pos
* @param neg the neg
* @param coSame the co same
* @param coDiff the co diff
* @return the polygon neg partition
*/
private void getPolygonNegPartition(final WB_BSPNode node,
final WB_ExplicitPolygon P, final List<WB_ExplicitPolygon> pos,
final List<WB_ExplicitPolygon> neg, final List<WB_ExplicitPolygon> coSame,
final List<WB_ExplicitPolygon> coDiff) {
if (node.neg != null) {
partitionPolygon(node.neg, P, pos, neg, coSame, coDiff);
} else {
neg.add(P);
}
}
/**
* Partition mesh.
*
* @param mesh the mesh
* @param pos the pos
* @param neg the neg
*/
public void partitionMesh(final HE_Mesh mesh, final List<HE_Mesh> pos,
final List<HE_Mesh> neg) {
partitionMesh(root, mesh, pos, neg);
}
/**
* Partition mesh.
*
* @param node the node
* @param mesh the mesh
* @param pos the pos
* @param neg the neg
*/
private void partitionMesh(final WB_BSPNode node, final HE_Mesh mesh,
final List<HE_Mesh> pos, final List<HE_Mesh> neg) {
final HEMC_SplitMesh sm = new HEMC_SplitMesh();
sm.setMesh(mesh);
sm.setPlane(node.partition);
final HE_Mesh[] split = sm.create();
if (split[0].numberOfVertices() > 4) {
getMeshPosPartition(node, split[0], pos, neg);
}
if (split[0].numberOfVertices() > 4) {
getMeshNegPartition(node, split[1], pos, neg);
}
}
/**
* Gets the mesh pos partition.
*
* @param node the node
* @param mesh the mesh
* @param pos the pos
* @param neg the neg
* @return the mesh pos partition
*/
private void getMeshPosPartition(final WB_BSPNode node, final HE_Mesh mesh,
final List<HE_Mesh> pos, final List<HE_Mesh> neg) {
if (node.pos != null) {
partitionMesh(node.pos, mesh, pos, neg);
} else {
pos.add(mesh);
}
}
/**
* Gets the mesh neg partition.
*
* @param node the node
* @param mesh the mesh
* @param pos the pos
* @param neg the neg
* @return the mesh neg partition
*/
private void getMeshNegPartition(final WB_BSPNode node, final HE_Mesh mesh,
final List<HE_Mesh> pos, final List<HE_Mesh> neg) {
if (node.neg != null) {
partitionMesh(node.neg, mesh, pos, neg);
} else {
neg.add(mesh);
}
}
/**
* To polygons.
*
* @return the array list
*/
public ArrayList<WB_ExplicitPolygon> toPolygons() {
final ArrayList<WB_ExplicitPolygon> polygons = new ArrayList<WB_ExplicitPolygon>();
addPolygons(root, polygons);
return polygons;
}
/**
* Adds the polygons.
*
* @param node the node
* @param polygons the polygons
*/
private void addPolygons(final WB_BSPNode node,
final ArrayList<WB_ExplicitPolygon> polygons) {
polygons.addAll(node.polygons);
if (node.pos != null) {
addPolygons(node.pos, polygons);
}
if (node.neg != null) {
addPolygons(node.neg, polygons);
}
}
/**
* Negate.
*
* @return the w b_ bsp tree
*/
public WB_BSPTree negate() {
final WB_BSPTree negTree = new WB_BSPTree();
negTree.root = negate(root);
return negTree;
}
/**
* Negate.
*
* @param node the node
* @return the w b_ bsp node
*/
private WB_BSPNode negate(final WB_BSPNode node) {
final WB_BSPNode negNode = new WB_BSPNode();
negNode.partition = node.partition.get();
negNode.partition.flipNormal();
for (int i = 0; i < node.polygons.size(); i++) {
final WB_ExplicitPolygon pol = node.polygons.get(i);
negNode.polygons.add(pol.negate());
}
if (node.pos != null) {
negNode.neg = negate(node.pos);
}
if (node.neg != null) {
negNode.pos = negate(node.neg);
}
return negNode;
}
}