blob: 39e6a11bcd04663ef5db61b31499891eb50f4dd3 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.geometry.core.partitioning.bsp;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
import org.apache.commons.geometry.core.Transform;
import org.apache.commons.geometry.core.partition.test.PartitionTestUtils;
import org.apache.commons.geometry.core.partition.test.TestBSPTree;
import org.apache.commons.geometry.core.partition.test.TestBSPTree.TestNode;
import org.apache.commons.geometry.core.partition.test.TestLine;
import org.apache.commons.geometry.core.partition.test.TestLineSegment;
import org.apache.commons.geometry.core.partition.test.TestLineSegmentCollection;
import org.apache.commons.geometry.core.partition.test.TestPoint2D;
import org.apache.commons.geometry.core.partition.test.TestTransform2D;
import org.apache.commons.geometry.core.partitioning.BoundarySource;
import org.apache.commons.geometry.core.partitioning.bsp.BSPTree.NodeCutRule;
import org.junit.Assert;
import org.junit.Test;
public class AbstractBSPTreeTest {
@Test
public void testInitialization() {
// act
TestBSPTree tree = new TestBSPTree();
// assert
TestNode root = tree.getRoot();
Assert.assertNotNull(root);
Assert.assertNull(root.getParent());
PartitionTestUtils.assertIsLeafNode(root);
Assert.assertFalse(root.isPlus());
Assert.assertFalse(root.isMinus());
Assert.assertSame(tree, root.getTree());
}
@Test
public void testNodeStateGetters() {
// arrange
TestBSPTree tree = new TestBSPTree();
TestNode root = tree.getRoot();
root.cut(TestLine.X_AXIS);
TestNode plus = root.getPlus();
TestNode minus = root.getMinus();
// act/assert
Assert.assertFalse(root.isLeaf());
Assert.assertTrue(root.isInternal());
Assert.assertFalse(root.isPlus());
Assert.assertFalse(root.isMinus());
Assert.assertTrue(plus.isLeaf());
Assert.assertFalse(plus.isInternal());
Assert.assertTrue(plus.isPlus());
Assert.assertFalse(plus.isMinus());
Assert.assertTrue(minus.isLeaf());
Assert.assertFalse(minus.isInternal());
Assert.assertFalse(minus.isPlus());
Assert.assertTrue(minus.isMinus());
}
@Test
public void testInsertCut() {
// arrange
TestBSPTree tree = new TestBSPTree();
TestLine line = TestLine.X_AXIS;
// act
boolean result = tree.getRoot().insertCut(line);
// assert
Assert.assertTrue(result);
TestNode root = tree.getRoot();
PartitionTestUtils.assertIsInternalNode(root);
Assert.assertSame(line, root.getCut().getHyperplane());
PartitionTestUtils.assertIsLeafNode(root.getMinus());
PartitionTestUtils.assertIsLeafNode(root.getPlus());
}
@Test
public void testInsertCut_fitsCutterToCell() {
// arrange
TestBSPTree tree = new TestBSPTree();
TestNode node = tree.getRoot()
.cut(TestLine.X_AXIS)
.getMinus()
.cut(TestLine.Y_AXIS)
.getPlus();
// act
boolean result = node.insertCut(new TestLine(0.5, 1.5, 1.5, 0.5));
// assert
Assert.assertTrue(result);
PartitionTestUtils.assertIsInternalNode(node);
TestLineSegment segment = (TestLineSegment) node.getCut();
PartitionTestUtils.assertPointsEqual(new TestPoint2D(0, 2), segment.getStartPoint());
PartitionTestUtils.assertPointsEqual(new TestPoint2D(2, 0), segment.getEndPoint());
}
@Test
public void testInsertCut_doesNotPassThroughCell_intersects() {
// arrange
TestBSPTree tree = new TestBSPTree();
TestNode node = tree.getRoot()
.cut(TestLine.X_AXIS)
.getMinus()
.cut(TestLine.Y_AXIS)
.getPlus();
// act
boolean result = node.insertCut(new TestLine(-2, 0, 0, -2));
// assert
Assert.assertFalse(result);
PartitionTestUtils.assertIsLeafNode(node);
}
@Test
public void testInsertCut_doesNotPassThroughCell_parallel() {
// arrange
TestBSPTree tree = new TestBSPTree();
TestNode node = tree.getRoot()
.cut(TestLine.X_AXIS)
.getMinus();
// act
boolean result = node.insertCut(new TestLine(0, -1, 1, -1));
// assert
Assert.assertFalse(result);
PartitionTestUtils.assertIsLeafNode(node);
}
@Test
public void testInsertCut_doesNotPassThroughCell_removesExistingChildren() {
// arrange
TestBSPTree tree = new TestBSPTree();
TestNode node = tree.getRoot()
.cut(TestLine.X_AXIS)
.getMinus()
.cut(TestLine.Y_AXIS)
.getPlus()
.cut(new TestLine(0, 2, 2, 0));
// act
boolean result = node.insertCut(new TestLine(-2, 0, 0, -2));
// assert
Assert.assertFalse(result);
PartitionTestUtils.assertIsLeafNode(node);
}
@Test
public void testInsertCut_cutExistsInTree_sameOrientation() {
// arrange
TestBSPTree tree = new TestBSPTree();
TestNode node = tree.getRoot()
.cut(TestLine.X_AXIS)
.getMinus()
.cut(TestLine.Y_AXIS)
.getPlus()
.cut(new TestLine(0, 2, 2, 0));
// act
boolean result = node.insertCut(new TestLine(0, 2, 0, 3));
// assert
Assert.assertFalse(result);
PartitionTestUtils.assertIsLeafNode(node);
}
@Test
public void testInsertCut_cutExistsInTree_oppositeOrientation() {
// arrange
TestBSPTree tree = new TestBSPTree();
TestNode node = tree.getRoot()
.cut(TestLine.X_AXIS)
.getMinus()
.cut(TestLine.Y_AXIS)
.getPlus()
.cut(new TestLine(0, 2, 2, 0));
// act
boolean result = node.insertCut(new TestLine(0, 3, 0, 2));
// assert
Assert.assertTrue(result);
PartitionTestUtils.assertIsInternalNode(node);
}
@Test
public void testInsertCut_createRegionWithThicknessOfHyperplane() {
// arrange
TestBSPTree tree = new TestBSPTree();
TestNode node = tree.getRoot()
.cut(TestLine.X_AXIS)
.getMinus();
// act
boolean result = node.insertCut(new TestLine(0, 0, -1, 0));
// assert
Assert.assertTrue(result);
Assert.assertSame(tree.getRoot().getPlus(), tree.findNode(new TestPoint2D(0, -1e-2)));
Assert.assertSame(node.getMinus(), tree.findNode(new TestPoint2D(0, 0)));
Assert.assertSame(node.getPlus(), tree.findNode(new TestPoint2D(0, 1e-2)));
}
@Test
public void testClearCut_cutExists() {
// arrange
TestBSPTree tree = new TestBSPTree();
TestNode node = tree.getRoot()
.cut(TestLine.X_AXIS)
.getMinus()
.cut(TestLine.Y_AXIS);
// act
boolean result = node.clearCut();
// assert
Assert.assertTrue(result);
Assert.assertTrue(node.isLeaf());
Assert.assertNull(node.getPlus());
Assert.assertNull(node.getMinus());
}
@Test
public void testClearCut_cutDoesNotExist() {
// arrange
TestBSPTree tree = new TestBSPTree();
TestNode node = tree.getRoot()
.cut(TestLine.X_AXIS)
.getMinus()
.cut(TestLine.Y_AXIS)
.getMinus();
// act
boolean result = node.clearCut();
// assert
Assert.assertFalse(result);
Assert.assertTrue(node.isLeaf());
Assert.assertNull(node.getPlus());
Assert.assertNull(node.getMinus());
}
@Test
public void testClearCut_root_fullTree() {
// arrange
TestBSPTree tree = new TestBSPTree();
TestNode node = tree.getRoot()
.cut(TestLine.X_AXIS)
.getMinus()
.cut(TestLine.Y_AXIS)
.getMinus();
// act
boolean result = tree.getRoot().clearCut();
// assert
Assert.assertTrue(result);
Assert.assertTrue(node.isLeaf());
Assert.assertNull(node.getPlus());
Assert.assertNull(node.getMinus());
Assert.assertEquals(1, tree.count());
}
@Test
public void testClearCut_root_emptyTree() {
// arrange
TestBSPTree tree = new TestBSPTree();
TestNode node = tree.getRoot();
// act
boolean result = node.clearCut();
// assert
Assert.assertFalse(result);
Assert.assertTrue(node.isLeaf());
Assert.assertNull(node.getPlus());
Assert.assertNull(node.getMinus());
Assert.assertEquals(1, tree.count());
}
@Test
public void testFindNode_emptyTree() {
// arrange
TestBSPTree tree = new TestBSPTree();
TestNode root = tree.getRoot();
List<TestPoint2D> testPoints = Arrays.asList(
new TestPoint2D(0, 0),
new TestPoint2D(1, 0),
new TestPoint2D(1, 1),
new TestPoint2D(0, 1),
new TestPoint2D(-1, 1),
new TestPoint2D(-1, 0),
new TestPoint2D(-1, -1),
new TestPoint2D(0, -1),
new TestPoint2D(1, -1)
);
// act/assert
for (TestPoint2D pt : testPoints) {
Assert.assertSame(root, tree.findNode(pt));
}
for (TestPoint2D pt : testPoints) {
Assert.assertSame(root, tree.findNode(pt, NodeCutRule.NODE));
}
for (TestPoint2D pt : testPoints) {
Assert.assertSame(root, tree.findNode(pt, NodeCutRule.MINUS));
}
for (TestPoint2D pt : testPoints) {
Assert.assertSame(root, tree.findNode(pt, NodeCutRule.PLUS));
}
}
@Test
public void testFindNode_singleArg() {
// arrange
TestBSPTree tree = new TestBSPTree();
tree.getRoot()
.cut(TestLine.X_AXIS)
.getMinus()
.cut(TestLine.Y_AXIS)
.getPlus()
.cut(new TestLine(0, 2, 2, 0));
TestNode root = tree.getRoot();
TestNode minusY = root.getPlus();
TestNode yCut = root.getMinus();
TestNode minusXPlusY = yCut.getMinus();
TestNode diagonalCut = yCut.getPlus();
TestNode underDiagonal = diagonalCut.getPlus();
TestNode aboveDiagonal = diagonalCut.getMinus();
// act/assert
Assert.assertSame(minusXPlusY, tree.findNode(new TestPoint2D(0, 0)));
Assert.assertSame(underDiagonal, tree.findNode(new TestPoint2D(1, 0)));
Assert.assertSame(aboveDiagonal, tree.findNode(new TestPoint2D(1, 1)));
Assert.assertSame(minusXPlusY, tree.findNode(new TestPoint2D(0, 1)));
Assert.assertSame(minusXPlusY, tree.findNode(new TestPoint2D(-1, 1)));
Assert.assertSame(minusXPlusY, tree.findNode(new TestPoint2D(-1, 0)));
Assert.assertSame(minusY, tree.findNode(new TestPoint2D(-1, -1)));
Assert.assertSame(minusY, tree.findNode(new TestPoint2D(0, -1)));
Assert.assertSame(minusY, tree.findNode(new TestPoint2D(1, -1)));
Assert.assertSame(underDiagonal, tree.findNode(new TestPoint2D(0.5, 0.5)));
Assert.assertSame(aboveDiagonal, tree.findNode(new TestPoint2D(3, 3)));
}
@Test
public void testFindNode_nodeCutBehavior() {
// arrange
TestBSPTree tree = new TestBSPTree();
tree.getRoot()
.cut(TestLine.X_AXIS)
.getMinus()
.cut(TestLine.Y_AXIS)
.getPlus()
.cut(new TestLine(0, 2, 2, 0));
TestNode root = tree.getRoot();
TestNode minusY = root.getPlus();
TestNode yCut = root.getMinus();
TestNode minusXPlusY = yCut.getMinus();
TestNode diagonalCut = yCut.getPlus();
TestNode underDiagonal = diagonalCut.getPlus();
TestNode aboveDiagonal = diagonalCut.getMinus();
NodeCutRule cutBehavior = NodeCutRule.NODE;
// act/assert
Assert.assertSame(root, tree.findNode(new TestPoint2D(0, 0), cutBehavior));
Assert.assertSame(root, tree.findNode(new TestPoint2D(1, 0), cutBehavior));
Assert.assertSame(diagonalCut, tree.findNode(new TestPoint2D(1, 1), cutBehavior));
Assert.assertSame(yCut, tree.findNode(new TestPoint2D(0, 1), cutBehavior));
Assert.assertSame(minusXPlusY, tree.findNode(new TestPoint2D(-1, 1), cutBehavior));
Assert.assertSame(root, tree.findNode(new TestPoint2D(-1, 0), cutBehavior));
Assert.assertSame(minusY, tree.findNode(new TestPoint2D(-1, -1), cutBehavior));
Assert.assertSame(minusY, tree.findNode(new TestPoint2D(0, -1), cutBehavior));
Assert.assertSame(minusY, tree.findNode(new TestPoint2D(1, -1), cutBehavior));
Assert.assertSame(underDiagonal, tree.findNode(new TestPoint2D(0.5, 0.5), cutBehavior));
Assert.assertSame(aboveDiagonal, tree.findNode(new TestPoint2D(3, 3), cutBehavior));
}
@Test
public void testFindNode_minusCutBehavior() {
// arrange
TestBSPTree tree = new TestBSPTree();
tree.getRoot()
.cut(TestLine.X_AXIS)
.getMinus()
.cut(TestLine.Y_AXIS)
.getPlus()
.cut(new TestLine(0, 2, 2, 0));
TestNode root = tree.getRoot();
TestNode minusY = root.getPlus();
TestNode yCut = root.getMinus();
TestNode minusXPlusY = yCut.getMinus();
TestNode diagonalCut = yCut.getPlus();
TestNode underDiagonal = diagonalCut.getPlus();
TestNode aboveDiagonal = diagonalCut.getMinus();
NodeCutRule cutBehavior = NodeCutRule.MINUS;
// act/assert
Assert.assertSame(minusXPlusY, tree.findNode(new TestPoint2D(0, 0), cutBehavior));
Assert.assertSame(underDiagonal, tree.findNode(new TestPoint2D(1, 0), cutBehavior));
Assert.assertSame(aboveDiagonal, tree.findNode(new TestPoint2D(1, 1), cutBehavior));
Assert.assertSame(minusXPlusY, tree.findNode(new TestPoint2D(0, 1), cutBehavior));
Assert.assertSame(minusXPlusY, tree.findNode(new TestPoint2D(-1, 1), cutBehavior));
Assert.assertSame(minusXPlusY, tree.findNode(new TestPoint2D(-1, 0), cutBehavior));
Assert.assertSame(minusY, tree.findNode(new TestPoint2D(-1, -1), cutBehavior));
Assert.assertSame(minusY, tree.findNode(new TestPoint2D(0, -1), cutBehavior));
Assert.assertSame(minusY, tree.findNode(new TestPoint2D(1, -1), cutBehavior));
Assert.assertSame(underDiagonal, tree.findNode(new TestPoint2D(0.5, 0.5), cutBehavior));
Assert.assertSame(aboveDiagonal, tree.findNode(new TestPoint2D(3, 3), cutBehavior));
}
@Test
public void testFindNode_plusCutBehavior() {
// arrange
TestBSPTree tree = new TestBSPTree();
tree.getRoot()
.cut(TestLine.X_AXIS)
.getMinus()
.cut(TestLine.Y_AXIS)
.getPlus()
.cut(new TestLine(0, 2, 2, 0));
TestNode root = tree.getRoot();
TestNode minusY = root.getPlus();
TestNode yCut = root.getMinus();
TestNode minusXPlusY = yCut.getMinus();
TestNode diagonalCut = yCut.getPlus();
TestNode underDiagonal = diagonalCut.getPlus();
TestNode aboveDiagonal = diagonalCut.getMinus();
NodeCutRule cutBehavior = NodeCutRule.PLUS;
// act/assert
Assert.assertSame(minusY, tree.findNode(new TestPoint2D(0, 0), cutBehavior));
Assert.assertSame(minusY, tree.findNode(new TestPoint2D(1, 0), cutBehavior));
Assert.assertSame(underDiagonal, tree.findNode(new TestPoint2D(1, 1), cutBehavior));
Assert.assertSame(underDiagonal, tree.findNode(new TestPoint2D(0, 1), cutBehavior));
Assert.assertSame(minusXPlusY, tree.findNode(new TestPoint2D(-1, 1), cutBehavior));
Assert.assertSame(minusY, tree.findNode(new TestPoint2D(-1, 0), cutBehavior));
Assert.assertSame(minusY, tree.findNode(new TestPoint2D(-1, -1), cutBehavior));
Assert.assertSame(minusY, tree.findNode(new TestPoint2D(0, -1), cutBehavior));
Assert.assertSame(minusY, tree.findNode(new TestPoint2D(1, -1), cutBehavior));
Assert.assertSame(underDiagonal, tree.findNode(new TestPoint2D(0.5, 0.5), cutBehavior));
Assert.assertSame(aboveDiagonal, tree.findNode(new TestPoint2D(3, 3), cutBehavior));
}
@Test
public void testInsert_convex_emptyTree() {
// arrange
TestBSPTree tree = new TestBSPTree();
// act
tree.insert(new TestLineSegment(1, 0, 1, 1));
// assert
TestNode root = tree.getRoot();
Assert.assertFalse(root.isLeaf());
Assert.assertTrue(root.getMinus().isLeaf());
Assert.assertTrue(root.getPlus().isLeaf());
TestLineSegment seg = (TestLineSegment) root.getCut();
PartitionTestUtils.assertPointsEqual(
new TestPoint2D(1, Double.NEGATIVE_INFINITY),
seg.getStartPoint());
PartitionTestUtils.assertPointsEqual(
new TestPoint2D(1, Double.POSITIVE_INFINITY),
seg.getEndPoint());
}
@Test
public void testInsert_convex_noSplit() {
// arrange
TestBSPTree tree = new TestBSPTree();
tree.getRoot()
.cut(TestLine.X_AXIS)
.getMinus()
.cut(TestLine.Y_AXIS);
// act
tree.insert(new TestLineSegment(0.5, 1.5, 1.5, 0.5));
// assert
TestNode root = tree.getRoot();
Assert.assertFalse(root.isLeaf());
TestNode node = tree.findNode(new TestPoint2D(0.5, 0.5));
TestLineSegment seg = (TestLineSegment) node.getParent().getCut();
PartitionTestUtils.assertPointsEqual(new TestPoint2D(0, 2), seg.getStartPoint());
PartitionTestUtils.assertPointsEqual(new TestPoint2D(2, 0), seg.getEndPoint());
Assert.assertTrue(tree.getRoot().getPlus().isLeaf());
Assert.assertTrue(tree.getRoot().getMinus().getMinus().isLeaf());
}
@Test
public void testInsert_convex_split() {
// arrange
TestBSPTree tree = new TestBSPTree();
tree.getRoot()
.cut(TestLine.X_AXIS)
.getMinus()
.cut(TestLine.Y_AXIS);
// act
tree.insert(new TestLineSegment(-0.5, 2.5, 2.5, -0.5));
// assert
TestNode root = tree.getRoot();
Assert.assertFalse(root.isLeaf());
TestNode plusXPlusY = tree.getRoot().getMinus().getPlus();
TestLineSegment plusXPlusYSeg = (TestLineSegment) plusXPlusY.getCut();
PartitionTestUtils.assertPointsEqual(new TestPoint2D(0, 2), plusXPlusYSeg.getStartPoint());
PartitionTestUtils.assertPointsEqual(new TestPoint2D(2, 0), plusXPlusYSeg.getEndPoint());
TestNode minusY = tree.getRoot().getPlus();
TestLineSegment minusYSeg = (TestLineSegment) minusY.getCut();
PartitionTestUtils.assertPointsEqual(new TestPoint2D(2, 0), minusYSeg.getStartPoint());
PartitionTestUtils.assertPointsEqual(
new TestPoint2D(Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY), minusYSeg.getEndPoint());
TestNode minusXPlusY = tree.getRoot().getMinus().getMinus();
TestLineSegment minusXPlusYSeg = (TestLineSegment) minusXPlusY.getCut();
PartitionTestUtils.assertPointsEqual(
new TestPoint2D(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY), minusXPlusYSeg.getStartPoint());
PartitionTestUtils.assertPointsEqual(new TestPoint2D(0, 2), minusXPlusYSeg.getEndPoint());
}
@Test
public void testInsert_convexList_convexRegion() {
// arrange
TestBSPTree tree = new TestBSPTree();
TestLineSegment a = new TestLineSegment(0, 0, 1, 0);
TestLineSegment b = new TestLineSegment(1, 0, 0, 1);
TestLineSegment c = new TestLineSegment(0, 1, 0, 0);
// act
tree.insert(Arrays.asList(a, b, c));
// assert
List<TestLineSegment> segments = getLineSegments(tree);
Assert.assertEquals(3, segments.size());
PartitionTestUtils.assertSegmentsEqual(
new TestLineSegment(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, TestLine.X_AXIS),
segments.get(0));
PartitionTestUtils.assertSegmentsEqual(
new TestLineSegment(-Math.sqrt(0.5), Double.POSITIVE_INFINITY, new TestLine(1, 0, 0, 1)),
segments.get(1));
PartitionTestUtils.assertSegmentsEqual(c, segments.get(2));
}
@Test
public void testInsert_convexList_concaveRegion() {
// arrange
TestBSPTree tree = new TestBSPTree();
TestLineSegment a = new TestLineSegment(-1, -1, 1, -1);
TestLineSegment b = new TestLineSegment(1, -1, 0, 0);
TestLineSegment c = new TestLineSegment(0, 0, 1, 1);
TestLineSegment d = new TestLineSegment(1, 1, -1, 1);
TestLineSegment e = new TestLineSegment(-1, 1, -1, -1);
// act
tree.insert(Arrays.asList(a, b, c, d, e));
// assert
List<TestLineSegment> segments = getLineSegments(tree);
Assert.assertEquals(5, segments.size());
PartitionTestUtils.assertSegmentsEqual(
new TestLineSegment(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, new TestLine(-1, -1, 1, -1)),
segments.get(0));
PartitionTestUtils.assertSegmentsEqual(
new TestLineSegment(-Math.sqrt(2), Double.POSITIVE_INFINITY, new TestLine(1, -1, 0, 0)),
segments.get(1));
PartitionTestUtils.assertSegmentsEqual(
new TestLineSegment(-1, 1, -1, -1),
segments.get(2));
PartitionTestUtils.assertSegmentsEqual(
new TestLineSegment(0, Double.POSITIVE_INFINITY, new TestLine(0, 0, 1, 1)),
segments.get(3));
PartitionTestUtils.assertSegmentsEqual(
new TestLineSegment(1, 1, -1, 1),
segments.get(4));
}
@Test
public void testInsert_subhyperplane_concaveRegion() {
// arrange
TestBSPTree tree = new TestBSPTree();
TestLineSegment a = new TestLineSegment(-1, -1, 1, -1);
TestLineSegment b = new TestLineSegment(1, -1, 0, 0);
TestLineSegment c = new TestLineSegment(0, 0, 1, 1);
TestLineSegment d = new TestLineSegment(1, 1, -1, 1);
TestLineSegment e = new TestLineSegment(-1, 1, -1, -1);
TestLineSegmentCollection coll = new TestLineSegmentCollection(
Arrays.asList(a, b, c, d, e));
// act
tree.insert(coll);
// assert
List<TestLineSegment> segments = getLineSegments(tree);
Assert.assertEquals(5, segments.size());
PartitionTestUtils.assertSegmentsEqual(
new TestLineSegment(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, new TestLine(-1, -1, 1, -1)),
segments.get(0));
PartitionTestUtils.assertSegmentsEqual(
new TestLineSegment(-Math.sqrt(2), Double.POSITIVE_INFINITY, new TestLine(1, -1, 0, 0)),
segments.get(1));
PartitionTestUtils.assertSegmentsEqual(
new TestLineSegment(-1, 1, -1, -1),
segments.get(2));
PartitionTestUtils.assertSegmentsEqual(
new TestLineSegment(0, Double.POSITIVE_INFINITY, new TestLine(0, 0, 1, 1)),
segments.get(3));
PartitionTestUtils.assertSegmentsEqual(
new TestLineSegment(1, 1, -1, 1),
segments.get(4));
}
@Test
public void testInsert_boundarySource() {
// arrange
TestBSPTree tree = new TestBSPTree();
TestLineSegment a = new TestLineSegment(-1, -1, 1, -1);
TestLineSegment b = new TestLineSegment(1, -1, 0, 0);
TestLineSegment c = new TestLineSegment(0, 0, 1, 1);
TestLineSegment d = new TestLineSegment(1, 1, -1, 1);
TestLineSegment e = new TestLineSegment(-1, 1, -1, -1);
BoundarySource<TestLineSegment> src = () -> Arrays.asList(a, b, c, d, e).stream();
// act
tree.insert(src);
// assert
List<TestLineSegment> segments = getLineSegments(tree);
Assert.assertEquals(5, segments.size());
PartitionTestUtils.assertSegmentsEqual(
new TestLineSegment(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, new TestLine(-1, -1, 1, -1)),
segments.get(0));
PartitionTestUtils.assertSegmentsEqual(
new TestLineSegment(-Math.sqrt(2), Double.POSITIVE_INFINITY, new TestLine(1, -1, 0, 0)),
segments.get(1));
PartitionTestUtils.assertSegmentsEqual(
new TestLineSegment(-1, 1, -1, -1),
segments.get(2));
PartitionTestUtils.assertSegmentsEqual(
new TestLineSegment(0, Double.POSITIVE_INFINITY, new TestLine(0, 0, 1, 1)),
segments.get(3));
PartitionTestUtils.assertSegmentsEqual(
new TestLineSegment(1, 1, -1, 1),
segments.get(4));
}
@Test
public void testInsert_boundarySource_emptySource() {
// arrange
TestBSPTree tree = new TestBSPTree();
BoundarySource<TestLineSegment> src = () -> new ArrayList<TestLineSegment>().stream();
// act
tree.insert(src);
// assert
Assert.assertEquals(1, tree.count());
}
@Test
public void testCount() {
// arrange
TestBSPTree tree = new TestBSPTree();
// act/assert
Assert.assertEquals(1, tree.count());
Assert.assertEquals(1, tree.getRoot().count());
tree.getRoot().insertCut(TestLine.X_AXIS);
Assert.assertEquals(1, tree.getRoot().getMinus().count());
Assert.assertEquals(1, tree.getRoot().getPlus().count());
Assert.assertEquals(3, tree.count());
tree.getRoot().getPlus().insertCut(TestLine.Y_AXIS);
Assert.assertEquals(1, tree.getRoot().getMinus().count());
Assert.assertEquals(3, tree.getRoot().getPlus().count());
Assert.assertEquals(5, tree.count());
tree.getRoot().getMinus().insertCut(TestLine.Y_AXIS);
Assert.assertEquals(3, tree.getRoot().getMinus().count());
Assert.assertEquals(3, tree.getRoot().getPlus().count());
Assert.assertEquals(7, tree.count());
tree.getRoot().getMinus().insertCut(new TestLine(new TestPoint2D(-1, -1), new TestPoint2D(1, -1)));
Assert.assertEquals(1, tree.getRoot().getMinus().count());
Assert.assertEquals(3, tree.getRoot().getPlus().count());
Assert.assertEquals(5, tree.count());
}
@Test
public void testHeight() {
// arrange
TestBSPTree tree = new TestBSPTree();
// act/assert
Assert.assertEquals(0, tree.height());
Assert.assertEquals(0, tree.getRoot().height());
tree.getRoot().insertCut(TestLine.X_AXIS);
Assert.assertEquals(0, tree.getRoot().getMinus().height());
Assert.assertEquals(0, tree.getRoot().getPlus().height());
Assert.assertEquals(1, tree.height());
tree.getRoot().getPlus().insertCut(TestLine.Y_AXIS);
Assert.assertEquals(0, tree.getRoot().getMinus().height());
Assert.assertEquals(1, tree.getRoot().getPlus().height());
Assert.assertEquals(2, tree.height());
tree.getRoot().getMinus().insertCut(TestLine.Y_AXIS);
Assert.assertEquals(1, tree.getRoot().getMinus().height());
Assert.assertEquals(1, tree.getRoot().getPlus().height());
Assert.assertEquals(2, tree.height());
tree.getRoot().getMinus().clearCut();
Assert.assertEquals(0, tree.getRoot().getMinus().height());
Assert.assertEquals(1, tree.getRoot().getPlus().height());
Assert.assertEquals(2, tree.height());
tree.getRoot().getPlus().getPlus()
.insertCut(new TestLine(new TestPoint2D(0, -1), new TestPoint2D(1, -1)));
Assert.assertEquals(0, tree.getRoot().getMinus().height());
Assert.assertEquals(2, tree.getRoot().getPlus().height());
Assert.assertEquals(3, tree.height());
}
@Test
public void testDepth() {
// arrange
TestBSPTree tree = new TestBSPTree();
TestNode root = tree.getRoot();
root.cut(TestLine.X_AXIS)
.getMinus()
.cut(TestLine.Y_AXIS);
// act/assert
Assert.assertEquals(0, root.depth());
Assert.assertEquals(1, root.getPlus().depth());
Assert.assertEquals(1, root.getMinus().depth());
Assert.assertEquals(2, root.getMinus().getPlus().depth());
Assert.assertEquals(2, root.getMinus().getMinus().depth());
}
@Test
public void testVisit_defaultOrder() {
// arrange
TestBSPTree tree = new TestBSPTree();
tree.getRoot().cut(TestLine.X_AXIS)
.getMinus().cut(TestLine.Y_AXIS);
TestNode root = tree.getRoot();
TestNode minus = root.getMinus();
TestNode plus = root.getPlus();
TestNode minusMinus = minus.getMinus();
TestNode minusPlus = minus.getPlus();
List<TestNode> nodes = new ArrayList<>();
// act
tree.accept(node -> {
nodes.add(node);
return BSPTreeVisitor.Result.CONTINUE;
});
// assert
Assert.assertEquals(
Arrays.asList(root, minus, minusMinus, minusPlus, plus),
nodes);
}
@Test
public void testVisit_specifiedOrder() {
// arrange
TestBSPTree tree = new TestBSPTree();
tree.getRoot().cut(TestLine.X_AXIS)
.getMinus().cut(TestLine.Y_AXIS);
TestNode root = tree.getRoot();
TestNode minus = root.getMinus();
TestNode plus = root.getPlus();
TestNode minusMinus = minus.getMinus();
TestNode minusPlus = minus.getPlus();
// act/assert
TestVisitor plusMinusNode = new TestVisitor(BSPTreeVisitor.Order.PLUS_MINUS_NODE);
tree.accept(plusMinusNode);
Assert.assertEquals(
Arrays.asList(plus, minusPlus, minusMinus, minus, root),
plusMinusNode.getVisited());
TestVisitor plusNodeMinus = new TestVisitor(BSPTreeVisitor.Order.PLUS_NODE_MINUS);
tree.accept(plusNodeMinus);
Assert.assertEquals(
Arrays.asList(plus, root, minusPlus, minus, minusMinus),
plusNodeMinus.getVisited());
TestVisitor minusPlusNode = new TestVisitor(BSPTreeVisitor.Order.MINUS_PLUS_NODE);
tree.accept(minusPlusNode);
Assert.assertEquals(
Arrays.asList(minusMinus, minusPlus, minus, plus, root),
minusPlusNode.getVisited());
TestVisitor minusNodePlus = new TestVisitor(BSPTreeVisitor.Order.MINUS_NODE_PLUS);
tree.accept(minusNodePlus);
Assert.assertEquals(
Arrays.asList(minusMinus, minus, minusPlus, root, plus),
minusNodePlus.getVisited());
TestVisitor nodeMinusPlus = new TestVisitor(BSPTreeVisitor.Order.NODE_MINUS_PLUS);
tree.accept(nodeMinusPlus);
Assert.assertEquals(
Arrays.asList(root, minus, minusMinus, minusPlus, plus),
nodeMinusPlus.getVisited());
TestVisitor nodePlusMinus = new TestVisitor(BSPTreeVisitor.Order.NODE_PLUS_MINUS);
tree.accept(nodePlusMinus);
Assert.assertEquals(
Arrays.asList(root, plus, minus, minusPlus, minusMinus),
nodePlusMinus.getVisited());
}
@Test
public void testVisit_nullVisitOrderSkipsSubtree() {
// arrange
TestBSPTree tree = new TestBSPTree();
tree.getRoot().cut(TestLine.X_AXIS)
.getMinus().cut(TestLine.Y_AXIS);
TestNode root = tree.getRoot();
TestNode plus = root.getPlus();
TestNode minus = root.getMinus();
TestVisitor visitor = new TestVisitor(BSPTreeVisitor.Order.NODE_MINUS_PLUS) {
@Override
public Order visitOrder(TestNode node) {
if (node == minus) {
return null;
}
return super.visitOrder(node);
}
};
// act
tree.accept(visitor);
// assert
Assert.assertEquals(
Arrays.asList(root, plus),
visitor.getVisited());
}
@Test
public void testVisit_noneVisitOrderSkipsSubtree() {
// arrange
TestBSPTree tree = new TestBSPTree();
tree.getRoot().cut(TestLine.X_AXIS)
.getMinus().cut(TestLine.Y_AXIS);
TestNode root = tree.getRoot();
TestNode plus = root.getPlus();
TestNode minus = root.getMinus();
TestVisitor visitor = new TestVisitor(BSPTreeVisitor.Order.NODE_MINUS_PLUS) {
@Override
public Order visitOrder(TestNode node) {
if (node == minus) {
return Order.NONE;
}
return super.visitOrder(node);
}
};
// act
tree.accept(visitor);
// assert
Assert.assertEquals(
Arrays.asList(root, plus),
visitor.getVisited());
}
@Test
public void testVisit_visitorReturnsNull_terminatesEarly() {
// arrange
TestBSPTree tree = new TestBSPTree();
tree.getRoot().cut(TestLine.X_AXIS)
.getMinus().cut(TestLine.Y_AXIS);
TestNode root = tree.getRoot();
TestNode minus = root.getMinus();
TestNode minusMinus = minus.getMinus();
TestNode minusPlus = minus.getPlus();
TestVisitor visitor = new TestVisitor(BSPTreeVisitor.Order.MINUS_PLUS_NODE) {
@Override
public Result visit(TestNode node) {
super.visit(node);
if (node == minus) {
return null;
}
return Result.CONTINUE;
}
};
// act
tree.accept(visitor);
// assert
Assert.assertEquals(
Arrays.asList(minusMinus, minusPlus, minus),
visitor.getVisited());
}
@Test
public void testVisit_visitorReturnsTerminate_terminatesEarly() {
// arrange
TestBSPTree tree = new TestBSPTree();
tree.getRoot().cut(TestLine.X_AXIS)
.getMinus().cut(TestLine.Y_AXIS);
TestNode root = tree.getRoot();
TestNode minus = root.getMinus();
TestNode minusMinus = minus.getMinus();
TestNode minusPlus = minus.getPlus();
TestVisitor visitor = new TestVisitor(BSPTreeVisitor.Order.MINUS_PLUS_NODE) {
@Override
public Result visit(TestNode node) {
super.visit(node);
if (node == minus) {
return Result.TERMINATE;
}
return Result.CONTINUE;
}
};
// act
tree.accept(visitor);
// assert
Assert.assertEquals(
Arrays.asList(minusMinus, minusPlus, minus),
visitor.getVisited());
}
@Test
public void testVisit_earlyTerminationPermutations() {
// arrange
TestBSPTree tree = new TestBSPTree();
tree.getRoot().cut(TestLine.X_AXIS)
.getMinus().cut(TestLine.Y_AXIS);
TestNode root = tree.getRoot();
TestNode minus = root.getMinus();
TestNode plus = root.getPlus();
TestNode minusMinus = minus.getMinus();
TestNode minusPlus = minus.getPlus();
// act/assert
TestVisitor plusMinusNode = new TestVisitor(BSPTreeVisitor.Order.PLUS_MINUS_NODE).withTerminationNode(minus);
tree.accept(plusMinusNode);
Assert.assertEquals(
Arrays.asList(plus, minusPlus, minusMinus, minus),
plusMinusNode.getVisited());
TestVisitor plusNodeMinus = new TestVisitor(BSPTreeVisitor.Order.PLUS_NODE_MINUS).withTerminationNode(minus);
tree.accept(plusNodeMinus);
Assert.assertEquals(
Arrays.asList(plus, root, minusPlus, minus),
plusNodeMinus.getVisited());
TestVisitor minusPlusNode = new TestVisitor(BSPTreeVisitor.Order.MINUS_PLUS_NODE).withTerminationNode(minus);
tree.accept(minusPlusNode);
Assert.assertEquals(
Arrays.asList(minusMinus, minusPlus, minus),
minusPlusNode.getVisited());
TestVisitor minusNodePlus = new TestVisitor(BSPTreeVisitor.Order.MINUS_NODE_PLUS).withTerminationNode(minus);
tree.accept(minusNodePlus);
Assert.assertEquals(
Arrays.asList(minusMinus, minus),
minusNodePlus.getVisited());
TestVisitor nodeMinusPlus = new TestVisitor(BSPTreeVisitor.Order.NODE_MINUS_PLUS).withTerminationNode(minus);
tree.accept(nodeMinusPlus);
Assert.assertEquals(
Arrays.asList(root, minus),
nodeMinusPlus.getVisited());
TestVisitor nodePlusMinus = new TestVisitor(BSPTreeVisitor.Order.NODE_PLUS_MINUS).withTerminationNode(minus);
tree.accept(nodePlusMinus);
Assert.assertEquals(
Arrays.asList(root, plus, minus),
nodePlusMinus.getVisited());
}
@Test
public void testVisit_visitNode() {
// arrange
TestBSPTree tree = new TestBSPTree();
tree.getRoot().cut(TestLine.X_AXIS)
.getMinus().cut(TestLine.Y_AXIS);
TestNode root = tree.getRoot();
TestNode minus = root.getMinus();
TestNode minusMinus = minus.getMinus();
TestNode minusPlus = minus.getPlus();
List<TestNode> nodes = new ArrayList<>();
// act
minus.accept(node -> {
nodes.add(node);
return BSPTreeVisitor.Result.CONTINUE;
});
// assert
Assert.assertEquals(
Arrays.asList(minus, minusMinus, minusPlus),
nodes);
}
@Test
public void testNodesIterable_emptyTree() {
// arrange
TestBSPTree tree = new TestBSPTree();
List<TestNode> nodes = new ArrayList<>();
// act
for (TestNode node : tree.nodes()) {
nodes.add(node);
}
// assert
Assert.assertEquals(1, nodes.size());
Assert.assertSame(tree.getRoot(), nodes.get(0));
}
@Test
public void testNodesIterable_multipleNodes() {
// arrange
TestBSPTree tree = new TestBSPTree();
TestNode root = tree.getRoot();
root.cut(TestLine.X_AXIS)
.getMinus()
.cut(TestLine.Y_AXIS)
.getParent()
.getPlus()
.cut(TestLine.Y_AXIS);
List<TestNode> nodes = new ArrayList<>();
// act
for (TestNode node : tree.nodes()) {
nodes.add(node);
}
// assert
Assert.assertEquals(7, nodes.size());
Assert.assertSame(root, nodes.get(0));
Assert.assertSame(root.getMinus(), nodes.get(1));
Assert.assertSame(root.getMinus().getMinus(), nodes.get(2));
Assert.assertSame(root.getMinus().getPlus(), nodes.get(3));
Assert.assertSame(root.getPlus(), nodes.get(4));
Assert.assertSame(root.getPlus().getMinus(), nodes.get(5));
Assert.assertSame(root.getPlus().getPlus(), nodes.get(6));
}
@Test
public void testNodesIterable_iteratorThrowsNoSuchElementExceptionAtEnd() {
// arrange
TestBSPTree tree = new TestBSPTree();
Iterator<TestNode> it = tree.nodes().iterator();
it.next();
// act
try {
it.next();
Assert.fail("Operation should have thrown an exception");
} catch (NoSuchElementException exc) {
// expected
}
}
@Test
public void testSubtreeNodesIterable_singleNodeSubtree() {
// arrange
TestBSPTree tree = new TestBSPTree();
TestNode node = tree.getRoot().cut(TestLine.X_AXIS)
.getMinus()
.cut(TestLine.Y_AXIS)
.getMinus();
List<TestNode> nodes = new ArrayList<>();
// act
for (TestNode n : node.nodes()) {
nodes.add(n);
}
// assert
Assert.assertEquals(1, nodes.size());
Assert.assertSame(node, nodes.get(0));
}
@Test
public void testSubtreeNodesIterable_multipleNodeSubtree() {
// arrange
TestBSPTree tree = new TestBSPTree();
TestNode node = tree.getRoot().cut(TestLine.X_AXIS)
.getMinus()
.cut(TestLine.Y_AXIS);
List<TestNode> nodes = new ArrayList<>();
// act
for (TestNode n : node.nodes()) {
nodes.add(n);
}
// assert
Assert.assertEquals(3, nodes.size());
Assert.assertSame(node, nodes.get(0));
Assert.assertSame(node.getMinus(), nodes.get(1));
Assert.assertSame(node.getPlus(), nodes.get(2));
}
@Test
public void testCopy_rootOnly() {
// arrange
TestBSPTree tree = new TestBSPTree();
// act
TestBSPTree copy = new TestBSPTree();
copy.copy(tree);
// assert
Assert.assertNotSame(tree, copy);
Assert.assertNotSame(tree.getRoot(), copy.getRoot());
Assert.assertEquals(tree.count(), copy.count());
}
@Test
public void testCopy_withCuts() {
// arrange
TestBSPTree tree = new TestBSPTree();
tree.getRoot()
.cut(TestLine.X_AXIS)
.getMinus()
.cut(TestLine.Y_AXIS);
// act
TestBSPTree copy = new TestBSPTree();
copy.copy(tree);
// assert
Assert.assertNotSame(tree, copy);
assertNodesCopiedRecursive(tree.getRoot(), copy.getRoot());
Assert.assertEquals(tree.count(), copy.count());
}
@Test
public void testCopy_changesToOneTreeDoNotAffectCopy() {
// arrange
TestBSPTree tree = new TestBSPTree();
tree.getRoot()
.cut(TestLine.X_AXIS)
.getMinus()
.cut(TestLine.Y_AXIS);
// act
TestBSPTree copy = new TestBSPTree();
copy.copy(tree);
tree.getRoot().clearCut();
// assert
Assert.assertEquals(1, tree.count());
Assert.assertEquals(5, copy.count());
}
@Test
public void testCopy_instancePassedAsArgument() {
// arrange
TestBSPTree tree = new TestBSPTree();
tree.getRoot()
.cut(TestLine.X_AXIS)
.getMinus()
.cut(TestLine.Y_AXIS);
// act
tree.copy(tree);
// assert
Assert.assertEquals(5, tree.count());
}
@Test
public void testExtract_singleNodeTree() {
// arrange
TestBSPTree tree = new TestBSPTree();
TestBSPTree result = new TestBSPTree();
result.getRoot().insertCut(TestLine.X_AXIS);
// act
result.extract(tree.getRoot());
// assert
Assert.assertNotSame(tree.getRoot(), result.getRoot());
Assert.assertEquals(1, tree.count());
Assert.assertEquals(1, result.count());
PartitionTestUtils.assertTreeStructure(tree);
PartitionTestUtils.assertTreeStructure(result);
}
@Test
public void testExtract_clearsExistingNodesInCallingTree() {
// arrange
TestBSPTree tree = new TestBSPTree();
TestBSPTree result = new TestBSPTree();
result.getRoot().cut(TestLine.X_AXIS)
.getMinus().cut(TestLine.Y_AXIS);
// act
result.extract(tree.getRoot());
// assert
Assert.assertNotSame(tree.getRoot(), result.getRoot());
Assert.assertEquals(1, tree.count());
Assert.assertEquals(1, result.count());
PartitionTestUtils.assertTreeStructure(tree);
PartitionTestUtils.assertTreeStructure(result);
}
@Test
public void testExtract_internalNode() {
// arrange
TestBSPTree tree = new TestBSPTree();
tree.insert(Arrays.asList(
new TestLineSegment(TestPoint2D.ZERO, new TestPoint2D(1, 0)),
new TestLineSegment(new TestPoint2D(0, -1), new TestPoint2D(0, 1)),
new TestLineSegment(new TestPoint2D(1, 2), new TestPoint2D(2, 1)),
new TestLineSegment(new TestPoint2D(-1, 2), new TestPoint2D(-2, 1)),
new TestLineSegment(new TestPoint2D(0, -2), new TestPoint2D(1, -2))
));
TestBSPTree result = new TestBSPTree();
// act
result.extract(tree.getRoot().getPlus());
// assert
Assert.assertEquals(7, result.count());
List<TestLineSegment> resultSegments = getLineSegments(result);
Assert.assertEquals(3, resultSegments.size());
PartitionTestUtils.assertSegmentsEqual(
new TestLineSegment(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, TestLine.X_AXIS),
resultSegments.get(0));
PartitionTestUtils.assertSegmentsEqual(
new TestLineSegment(Double.NEGATIVE_INFINITY, 0, TestLine.Y_AXIS),
resultSegments.get(1));
PartitionTestUtils.assertSegmentsEqual(
new TestLineSegment(0, Double.POSITIVE_INFINITY, new TestLine(new TestPoint2D(0, -2), new TestPoint2D(1, -2))),
resultSegments.get(2));
Assert.assertEquals(13, tree.count());
List<TestLineSegment> inputSegment = getLineSegments(tree);
Assert.assertEquals(6, inputSegment.size());
PartitionTestUtils.assertTreeStructure(tree);
PartitionTestUtils.assertTreeStructure(result);
}
@Test
public void testExtract_leafNode() {
// arrange
TestBSPTree tree = new TestBSPTree();
tree.insert(Arrays.asList(
new TestLineSegment(TestPoint2D.ZERO, new TestPoint2D(1, 0)),
new TestLineSegment(new TestPoint2D(0, -1), new TestPoint2D(0, 1)),
new TestLineSegment(new TestPoint2D(1, 2), new TestPoint2D(2, 1)),
new TestLineSegment(new TestPoint2D(-1, 2), new TestPoint2D(-2, 1)),
new TestLineSegment(new TestPoint2D(0, -2), new TestPoint2D(1, -2))
));
TestPoint2D pt = new TestPoint2D(1, 1);
TestNode node = tree.findNode(pt);
TestBSPTree result = new TestBSPTree();
// act
result.extract(node);
// assert
TestNode resultNode = result.findNode(pt);
Assert.assertNotNull(resultNode);
Assert.assertNotSame(node, resultNode);
Assert.assertEquals(7, result.count());
List<TestLineSegment> resultSegments = getLineSegments(result);
Assert.assertEquals(3, resultSegments.size());
PartitionTestUtils.assertSegmentsEqual(
new TestLineSegment(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, TestLine.X_AXIS),
resultSegments.get(0));
PartitionTestUtils.assertSegmentsEqual(
new TestLineSegment(0, Double.POSITIVE_INFINITY, TestLine.Y_AXIS),
resultSegments.get(1));
PartitionTestUtils.assertSegmentsEqual(
new TestLineSegment(new TestPoint2D(0, 3), new TestPoint2D(3, 0)),
resultSegments.get(2));
Assert.assertEquals(13, tree.count());
List<TestLineSegment> inputSegment = getLineSegments(tree);
Assert.assertEquals(6, inputSegment.size());
PartitionTestUtils.assertTreeStructure(tree);
PartitionTestUtils.assertTreeStructure(result);
}
@Test
public void testExtract_extractFromSameTree() {
// arrange
TestBSPTree tree = new TestBSPTree();
tree.insert(Arrays.asList(
new TestLineSegment(TestPoint2D.ZERO, new TestPoint2D(1, 0)),
new TestLineSegment(new TestPoint2D(0, -1), new TestPoint2D(0, 1)),
new TestLineSegment(new TestPoint2D(1, 2), new TestPoint2D(2, 1)),
new TestLineSegment(new TestPoint2D(-1, 2), new TestPoint2D(-2, 1)),
new TestLineSegment(new TestPoint2D(0, -2), new TestPoint2D(1, -2))
));
TestPoint2D pt = new TestPoint2D(1, 1);
TestNode node = tree.findNode(pt);
// act
tree.extract(node);
// assert
TestNode resultNode = tree.findNode(pt);
Assert.assertNotNull(resultNode);
Assert.assertSame(node, resultNode);
Assert.assertEquals(7, tree.count());
List<TestLineSegment> resultSegments = getLineSegments(tree);
Assert.assertEquals(3, resultSegments.size());
PartitionTestUtils.assertSegmentsEqual(
new TestLineSegment(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, TestLine.X_AXIS),
resultSegments.get(0));
PartitionTestUtils.assertSegmentsEqual(
new TestLineSegment(0, Double.POSITIVE_INFINITY, TestLine.Y_AXIS),
resultSegments.get(1));
PartitionTestUtils.assertSegmentsEqual(
new TestLineSegment(new TestPoint2D(0, 3), new TestPoint2D(3, 0)),
resultSegments.get(2));
PartitionTestUtils.assertTreeStructure(tree);
}
@Test
public void testTransform_singleNodeTree() {
// arrange
TestBSPTree tree = new TestBSPTree();
Transform<TestPoint2D> t = new TestTransform2D(p -> new TestPoint2D(p.getX(), p.getY() + 2));
// act
tree.transform(t);
// assert
Assert.assertEquals(1, tree.count());
Assert.assertTrue(tree.getRoot().isLeaf());
}
@Test
public void testTransform_singleCut() {
// arrange
TestBSPTree tree = new TestBSPTree();
tree.getRoot().insertCut(TestLine.X_AXIS);
Transform<TestPoint2D> t = new TestTransform2D(p -> new TestPoint2D(p.getX(), p.getY() + 2));
// act
tree.transform(t);
// assert
Assert.assertEquals(3, tree.count());
List<TestLineSegment> segments = getLineSegments(tree);
Assert.assertEquals(1, segments.size());
TestLineSegment seg = segments.get(0);
PartitionTestUtils.assertPointsEqual(new TestPoint2D(Double.NEGATIVE_INFINITY, 2), seg.getStartPoint());
PartitionTestUtils.assertPointsEqual(new TestPoint2D(Double.POSITIVE_INFINITY, 2), seg.getEndPoint());
}
@Test
public void testTransform_multipleCuts() {
// arrange
TestBSPTree tree = new TestBSPTree();
tree.insert(Arrays.asList(
new TestLineSegment(new TestPoint2D(-1, 0), new TestPoint2D(1, 0)),
new TestLineSegment(new TestPoint2D(-1, -1), new TestPoint2D(1, 1)),
new TestLineSegment(new TestPoint2D(3, 1), new TestPoint2D(3, 2))
));
Transform<TestPoint2D> t = new TestTransform2D(p -> new TestPoint2D(0.5 * p.getX(), p.getY() + 2));
// act
tree.transform(t);
// assert
Assert.assertEquals(9, tree.count());
List<TestLineSegment> segments = getLineSegments(tree);
Assert.assertEquals(4, segments.size());
TestLineSegment segment1 = segments.get(0);
PartitionTestUtils.assertPointsEqual(new TestPoint2D(Double.NEGATIVE_INFINITY, 2), segment1.getStartPoint());
PartitionTestUtils.assertPointsEqual(new TestPoint2D(Double.POSITIVE_INFINITY, 2), segment1.getEndPoint());
TestLineSegment segment2 = segments.get(1);
PartitionTestUtils.assertPointsEqual(new TestPoint2D(0, 2), segment2.getStartPoint());
PartitionTestUtils.assertPointsEqual(new TestPoint2D(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY), segment2.getEndPoint());
TestLineSegment segment3 = segments.get(2);
PartitionTestUtils.assertPointsEqual(new TestPoint2D(1.5, 2), segment3.getStartPoint());
PartitionTestUtils.assertPointsEqual(new TestPoint2D(1.5, 5), segment3.getEndPoint());
TestLineSegment segment4 = segments.get(3);
PartitionTestUtils.assertPointsEqual(new TestPoint2D(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY), segment4.getStartPoint());
PartitionTestUtils.assertPointsEqual(new TestPoint2D(0, 2), segment4.getEndPoint());
}
@Test
public void testTransform_xAxisReflection() {
// arrange
TestBSPTree tree = new TestBSPTree();
tree.insert(Arrays.asList(
new TestLineSegment(new TestPoint2D(-1, 0), new TestPoint2D(1, 0)),
new TestLineSegment(new TestPoint2D(0, -1), new TestPoint2D(0, 1)),
new TestLineSegment(new TestPoint2D(0, 3), new TestPoint2D(3, 0))
));
Transform<TestPoint2D> t = new TestTransform2D(p -> new TestPoint2D(-p.getX(), p.getY()));
Map<TestPoint2D, TestNode> pointNodeMap = createPointNodeMap(tree, -5, 5);
// act
tree.transform(t);
// assert
checkTransformedPointNodeMap(tree, t, pointNodeMap);
List<TestLineSegment> segments = getLineSegments(tree);
Assert.assertEquals(4, segments.size());
}
@Test
public void testTransform_yAxisReflection() {
// arrange
TestBSPTree tree = new TestBSPTree();
tree.insert(Arrays.asList(
new TestLineSegment(new TestPoint2D(-1, 0), new TestPoint2D(1, 0)),
new TestLineSegment(new TestPoint2D(0, -1), new TestPoint2D(0, 1)),
new TestLineSegment(new TestPoint2D(0, 3), new TestPoint2D(3, 0))
));
Transform<TestPoint2D> t = new TestTransform2D(p -> new TestPoint2D(p.getX(), -p.getY()));
Map<TestPoint2D, TestNode> pointNodeMap = createPointNodeMap(tree, -5, 5);
// act
tree.transform(t);
// assert
checkTransformedPointNodeMap(tree, t, pointNodeMap);
List<TestLineSegment> segments = getLineSegments(tree);
Assert.assertEquals(4, segments.size());
}
@Test
public void testTransform_xAndYAxisReflection() {
// arrange
TestBSPTree tree = new TestBSPTree();
tree.insert(Arrays.asList(
new TestLineSegment(new TestPoint2D(-1, 0), new TestPoint2D(1, 0)),
new TestLineSegment(new TestPoint2D(0, -1), new TestPoint2D(0, 1)),
new TestLineSegment(new TestPoint2D(0, 3), new TestPoint2D(3, 0))
));
Transform<TestPoint2D> t = new TestTransform2D(p -> new TestPoint2D(-p.getX(), -p.getY()));
Map<TestPoint2D, TestNode> pointNodeMap = createPointNodeMap(tree, -5, 5);
// act
tree.transform(t);
// assert
checkTransformedPointNodeMap(tree, t, pointNodeMap);
List<TestLineSegment> segments = getLineSegments(tree);
Assert.assertEquals(4, segments.size());
}
@Test
public void testTreeString() {
// arrange
TestBSPTree tree = new TestBSPTree();
tree.getRoot().cut(TestLine.X_AXIS)
.getMinus().cut(TestLine.Y_AXIS);
// act
String str = tree.treeString();
// assert
String[] lines = str.split("\n");
Assert.assertEquals(5, lines.length);
Assert.assertTrue(lines[0].startsWith("TestNode[cut= TestLineSegment"));
Assert.assertTrue(lines[1].startsWith(" [-] TestNode[cut= TestLineSegment"));
Assert.assertEquals(" [-] TestNode[cut= null]", lines[2]);
Assert.assertEquals(" [+] TestNode[cut= null]", lines[3]);
Assert.assertEquals(" [+] TestNode[cut= null]", lines[4]);
}
@Test
public void testTreeString_emptyTree() {
// arrange
TestBSPTree tree = new TestBSPTree();
// act
String str = tree.treeString();
// assert
Assert.assertEquals("TestNode[cut= null]", str);
}
@Test
public void testTreeString_reachesMaxDepth() {
// arrange
TestBSPTree tree = new TestBSPTree();
tree.getRoot().cut(TestLine.X_AXIS)
.getMinus().cut(TestLine.Y_AXIS)
.getMinus().cut(new TestLine(new TestPoint2D(-2, 1), new TestPoint2D(0, 1)));
// act
String str = tree.treeString(1);
// assert
String[] lines = str.split("\n");
Assert.assertEquals(4, lines.length);
Assert.assertTrue(lines[0].startsWith("TestNode[cut= TestLineSegment"));
Assert.assertTrue(lines[1].startsWith(" [-] TestNode[cut= TestLineSegment"));
Assert.assertEquals(" ...", lines[2]);
Assert.assertEquals(" [+] TestNode[cut= null]", lines[3]);
}
@Test
public void testTreeString_zeroMaxDepth() {
// arrange
TestBSPTree tree = new TestBSPTree();
tree.getRoot().cut(TestLine.X_AXIS)
.getMinus().cut(TestLine.Y_AXIS)
.getMinus().cut(new TestLine(new TestPoint2D(-2, 1), new TestPoint2D(0, 1)));
// act
String str = tree.treeString(0);
// assert
String[] lines = str.split("\n");
Assert.assertEquals(2, lines.length);
Assert.assertTrue(lines[0].startsWith("TestNode[cut= TestLineSegment"));
Assert.assertTrue(lines[1].startsWith(" ..."));
}
@Test
public void testTreeString_negativeMaxDepth() {
// arrange
TestBSPTree tree = new TestBSPTree();
tree.getRoot().cut(TestLine.X_AXIS)
.getMinus().cut(TestLine.Y_AXIS)
.getMinus().cut(new TestLine(new TestPoint2D(-2, 1), new TestPoint2D(0, 1)));
// act
String str = tree.treeString(-1);
// assert
Assert.assertEquals("", str);
}
@Test
public void testToString() {
// arrange
TestBSPTree tree = new TestBSPTree();
tree.getRoot().insertCut(TestLine.Y_AXIS);
// act
String str = tree.toString();
// assert
String msg = "Unexpected toString() representation: " + str;
Assert.assertTrue(msg, str.contains("TestBSPTree"));
Assert.assertTrue(msg, str.contains("count= 3"));
Assert.assertTrue(msg, str.contains("height= 1"));
}
@Test
public void testNodeToString() {
// arrange
TestBSPTree tree = new TestBSPTree();
tree.getRoot().cut(TestLine.X_AXIS);
// act
String str = tree.getRoot().toString();
// assert
Assert.assertTrue(str.contains("TestNode"));
Assert.assertTrue(str.contains("cut= TestLineSegment"));
}
@Test
public void testSplitIntoTree() {
// arrange
TestBSPTree tree = new TestBSPTree();
tree.getRoot()
.cut(TestLine.X_AXIS)
.getMinus()
.cut(TestLine.Y_AXIS);
TestBSPTree minus = new TestBSPTree();
TestBSPTree plus = new TestBSPTree();
TestLine splitter = new TestLine(new TestPoint2D(0, 0), new TestPoint2D(-1, 1));
// act
tree.splitIntoTrees(splitter, minus, plus);
// assert
TestLineSegment splitSegment = new TestLineSegment(Double.NEGATIVE_INFINITY,
Double.POSITIVE_INFINITY, splitter);
Assert.assertEquals(5, tree.count());
Assert.assertEquals(2, tree.height());
Assert.assertEquals(5, minus.count());
Assert.assertEquals(2, minus.height());
List<TestLineSegment> minusSegments = getLineSegments(minus);
Assert.assertEquals(2, minusSegments.size());
PartitionTestUtils.assertSegmentsEqual(splitSegment, minusSegments.get(0));
PartitionTestUtils.assertSegmentsEqual(new TestLineSegment(Double.NEGATIVE_INFINITY, 0, TestLine.X_AXIS),
minusSegments.get(1));
Assert.assertEquals(7, plus.count());
Assert.assertEquals(3, plus.height());
List<TestLineSegment> plusSegments = getLineSegments(plus);
Assert.assertEquals(3, plusSegments.size());
PartitionTestUtils.assertSegmentsEqual(splitSegment, plusSegments.get(0));
PartitionTestUtils.assertSegmentsEqual(new TestLineSegment(0, Double.POSITIVE_INFINITY, TestLine.X_AXIS),
plusSegments.get(1));
PartitionTestUtils.assertSegmentsEqual(new TestLineSegment(0, Double.POSITIVE_INFINITY, TestLine.Y_AXIS),
plusSegments.get(2));
}
@Test
public void testSplitIntoTree_minusOnly() {
// arrange
TestBSPTree tree = new TestBSPTree();
tree.getRoot()
.cut(TestLine.X_AXIS)
.getMinus()
.cut(TestLine.Y_AXIS);
TestBSPTree minus = new TestBSPTree();
TestLine splitter = new TestLine(new TestPoint2D(0, 0), new TestPoint2D(-1, 1));
// act
tree.splitIntoTrees(splitter, minus, null);
// assert
TestLineSegment splitSegment = new TestLineSegment(Double.NEGATIVE_INFINITY,
Double.POSITIVE_INFINITY, splitter);
Assert.assertEquals(5, tree.count());
Assert.assertEquals(2, tree.height());
Assert.assertEquals(5, minus.count());
Assert.assertEquals(2, minus.height());
List<TestLineSegment> minusSegments = getLineSegments(minus);
Assert.assertEquals(2, minusSegments.size());
PartitionTestUtils.assertSegmentsEqual(splitSegment, minusSegments.get(0));
PartitionTestUtils.assertSegmentsEqual(new TestLineSegment(Double.NEGATIVE_INFINITY, 0, TestLine.X_AXIS),
minusSegments.get(1));
}
@Test
public void testSplitIntoTree_plusOnly() {
// arrange
TestBSPTree tree = new TestBSPTree();
tree.getRoot()
.cut(TestLine.X_AXIS)
.getMinus()
.cut(TestLine.Y_AXIS);
TestBSPTree plus = new TestBSPTree();
TestLine splitter = new TestLine(new TestPoint2D(0, 0), new TestPoint2D(-1, 1));
// act
tree.splitIntoTrees(splitter, null, plus);
// assert
TestLineSegment splitSegment = new TestLineSegment(Double.NEGATIVE_INFINITY,
Double.POSITIVE_INFINITY, splitter);
Assert.assertEquals(5, tree.count());
Assert.assertEquals(2, tree.height());
Assert.assertEquals(7, plus.count());
Assert.assertEquals(3, plus.height());
List<TestLineSegment> plusSegments = getLineSegments(plus);
Assert.assertEquals(3, plusSegments.size());
PartitionTestUtils.assertSegmentsEqual(splitSegment, plusSegments.get(0));
PartitionTestUtils.assertSegmentsEqual(new TestLineSegment(0, Double.POSITIVE_INFINITY, TestLine.X_AXIS),
plusSegments.get(1));
PartitionTestUtils.assertSegmentsEqual(new TestLineSegment(0, Double.POSITIVE_INFINITY, TestLine.Y_AXIS),
plusSegments.get(2));
}
private void assertNodesCopiedRecursive(final TestNode orig, final TestNode copy) {
Assert.assertNotSame(orig, copy);
Assert.assertEquals(orig.getCut(), copy.getCut());
if (!orig.isLeaf()) {
Assert.assertNotSame(orig.getMinus(), copy.getMinus());
Assert.assertNotSame(orig.getPlus(), copy.getPlus());
assertNodesCopiedRecursive(orig.getMinus(), copy.getMinus());
assertNodesCopiedRecursive(orig.getPlus(), copy.getPlus());
} else {
Assert.assertNull(copy.getMinus());
Assert.assertNull(copy.getPlus());
}
Assert.assertEquals(orig.depth(), copy.depth());
Assert.assertEquals(orig.count(), copy.count());
}
private static List<TestLineSegment> getLineSegments(TestBSPTree tree) {
return StreamSupport.stream(tree.nodes().spliterator(), false)
.filter(BSPTree.Node::isInternal)
.map(n -> (TestLineSegment) n.getCut())
.collect(Collectors.toList());
}
/** Create a map of points to the nodes that they resolve to in the
* given tree.
*/
private static Map<TestPoint2D, TestNode> createPointNodeMap(TestBSPTree tree, int min, int max) {
Map<TestPoint2D, TestNode> map = new HashMap<>();
for (int x = min; x <= max; ++x) {
for (int y = min; y <= max; ++y) {
TestPoint2D pt = new TestPoint2D(x, y);
TestNode node = tree.findNode(pt, NodeCutRule.NODE);
map.put(pt, node);
}
}
return map;
}
/** Check that transformed points resolve to the same tree nodes that were found when the original
* points were resolved in the untransformed tree.
* @param transformed
* @param transform
* @param pointNodeMap
*/
private static void checkTransformedPointNodeMap(TestBSPTree transformedTree, Transform<TestPoint2D> transform,
Map<TestPoint2D, TestNode> pointNodeMap) {
for (TestPoint2D pt : pointNodeMap.keySet()) {
TestNode expectedNode = pointNodeMap.get(pt);
TestPoint2D transformedPt = transform.apply(pt);
String msg = "Expected transformed point " + transformedPt + " to resolve to node " + expectedNode;
Assert.assertSame(msg, expectedNode, transformedTree.findNode(transformedPt, NodeCutRule.NODE));
}
}
private static class TestVisitor implements BSPTreeVisitor<TestPoint2D, TestNode> {
private final Order order;
private TestNode terminationNode;
private final List<TestNode> visited = new ArrayList<>();
TestVisitor(Order order) {
this.order = order;
}
public TestVisitor withTerminationNode(TestNode newTerminationNode) {
this.terminationNode = newTerminationNode;
return this;
}
@Override
public Result visit(TestNode node) {
visited.add(node);
return (terminationNode == node) ?
Result.TERMINATE :
Result.CONTINUE;
}
@Override
public Order visitOrder(TestNode node) {
return order;
}
public List<TestNode> getVisited() {
return visited;
}
}
}