/**
*
*/
package wblut.geom;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import wblut.WB_Epsilon;
import wblut.math.WB_Fast;
import javolution.util.FastList;
// TODO: Auto-generated Javadoc
/**
* The Class WB_BSPTree2D.
*
* @author Frederik Vanhoutte, W:Blut
*/
public class WB_BSPTree2D {
/** The root. */
private WB_BSPNode2D root;
/**
* Instantiates a new w b_ bsp tree2 d.
*/
public WB_BSPTree2D() {
root = null;
}
/**
* Builds the.
*
* @param tree the tree
* @param segs the segs
*/
private void build(final WB_BSPNode2D tree,
final List<WB_ExplicitSegment2D> segs) {
WB_ExplicitSegment2D cseg = null;
final Iterator<WB_ExplicitSegment2D> S2DItr = segs.iterator();
if (S2DItr.hasNext()) {
cseg = S2DItr.next();
}
tree.partition = new WB_Line2D(cseg.getOrigin(), cseg.getDirection());
final FastList<WB_ExplicitSegment2D> _segs = new FastList<WB_ExplicitSegment2D>();
_segs.add(cseg);
final FastList<WB_ExplicitSegment2D> pos_list = new FastList<WB_ExplicitSegment2D>();
final FastList<WB_ExplicitSegment2D> neg_list = new FastList<WB_ExplicitSegment2D>();
WB_ExplicitSegment2D seg = null;
while (S2DItr.hasNext()) {
seg = S2DItr.next();
final WB_ClassifySegmentToLine2D result = tree.partition
.classifySegmentToLine2D(seg);
if (result == WB_ClassifySegmentToLine2D.SEGMENT_IN_FRONT_OF_LINE) {
pos_list.add(seg);
} else if (result == WB_ClassifySegmentToLine2D.SEGMENT_BEHIND_LINE) {
neg_list.add(seg);
} else if (result == WB_ClassifySegmentToLine2D.SEGMENT_SPANNING_LINE) { /* spanning */
final WB_ExplicitSegment2D[] split_seg = WB_Intersection2D
.splitSegment2D(seg, tree.partition);
if (split_seg != null) {
pos_list.add(split_seg[0]);
neg_list.add(split_seg[1]);
} else {
}
} else if (result == WB_ClassifySegmentToLine2D.SEGMENT_ON_LINE) {
_segs.add(seg);
}
}
if (!pos_list.isEmpty()) {
tree.pos = new WB_BSPNode2D();
build(tree.pos, pos_list);
}
if (!neg_list.isEmpty()) {
tree.neg = new WB_BSPNode2D();
build(tree.neg, neg_list);
}
if (tree.segments != null) {
tree.segments.clear();
}
tree.segments.addAll(_segs);
}
/**
* Builds the.
*
* @param segments the segments
*/
public void build(final List<WB_ExplicitSegment2D> segments) {
if (root == null) {
root = new WB_BSPNode2D();
}
build(root, segments);
}
/**
* Builds the.
*
* @param poly the poly
*/
public void build(final WB_Polygon2D poly) {
if (root == null) {
root = new WB_BSPNode2D();
}
build(root, poly.toExplicitSegments());
}
/**
* Point location.
*
* @param p the p
* @return the int
*/
public int pointLocation(final WB_Point2d p) {
return pointLocation(root, p);
}
/**
* Point location.
*
* @param x the x
* @param y the y
* @return the int
*/
public int pointLocation(final double x, final double y) {
return pointLocation(root, new WB_Point2d(x, y));
}
/**
* Point location.
*
* @param node the node
* @param p the p
* @return the int
*/
private int pointLocation(final WB_BSPNode2D node, final WB_Point2d p) {
final WB_ClassifyPointToLine2D type = node.partition
.classifyPointToLine2D(p);
if (type == WB_ClassifyPointToLine2D.POINT_IN_FRONT_OF_LINE) {
if (node.pos != null) {
return pointLocation(node.pos, p);
} else {
return 1;
}
} else if (type == WB_ClassifyPointToLine2D.POINT_BEHIND_LINE) {
if (node.neg != null) {
return pointLocation(node.neg, p);
} else {
return -1;
}
} else {
for (int i = 0; i < node.segments.size(); i++) {
if (WB_Epsilon.isZero(WB_Distance2D.distance(p,
node.segments.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 segment.
*
* @param S the s
* @param pos the pos
* @param neg the neg
* @param coSame the co same
* @param coDiff the co diff
*/
public void partitionSegment(final WB_ExplicitSegment2D S,
final List<WB_ExplicitSegment2D> pos,
final List<WB_ExplicitSegment2D> neg,
final List<WB_ExplicitSegment2D> coSame,
final List<WB_ExplicitSegment2D> coDiff) {
partitionSegment(root, S, pos, neg, coSame, coDiff);
}
/**
* Partition segment.
*
* @param node the node
* @param S the s
* @param pos the pos
* @param neg the neg
* @param coSame the co same
* @param coDiff the co diff
*/
private void partitionSegment(final WB_BSPNode2D node,
final WB_ExplicitSegment2D S, final List<WB_ExplicitSegment2D> pos,
final List<WB_ExplicitSegment2D> neg,
final List<WB_ExplicitSegment2D> coSame,
final List<WB_ExplicitSegment2D> coDiff) {
final WB_ClassifySegmentToLine2D type = node.partition
.classifySegmentToLine2D(S);
if (type == WB_ClassifySegmentToLine2D.SEGMENT_SPANNING_LINE) {
final WB_ExplicitSegment2D[] split = WB_Intersection2D
.splitSegment2D(S, node.partition);
if (split != null) {
getSegmentPosPartition(node, split[0], pos, neg, coSame, coDiff);
getSegmentNegPartition(node, split[1], pos, neg, coSame, coDiff);
}
} else if (type == WB_ClassifySegmentToLine2D.SEGMENT_IN_FRONT_OF_LINE) {
getSegmentPosPartition(node, S, pos, neg, coSame, coDiff);
} else if (type == WB_ClassifySegmentToLine2D.SEGMENT_BEHIND_LINE) {
getSegmentNegPartition(node, S, pos, neg, coSame, coDiff);
} else if (type == WB_ClassifySegmentToLine2D.SEGMENT_ON_LINE) {
partitionCoincidentSegments(node, S, pos, neg, coSame, coDiff);
}
}
/**
* Partition coincident segments.
*
* @param node the node
* @param S the s
* @param pos the pos
* @param neg the neg
* @param coSame the co same
* @param coDiff the co diff
*/
private void partitionCoincidentSegments(final WB_BSPNode2D node,
final WB_ExplicitSegment2D S, final List<WB_ExplicitSegment2D> pos,
final List<WB_ExplicitSegment2D> neg,
final List<WB_ExplicitSegment2D> coSame,
final List<WB_ExplicitSegment2D> coDiff) {
FastList<WB_ExplicitSegment2D> partSegments = new FastList<WB_ExplicitSegment2D>();
partSegments.add(S);
WB_ExplicitSegment2D thisS, otherS;
final WB_Line2D L = node.partition;
for (int i = 0; i < node.segments.size(); i++) {
final FastList<WB_ExplicitSegment2D> newpartSegments = new FastList<WB_ExplicitSegment2D>();
otherS = node.segments.get(i);
final double v0 = L.getT(otherS.getOrigin());
final double v1 = L.getT(otherS.getEnd());
for (int j = 0; j < partSegments.size(); j++) {
thisS = partSegments.get(j);
final double u0 = L.getT(thisS.getOrigin());
final double u1 = L.getT(thisS.getEnd());
double[] intersection;
if (u0 < u1) {
intersection = WB_Intersection2D.intervalIntersection(u0,
u1, WB_Fast.min(v0, v1), WB_Fast.max(v0, v1));
if (intersection[0] == 2) {
final WB_Point2d pi = L.getPoint(intersection[1]);
final WB_Point2d pj = L.getPoint(intersection[2]);
if (u0 < intersection[1]) {
newpartSegments.add(new WB_ExplicitSegment2D(thisS
.getOrigin(), pi));
}
coSame.add(new WB_ExplicitSegment2D(pi, pj));
if (u1 > intersection[2]) {
newpartSegments.add(new WB_ExplicitSegment2D(pj,
thisS.getEnd()));
}
} else {// this segment doesn't coincide with an edge
newpartSegments.add(thisS);
}
} else {
intersection = WB_Intersection2D.intervalIntersection(u1,
u0, WB_Fast.min(v0, v1), WB_Fast.max(v0, v1));
if (intersection[0] == 2) {
final WB_Point2d pi = L.getPoint(intersection[1]);
final WB_Point2d pj = L.getPoint(intersection[2]);
if (u1 < intersection[1]) {
newpartSegments.add(new WB_ExplicitSegment2D(pi,
thisS.getEnd()));
}
coDiff.add(new WB_ExplicitSegment2D(pj, pi));
if (u0 > intersection[2]) {
newpartSegments.add(new WB_ExplicitSegment2D(thisS
.getOrigin(), 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 segment pos partition.
*
* @param node the node
* @param S the s
* @param pos the pos
* @param neg the neg
* @param coSame the co same
* @param coDiff the co diff
* @return the segment pos partition
*/
private void getSegmentPosPartition(final WB_BSPNode2D node,
final WB_ExplicitSegment2D S, final List<WB_ExplicitSegment2D> pos,
final List<WB_ExplicitSegment2D> neg,
final List<WB_ExplicitSegment2D> coSame,
final List<WB_ExplicitSegment2D> coDiff) {
if (node.pos != null) {
partitionSegment(node.pos, S, pos, neg, coSame, coDiff);
} else {
pos.add(S);
}
}
/**
* Gets the segment neg partition.
*
* @param node the node
* @param S the s
* @param pos the pos
* @param neg the neg
* @param coSame the co same
* @param coDiff the co diff
* @return the segment neg partition
*/
private void getSegmentNegPartition(final WB_BSPNode2D node,
final WB_ExplicitSegment2D S, final List<WB_ExplicitSegment2D> pos,
final List<WB_ExplicitSegment2D> neg,
final List<WB_ExplicitSegment2D> coSame,
final List<WB_ExplicitSegment2D> coDiff) {
if (node.neg != null) {
partitionSegment(node.neg, S, pos, neg, coSame, coDiff);
} else {
neg.add(S);
}
}
/**
* To segments.
*
* @return the array list
*/
public ArrayList<WB_ExplicitSegment2D> toSegments() {
final ArrayList<WB_ExplicitSegment2D> segments = new ArrayList<WB_ExplicitSegment2D>();
addSegments(root, segments);
return segments;
}
/**
* Adds the segments.
*
* @param node the node
* @param segments the segments
*/
private void addSegments(final WB_BSPNode2D node,
final ArrayList<WB_ExplicitSegment2D> segments) {
segments.addAll(node.segments);
if (node.pos != null) {
addSegments(node.pos, segments);
}
if (node.neg != null) {
addSegments(node.neg, segments);
}
}
/**
* Negate.
*
* @return the w b_ bsp tree2 d
*/
public WB_BSPTree2D negate() {
final WB_BSPTree2D negTree = new WB_BSPTree2D();
negTree.root = negate(root);
return negTree;
}
/**
* Negate.
*
* @param node the node
* @return the w b_ bsp node2 d
*/
private WB_BSPNode2D negate(final WB_BSPNode2D node) {
final WB_BSPNode2D negNode = new WB_BSPNode2D();
negNode.partition = new WB_Line2D(node.partition.getOrigin(),
node.partition.getDirection().multAndCopy(-1));
for (int i = 0; i < node.segments.size(); i++) {
final WB_ExplicitSegment2D seg = node.segments.get(i);
negNode.segments.add(new WB_ExplicitSegment2D(seg.getEnd(), seg
.getOrigin()));
}
if (node.pos != null) {
negNode.neg = negate(node.pos);
}
if (node.neg != null) {
negNode.pos = negate(node.neg);
}
return negNode;
}
/**
* Partition polygon.
*
* @param P the p
* @param pos the pos
* @param neg the neg
*/
public void partitionPolygon(final WB_Polygon2D P,
final List<WB_Polygon2D> pos, final List<WB_Polygon2D> neg) {
partitionPolygon(root, P, pos, neg);
}
/**
* Partition polygon.
*
* @param node the node
* @param P the p
* @param pos the pos
* @param neg the neg
*/
private void partitionPolygon(final WB_BSPNode2D node,
final WB_Polygon2D P, final List<WB_Polygon2D> pos,
final List<WB_Polygon2D> neg) {
if (P.n > 2) {
final WB_ClassifyPolygonToLine2D type = node.partition
.classifyPolygonToLine2D(P);
if (type == WB_ClassifyPolygonToLine2D.POLYGON_SPANNING_LINE) {
final WB_Polygon2D[] split = WB_Intersection2D.splitPolygon2D(
P, node.partition);
if (split[0].n > 2) {
getPolygonPosPartition(node, split[0], pos, neg);
}
if (split[1].n > 2) {
getPolygonNegPartition(node, split[1], pos, neg);
}
} else if (type == WB_ClassifyPolygonToLine2D.POLYGON_IN_FRONT_OF_LINE) {
getPolygonPosPartition(node, P, pos, neg);
} else if (type == WB_ClassifyPolygonToLine2D.POLYGON_BEHIND_LINE) {
getPolygonNegPartition(node, P, pos, neg);
}
}
}
/**
* Gets the polygon pos partition.
*
* @param node the node
* @param P the p
* @param pos the pos
* @param neg the neg
* @return the polygon pos partition
*/
private void getPolygonPosPartition(final WB_BSPNode2D node,
final WB_Polygon2D P, final List<WB_Polygon2D> pos,
final List<WB_Polygon2D> neg) {
if (node.pos != null) {
partitionPolygon(node.pos, P, pos, neg);
} 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
* @return the polygon neg partition
*/
private void getPolygonNegPartition(final WB_BSPNode2D node,
final WB_Polygon2D P, final List<WB_Polygon2D> pos,
final List<WB_Polygon2D> neg) {
if (node.neg != null) {
partitionPolygon(node.neg, P, pos, neg);
} else {
neg.add(P);
}
}
}