/* *************************************************************************************** * Copyright (C) 2006 EsperTech, Inc. All rights reserved. * * http://www.espertech.com/esper * * http://www.espertech.com * * ---------------------------------------------------------------------------------- * * The software in this package is published under the terms of the GPL license * * a copy of which has been included with this distribution in the license.txt file. * *************************************************************************************** */ package com.espertech.esper.spatial.quadtree.core; import junit.framework.TestCase; import java.awt.*; import java.awt.geom.Rectangle2D; import java.util.Random; public class TestBoundingBox extends TestCase { public void testFrom() { BoundingBox bb = BoundingBox.from(10, 20, 4, 15); assertEquals(10d, bb.getMinX()); assertEquals(20d, bb.getMinY()); assertEquals(14d, bb.getMaxX()); assertEquals(35d, bb.getMaxY()); } public void testContainsPoint() { BoundingBox bb = new BoundingBox(10, 20, 40, 60); assertTrue(bb.containsPoint(10, 20)); assertTrue(bb.containsPoint(39.9999, 59.9999)); assertFalse(bb.containsPoint(40, 60)); assertFalse(bb.containsPoint(10, 100)); assertFalse(bb.containsPoint(100, 10)); } public void testIntersectsBoxIncludingEnd() { Rectangle2D.Double ref = rect(1, 2, 4, 6); assertIntersectsIncludingEnd(true, rect(1, 2, 4, 6), ref); assertIntersectsIncludingEnd(true, rect(2, 3, 1, 1), ref); assertIntersectsIncludingEnd(true, rect(0, 0, 10, 10), ref); // nw assertIntersectsIncludingEnd(true, rect(0, 0, 1.00001, 3), ref); assertIntersectsIncludingEnd(true, rect(0, 0, 1, 2), ref); assertIntersectsIncludingEnd(false, rect(0, 0, 0.99999, 2), ref); // ne assertIntersectsIncludingEnd(true, rect(4.99999, 0, 1, 3), ref); assertIntersectsIncludingEnd(true, rect(5, 0, 1, 3), ref); assertIntersectsIncludingEnd(false, rect(5.00001, 0, 1, 3), ref); // sw assertIntersectsIncludingEnd(true, rect(0, 7.9999, 1.5, 1), ref); assertIntersectsIncludingEnd(true, rect(0, 8, 1.5, 1), ref); assertIntersectsIncludingEnd(false, rect(0, 8.00001, 1.5, 1), ref); // se assertIntersectsIncludingEnd(true, rect(0, 0, 3, 2.00001), ref); assertIntersectsIncludingEnd(true, rect(0, 0, 3, 2), ref); assertIntersectsIncludingEnd(false, rect(0, 0, 3, 1.99999), ref); } private void assertIntersectsIncludingEnd(boolean expected, Rectangle2D.Double one, Rectangle2D.Double two) { BoundingBox bbOne = BoundingBox.from(one.getX(), one.getY(), one.width, one.getHeight()); BoundingBox bbTwo = BoundingBox.from(two.getX(), two.getY(), two.width, two.getHeight()); assertEquals(expected, bbOne.intersectsBoxIncludingEnd(two.getX(), two.getY(), two.getWidth(), two.getHeight())); assertEquals(expected, bbTwo.intersectsBoxIncludingEnd(one.getX(), one.getY(), one.getWidth(), one.getHeight())); } public void testQuadrant() { BoundingBox bb = new BoundingBox(10, 20, 40, 60); double w = (bb.getMaxX() - bb.getMinX()) / 2d; double h = (bb.getMaxY() - bb.getMinY()) / 2d; BoundingBox bbNW = new BoundingBox(bb.getMinX(), bb.getMinY(), bb.getMinX() + w, bb.getMinY() + h); BoundingBox bbNE = new BoundingBox(bb.getMinX() + w, bb.getMinY(), bb.getMaxX(), bb.getMinY() + h); BoundingBox bbSW = new BoundingBox(bb.getMinX(), bb.getMinY() + h, bb.getMinX() + w, bb.getMaxY()); BoundingBox bbSE = new BoundingBox(bb.getMinX() + w, bb.getMinY() + h, bb.getMaxX(), bb.getMaxY()); runAssertionQuadrant(10, 20, bb, bbNW, bbNE, bbSW, bbSE, QuadrantEnum.NW); runAssertionQuadrant(9, 19, bb, bbNW, bbNE, bbSW, bbSE, null); runAssertionQuadrant(40, 60, bb, bbNW, bbNE, bbSW, bbSE, null); runAssertionQuadrant(39.9999, 59.999999, bb, bbNW, bbNE, bbSW, bbSE, QuadrantEnum.SE); runAssertionQuadrant(39.9999, 20, bb, bbNW, bbNE, bbSW, bbSE, QuadrantEnum.NE); runAssertionQuadrant(10, 59.999999, bb, bbNW, bbNE, bbSW, bbSE, QuadrantEnum.SW); runAssertionQuadrant(24.9999, 39.9999, bb, bbNW, bbNE, bbSW, bbSE, QuadrantEnum.NW); runAssertionQuadrant(25, 40, bb, bbNW, bbNE, bbSW, bbSE, QuadrantEnum.SE); runAssertionQuadrant(24.9999, 40, bb, bbNW, bbNE, bbSW, bbSE, QuadrantEnum.SW); runAssertionQuadrant(25, 39.9999, bb, bbNW, bbNE, bbSW, bbSE, QuadrantEnum.NE); } public void testQuadrantIfFits() { BoundingBox bb = new BoundingBox(10, 20, 40, 60); double w = (bb.getMaxX() - bb.getMinX()) / 2d; double h = (bb.getMaxY() - bb.getMinY()) / 2d; BoundingBox bbNW = new BoundingBox(bb.getMinX(), bb.getMinY(), bb.getMinX() + w, bb.getMinY() + h); BoundingBox bbNE = new BoundingBox(bb.getMinX() + w, bb.getMinY(), bb.getMaxX(), bb.getMinY() + h); BoundingBox bbSW = new BoundingBox(bb.getMinX(), bb.getMinY() + h, bb.getMinX() + w, bb.getMaxY()); BoundingBox bbSE = new BoundingBox(bb.getMinX() + w, bb.getMinY() + h, bb.getMaxX(), bb.getMaxY()); assertEquals(QuadrantAppliesEnum.NW, bb.getQuadrantApplies(10, 20, 1, 1)); assertEquals(QuadrantAppliesEnum.SOME, bb.getQuadrantApplies(10, 20, 40, 60)); assertEquals(QuadrantAppliesEnum.NONE, bb.getQuadrantApplies(0, 0, 1, 1)); // within single runAssertionQuadrantAppliesOne(11, 21, 1, 1, bb, bbNW, bbNE, bbSW, bbSE, QuadrantAppliesEnum.NW); runAssertionQuadrantAppliesOne(26, 21, 1, 1, bb, bbNW, bbNE, bbSW, bbSE, QuadrantAppliesEnum.NE); runAssertionQuadrantAppliesOne(11, 50, 1, 1, bb, bbNW, bbNE, bbSW, bbSE, QuadrantAppliesEnum.SW); runAssertionQuadrantAppliesOne(26, 50, 1, 1, bb, bbNW, bbNE, bbSW, bbSE, QuadrantAppliesEnum.SE); // NW approach runAssertionQuadrantAppliesNone(0, 0, 0, 0, bb, bbNW, bbNE, bbSW, bbSE); runAssertionQuadrantAppliesNone(0, 0, 9.9999, 19.9999, bb, bbNW, bbNE, bbSW, bbSE); runAssertionQuadrantAppliesNone(0, 0, 10, 19.9999, bb, bbNW, bbNE, bbSW, bbSE); runAssertionQuadrantAppliesNone(0, 0, 9.9999, 20, bb, bbNW, bbNE, bbSW, bbSE); runAssertionQuadrantAppliesOne(0, 0, 10, 20, bb, bbNW, bbNE, bbSW, bbSE, QuadrantAppliesEnum.NW); runAssertionQuadrantAppliesOne(0, 0, 10+14.999, 20+19.999, bb, bbNW, bbNE, bbSW, bbSE, QuadrantAppliesEnum.NW); runAssertionQuadrantAppliesMulti(0, 0, 10+15, 20+19.9999, bb, bbNW, bbNE, bbSW, bbSE, true, true, false, false); runAssertionQuadrantAppliesMulti(0, 0, 10+14.999, 20+20, bb, bbNW, bbNE, bbSW, bbSE, true, false, true, false); runAssertionQuadrantAppliesMulti(0, 0, 10+15, 20+20, bb, bbNW, bbNE, bbSW, bbSE, true, true, true, true); // NE approach runAssertionQuadrantAppliesNone(45, 0, 0, 0, bb, bbNW, bbNE, bbSW, bbSE); runAssertionQuadrantAppliesNone(40.001, 0, 0, 19.9999, bb, bbNW, bbNE, bbSW, bbSE); runAssertionQuadrantAppliesNone(40.001, 0, 0, 20, bb, bbNW, bbNE, bbSW, bbSE); runAssertionQuadrantAppliesNone(40, 0, 0, 19.9999, bb, bbNW, bbNE, bbSW, bbSE); runAssertionQuadrantAppliesOne(40, 0, 0, 20, bb, bbNW, bbNE, bbSW, bbSE, QuadrantAppliesEnum.NE); runAssertionQuadrantAppliesOne(40-14.999, 0, 0, 20+19.999, bb, bbNW, bbNE, bbSW, bbSE, QuadrantAppliesEnum.NE); runAssertionQuadrantAppliesMulti(40-15, 0, 0, 20+19.999, bb, bbNW, bbNE, bbSW, bbSE, true, true, false, false); runAssertionQuadrantAppliesMulti(40-14.999, 0, 0, 20+20, bb, bbNW, bbNE, bbSW, bbSE, false, true, false, true); runAssertionQuadrantAppliesMulti(40-15, 0, 0, 20+20, bb, bbNW, bbNE, bbSW, bbSE, true, true, true, true); // SW approach runAssertionQuadrantAppliesNone(0, 70, 0, 0, bb, bbNW, bbNE, bbSW, bbSE); runAssertionQuadrantAppliesNone(0, 60.0001, 9.9999, 0, bb, bbNW, bbNE, bbSW, bbSE); runAssertionQuadrantAppliesNone(0, 60.0001, 10, 0, bb, bbNW, bbNE, bbSW, bbSE); runAssertionQuadrantAppliesNone(0, 60, 9.9999, 0, bb, bbNW, bbNE, bbSW, bbSE); runAssertionQuadrantAppliesOne(0, 60, 10, 0, bb, bbNW, bbNE, bbSW, bbSE, QuadrantAppliesEnum.SW); runAssertionQuadrantAppliesOne(0, 40.001, 10+14.999, 5, bb, bbNW, bbNE, bbSW, bbSE, QuadrantAppliesEnum.SW); runAssertionQuadrantAppliesMulti(0, 40, 10+14.999, 5, bb, bbNW, bbNE, bbSW, bbSE, true, false, true, false); runAssertionQuadrantAppliesMulti(0, 40.001, 10+15, 5, bb, bbNW, bbNE, bbSW, bbSE, false, false, true, true); runAssertionQuadrantAppliesMulti(0, 40, 10+15, 5, bb, bbNW, bbNE, bbSW, bbSE, true, true, true, true); // SE approach runAssertionQuadrantAppliesNone(50, 70, 0, 0, bb, bbNW, bbNE, bbSW, bbSE); runAssertionQuadrantAppliesNone(40.001, 60.001, 0, 0, bb, bbNW, bbNE, bbSW, bbSE); runAssertionQuadrantAppliesNone(40, 60.001, 0, 0, bb, bbNW, bbNE, bbSW, bbSE); runAssertionQuadrantAppliesNone(40.001, 60, 0, 0, bb, bbNW, bbNE, bbSW, bbSE); runAssertionQuadrantAppliesOne(40, 60, 0, 0, bb, bbNW, bbNE, bbSW, bbSE, QuadrantAppliesEnum.SE); runAssertionQuadrantAppliesOne(40-14.9999, 60-19.999, 100, 100, bb, bbNW, bbNE, bbSW, bbSE, QuadrantAppliesEnum.SE); runAssertionQuadrantAppliesMulti(40-14.9999, 60-20, 100, 100, bb, bbNW, bbNE, bbSW, bbSE, false, true, false, true); runAssertionQuadrantAppliesMulti(40-15, 60-19.999, 100, 100, bb, bbNW, bbNE, bbSW, bbSE, false, false, true, true); runAssertionQuadrantAppliesMulti(40-15, 60-20, 100, 100, bb, bbNW, bbNE, bbSW, bbSE, true, true, true, true); // contains-all runAssertionQuadrantAppliesAll(0, 0, 100, 100, bb, bbNW, bbNE, bbSW, bbSE); runAssertionQuadrantAppliesAll(10, 20, 40, 60, bb, bbNW, bbNE, bbSW, bbSE); runAssertionQuadrantAppliesAll(24, 39, 2, 2, bb, bbNW, bbNE, bbSW, bbSE); runAssertionQuadrantAppliesAll(25, 40, 0, 0, bb, bbNW, bbNE, bbSW, bbSE); // start-within runAssertionQuadrantAppliesOne(10, 20, 14.9999, 19.999, bb, bbNW, bbNE, bbSW, bbSE, QuadrantAppliesEnum.NW); runAssertionQuadrantAppliesMulti(10, 20, 15, 19.999, bb, bbNW, bbNE, bbSW, bbSE, true, true, false, false); runAssertionQuadrantAppliesMulti(10, 20, 14.9999, 20, bb, bbNW, bbNE, bbSW, bbSE, true, false, true, false); runAssertionQuadrantAppliesMulti(10, 20, 15, 20, bb, bbNW, bbNE, bbSW, bbSE, true, true, true, true); // try random Random random = new Random(); for (int i = 0; i < 1000000; i++) { double width = random.nextDouble() * 50; double height = random.nextDouble() * 70; double x = random.nextDouble() * 50; double y = random.nextDouble() * 70 + 10; QuadrantAppliesEnum result = bb.getQuadrantApplies(x, y, width, height); boolean nw = bbNW.intersectsBoxIncludingEnd(x, y, width, height); boolean ne = bbNE.intersectsBoxIncludingEnd(x, y, width, height); boolean sw = bbSW.intersectsBoxIncludingEnd(x, y, width, height); boolean se = bbSE.intersectsBoxIncludingEnd(x, y, width, height); if (result == QuadrantAppliesEnum.NONE && (nw | ne | sw | se)) { fail(); } else if (result == QuadrantAppliesEnum.SOME) { assertTrue((nw ? 1 : 0) + (ne ? 1 : 0) + (sw ? 1 : 0) + (se ? 1 : 0) > 1); } else { assertEquals(result == QuadrantAppliesEnum.NW, nw); assertEquals(result == QuadrantAppliesEnum.NE, ne); assertEquals(result == QuadrantAppliesEnum.SW, sw); assertEquals(result == QuadrantAppliesEnum.SE, se); } } } public void testTreeForDepth() { BoundingBox bb = new BoundingBox(0, 0, 100, 100); BoundingBox.BoundingBoxNode node = bb.treeForDepth(3); BoundingBox swNwNw = node.se.nw.nw.bb; assertTrue(swNwNw.equals(new BoundingBox(50, 50, 50+100/2/2/2.0, 50+100/2/2/2.0))); } public void testTreeForPath() { BoundingBox bb = new BoundingBox(0, 0, 100, 100); BoundingBox.BoundingBoxNode node = bb.treeForPath("se,nw,ne,sw".split(",")); BoundingBox inner = node.se.nw.ne.sw.bb; BoundingBox.BoundingBoxNode tree = bb.treeForDepth(4); assertTrue(inner.equals(tree.se.nw.ne.sw.bb)); assertEquals(node.nw, node.getQuadrant(QuadrantEnum.NW)); assertEquals(node.ne, node.getQuadrant(QuadrantEnum.NE)); assertEquals(node.sw, node.getQuadrant(QuadrantEnum.SW)); assertEquals(node.se, node.getQuadrant(QuadrantEnum.SE)); } private void runAssertionQuadrant(double x, double y, BoundingBox bb, BoundingBox bbNW, BoundingBox bbNE, BoundingBox bbSW, BoundingBox bbSE, QuadrantEnum expected) { if (!bb.containsPoint(x, y)) { assertNull(expected); assertFalse(bbNW.containsPoint(x, y)); assertFalse(bbNE.containsPoint(x, y)); assertFalse(bbSW.containsPoint(x, y)); assertFalse(bbSE.containsPoint(x, y)); return; } QuadrantEnum received = bb.getQuadrant(x, y); assertEquals(expected, received); assertEquals(expected == QuadrantEnum.NW, bbNW.containsPoint(x, y)); assertEquals(expected == QuadrantEnum.NE, bbNE.containsPoint(x, y)); assertEquals(expected == QuadrantEnum.SW, bbSW.containsPoint(x, y)); assertEquals(expected == QuadrantEnum.SE, bbSE.containsPoint(x, y)); BoundingBox[] subdivided = bb.subdivide(); assertEquals(expected == QuadrantEnum.NW, subdivided[0].containsPoint(x, y)); assertEquals(expected == QuadrantEnum.NE, subdivided[1].containsPoint(x, y)); assertEquals(expected == QuadrantEnum.SW, subdivided[2].containsPoint(x, y)); assertEquals(expected == QuadrantEnum.SE, subdivided[3].containsPoint(x, y)); } private void runAssertionQuadrantAppliesMulti(double x, double y, double width, double height, BoundingBox bb, BoundingBox bbNW, BoundingBox bbNE, BoundingBox bbSW, BoundingBox bbSE, boolean intersectsNW, boolean intersectsNE, boolean intersectsSW, boolean intersectsSE) { assertEquals(QuadrantAppliesEnum.SOME, bb.getQuadrantApplies(x, y, width, height)); assertEquals(intersectsNW, bbNW.intersectsBoxIncludingEnd(x, y, width, height)); assertEquals(intersectsNE, bbNE.intersectsBoxIncludingEnd(x, y, width, height)); assertEquals(intersectsSW, bbSW.intersectsBoxIncludingEnd(x, y, width, height)); assertEquals(intersectsSE, bbSE.intersectsBoxIncludingEnd(x, y, width, height)); } private void runAssertionQuadrantAppliesNone(double x, double y, double width, double height, BoundingBox bb, BoundingBox bbNW, BoundingBox bbNE, BoundingBox bbSW, BoundingBox bbSE) { assertEquals(QuadrantAppliesEnum.NONE, bb.getQuadrantApplies(x, y, width, height)); assertFalse(bb.intersectsBoxIncludingEnd(x, y, width, height)); assertFalse(bbNW.intersectsBoxIncludingEnd(x, y, width, height)); assertFalse(bbNE.intersectsBoxIncludingEnd(x, y, width, height)); assertFalse(bbSW.intersectsBoxIncludingEnd(x, y, width, height)); assertFalse(bbSE.intersectsBoxIncludingEnd(x, y, width, height)); } private void runAssertionQuadrantAppliesAll(double x, double y, double width, double height, BoundingBox bb, BoundingBox bbNW, BoundingBox bbNE, BoundingBox bbSW, BoundingBox bbSE) { assertEquals(QuadrantAppliesEnum.SOME, bb.getQuadrantApplies(x, y, width, height)); assertTrue(bb.intersectsBoxIncludingEnd(x, y, width, height)); assertTrue(bbNW.intersectsBoxIncludingEnd(x, y, width, height)); assertTrue(bbNE.intersectsBoxIncludingEnd(x, y, width, height)); assertTrue(bbSW.intersectsBoxIncludingEnd(x, y, width, height)); assertTrue(bbSE.intersectsBoxIncludingEnd(x, y, width, height)); } private void runAssertionQuadrantAppliesOne(double x, double y, double width, double height, BoundingBox bb, BoundingBox bbNW, BoundingBox bbNE, BoundingBox bbSW, BoundingBox bbSE, QuadrantAppliesEnum expected) { assertEquals(expected, bb.getQuadrantApplies(x, y, width, height)); assertTrue(bb.intersectsBoxIncludingEnd(x, y, width, height)); assertEquals(expected == QuadrantAppliesEnum.NW, bbNW.intersectsBoxIncludingEnd(x, y, width, height)); assertEquals(expected == QuadrantAppliesEnum.NE, bbNE.intersectsBoxIncludingEnd(x, y, width, height)); assertEquals(expected == QuadrantAppliesEnum.SW, bbSW.intersectsBoxIncludingEnd(x, y, width, height)); assertEquals(expected == QuadrantAppliesEnum.SE, bbSE.intersectsBoxIncludingEnd(x, y, width, height)); } private Rectangle2D.Double rect(double x, double y, double width, double height) { return new Rectangle.Double(x, y, width, height); } }