/* *************************************************************************************** * 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; public class BoundingBox { private final double minX; private final double minY; private final double maxX; private final double maxY; public BoundingBox(double minX, double minY, double maxX, double maxY) { this.minX = minX; this.minY = minY; this.maxX = maxX; this.maxY = maxY; } public double getMinX() { return minX; } public double getMinY() { return minY; } public double getMaxX() { return maxX; } public double getMaxY() { return maxY; } public boolean containsPoint(double x, double y) { return x >= minX && y >= minY && x < maxX && y < maxY; } public boolean intersectsBoxIncludingEnd(double x, double y, double width, double height) { return intersectsBoxIncludingEnd(minX, minY, maxX, maxY, x, y, width, height); } public static boolean intersectsBoxIncludingEnd(double minX, double minY, double maxX, double maxY, double otherX, double otherY, double otherWidth, double otherHeight) { double otherMaxX = otherX + otherWidth; double otherMaxY = otherY + otherHeight; if (maxX < otherX) return false; // a is left of b if (minX > otherMaxX) return false; // a is right of b if (maxY < otherY) return false; // a is above b if (minY > otherMaxY) return false; // a is below b return true; // boxes overlap } public static boolean containsPoint(double x, double y, double width, double height, double px, double py) { if (px >= x + width) return false; if (px < x) return false; if (py >= y + height) return false; if (py < y) return false; return true; } public String toString() { return "{" + "minX=" + minX + ", minY=" + minY + ", maxX=" + maxX + ", maxY=" + maxY + '}'; } public QuadrantEnum getQuadrant(double x, double y) { double deltaX = x - minX; double deltaY = y - minY; double halfWidth = (maxX - minX) / 2; double halfHeight = (maxY - minY) / 2; if (deltaX < halfWidth) { return deltaY < halfHeight ? QuadrantEnum.NW : QuadrantEnum.SW; } return deltaY < halfHeight ? QuadrantEnum.NE : QuadrantEnum.SE; } public QuadrantAppliesEnum getQuadrantApplies(double x, double y, double w, double h) { double deltaX = x - minX; double deltaY = y - minY; double halfWidth = (maxX - minX) / 2; double halfHeight = (maxY - minY) / 2; double midX = minX + halfWidth; double midY = minY + halfHeight; if (deltaX < halfWidth) { if (deltaY < halfHeight) { // x,y is NW world if (x + w < minX || y + h < minY) { return QuadrantAppliesEnum.NONE; } if (x + w >= midX || y + h >= midY) { return QuadrantAppliesEnum.SOME; } return QuadrantAppliesEnum.NW; } else { if (y > maxY || x + w < minX) { return QuadrantAppliesEnum.NONE; } if (x + w >= midX || y <= midY) { return QuadrantAppliesEnum.SOME; } return QuadrantAppliesEnum.SW; } } if (deltaY < halfHeight) { // x,y is NE world if (x > maxX || y + h < minY) { return QuadrantAppliesEnum.NONE; } if (x <= midX || y + h >= midY) { return QuadrantAppliesEnum.SOME; } return QuadrantAppliesEnum.NE; } else { if (x > maxX || y > maxY) { return QuadrantAppliesEnum.NONE; } if (x <= midX || y <= midY) { return QuadrantAppliesEnum.SOME; } return QuadrantAppliesEnum.SE; } } public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; BoundingBox that = (BoundingBox) o; if (Double.compare(that.minX, minX) != 0) return false; if (Double.compare(that.minY, minY) != 0) return false; if (Double.compare(that.maxX, maxX) != 0) return false; return Double.compare(that.maxY, maxY) == 0; } public int hashCode() { int result; long temp; temp = Double.doubleToLongBits(minX); result = (int) (temp ^ (temp >>> 32)); temp = Double.doubleToLongBits(minY); result = 31 * result + (int) (temp ^ (temp >>> 32)); temp = Double.doubleToLongBits(maxX); result = 31 * result + (int) (temp ^ (temp >>> 32)); temp = Double.doubleToLongBits(maxY); result = 31 * result + (int) (temp ^ (temp >>> 32)); return result; } public BoundingBox[] subdivide() { double w = (maxX - minX) / 2d; double h = (maxY - minY) / 2d; BoundingBox bbNW = new BoundingBox(minX, minY, minX + w, minY + h); BoundingBox bbNE = new BoundingBox(minX + w, minY, maxX, minY + h); BoundingBox bbSW = new BoundingBox(minX, minY + h, minX + w, maxY); BoundingBox bbSE = new BoundingBox(minX + w, minY + h, maxX, maxY); return new BoundingBox[]{bbNW, bbNE, bbSW, bbSE}; } public BoundingBoxNode treeForDepth(int depth) { BoundingBoxNode[] quadrants = new BoundingBoxNode[4]; if (depth > 0) { BoundingBox[] subs = subdivide(); quadrants[0] = subs[0].treeForDepth(depth - 1); quadrants[1] = subs[1].treeForDepth(depth - 1); quadrants[2] = subs[2].treeForDepth(depth - 1); quadrants[3] = subs[3].treeForDepth(depth - 1); } return new BoundingBoxNode(this, quadrants[0], quadrants[1], quadrants[2], quadrants[3]); } public static BoundingBox from(double x, double y, double width, double height) { return new BoundingBox(x, y, x + width, y + height); } public BoundingBoxNode treeForPath(String[] path) { return treeForPath(path, 0); } private BoundingBoxNode treeForPath(String[] path, int offset) { BoundingBoxNode[] quadrants = new BoundingBoxNode[4]; if (offset < path.length) { BoundingBox[] subs = subdivide(); String q = path[offset]; if (q.equals("nw")) { quadrants[0] = subs[0].treeForPath(path, offset + 1); } if (q.equals("ne")) { quadrants[1] = subs[1].treeForPath(path, offset + 1); } if (q.equals("sw")) { quadrants[2] = subs[2].treeForPath(path, offset + 1); } if (q.equals("se")) { quadrants[3] = subs[3].treeForPath(path, offset + 1); } } return new BoundingBoxNode(this, quadrants[0], quadrants[1], quadrants[2], quadrants[3]); } public static class BoundingBoxNode { public final BoundingBox bb; public final BoundingBoxNode nw; public final BoundingBoxNode ne; public final BoundingBoxNode sw; public final BoundingBoxNode se; public BoundingBoxNode(BoundingBox bb, BoundingBoxNode nw, BoundingBoxNode ne, BoundingBoxNode sw, BoundingBoxNode se) { this.bb = bb; this.nw = nw; this.ne = ne; this.sw = sw; this.se = se; } public BoundingBoxNode getQuadrant(QuadrantEnum q) { if (q == QuadrantEnum.NW) { return nw; } else if (q == QuadrantEnum.NE) { return ne; } else if (q == QuadrantEnum.SW) { return sw; } else { return se; } } } }