/*
***************************************************************************************
* 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.mxciffilterindex;
import com.espertech.esper.spatial.quadtree.core.BoundingBox;
import com.espertech.esper.spatial.quadtree.core.QuadrantAppliesEnum;
import com.espertech.esper.spatial.quadtree.mxcif.MXCIFQuadTree;
import com.espertech.esper.spatial.quadtree.mxcif.MXCIFQuadTreeNode;
import com.espertech.esper.spatial.quadtree.mxcif.MXCIFQuadTreeNodeBranch;
import com.espertech.esper.spatial.quadtree.mxcif.MXCIFQuadTreeNodeLeaf;
import java.util.Collection;
import java.util.LinkedList;
import static com.espertech.esper.spatial.quadtree.mxcifrowindex.MXCIFQuadTreeFilterIndexCheckBB.checkBB;
public class MXCIFQuadTreeFilterIndexSet {
public static <L> void set(double x, double y, double width, double height, L value, MXCIFQuadTree<Object> tree) {
MXCIFQuadTreeNode<Object> root = tree.getRoot();
checkBB(root.getBb(), x, y, width, height);
MXCIFQuadTreeNode<Object> replacement = setOnNode(x, y, width, height, value, root, tree);
tree.setRoot(replacement);
}
private static <L> MXCIFQuadTreeNode<Object> setOnNode(double x, double y, double width, double height, L value, MXCIFQuadTreeNode<Object> node, MXCIFQuadTree<Object> tree) {
if (node instanceof MXCIFQuadTreeNodeLeaf) {
MXCIFQuadTreeNodeLeaf<Object> leaf = (MXCIFQuadTreeNodeLeaf<Object>) node;
int count = setOnNode(leaf, x, y, width, height, value);
leaf.incCount(count);
if (leaf.getCount() <= tree.getLeafCapacity() || node.getLevel() >= tree.getMaxTreeHeight()) {
return leaf;
}
node = subdivide(leaf, tree);
}
MXCIFQuadTreeNodeBranch<Object> branch = (MXCIFQuadTreeNodeBranch) node;
addToBranch(branch, x, y, width, height, value, tree);
return node;
}
private static void addToBranch(MXCIFQuadTreeNodeBranch<Object> branch, double x, double y, double width, double height, Object value, MXCIFQuadTree<Object> tree) {
QuadrantAppliesEnum quadrant = branch.getBb().getQuadrantApplies(x, y, width, height);
if (quadrant == QuadrantAppliesEnum.NW) {
branch.setNw(setOnNode(x, y, width, height, value, branch.getNw(), tree));
} else if (quadrant == QuadrantAppliesEnum.NE) {
branch.setNe(setOnNode(x, y, width, height, value, branch.getNe(), tree));
} else if (quadrant == QuadrantAppliesEnum.SW) {
branch.setSw(setOnNode(x, y, width, height, value, branch.getSw(), tree));
} else if (quadrant == QuadrantAppliesEnum.SE) {
branch.setSe(setOnNode(x, y, width, height, value, branch.getSe(), tree));
} else if (quadrant == QuadrantAppliesEnum.SOME) {
int count = setOnNode(branch, x, y, width, height, value);
branch.incCount(count);
} else {
throw new IllegalStateException("Quandrant not applies to any");
}
}
private static <L> MXCIFQuadTreeNode<Object> subdivide(MXCIFQuadTreeNodeLeaf<Object> leaf, MXCIFQuadTree<Object> tree) {
double w = (leaf.getBb().getMaxX() - leaf.getBb().getMinX()) / 2d;
double h = (leaf.getBb().getMaxY() - leaf.getBb().getMinY()) / 2d;
double minx = leaf.getBb().getMinX();
double miny = leaf.getBb().getMinY();
BoundingBox bbNW = new BoundingBox(minx, miny, minx + w, miny + h);
BoundingBox bbNE = new BoundingBox(minx + w, miny, leaf.getBb().getMaxX(), miny + h);
BoundingBox bbSW = new BoundingBox(minx, miny + h, minx + w, leaf.getBb().getMaxY());
BoundingBox bbSE = new BoundingBox(minx + w, miny + h, leaf.getBb().getMaxX(), leaf.getBb().getMaxY());
MXCIFQuadTreeNode<Object> nw = new MXCIFQuadTreeNodeLeaf<>(bbNW, leaf.getLevel() + 1, null, 0);
MXCIFQuadTreeNode<Object> ne = new MXCIFQuadTreeNodeLeaf<>(bbNE, leaf.getLevel() + 1, null, 0);
MXCIFQuadTreeNode<Object> sw = new MXCIFQuadTreeNodeLeaf<>(bbSW, leaf.getLevel() + 1, null, 0);
MXCIFQuadTreeNode<Object> se = new MXCIFQuadTreeNodeLeaf<>(bbSE, leaf.getLevel() + 1, null, 0);
MXCIFQuadTreeNodeBranch<Object> branch = new MXCIFQuadTreeNodeBranch<>(leaf.getBb(), leaf.getLevel(), null, 0, nw, ne, sw, se);
Object rectangles = leaf.getData();
if (rectangles instanceof XYWHRectangleWValue) {
XYWHRectangleWValue rectangle = (XYWHRectangleWValue<L>) rectangles;
subdivide(rectangle, branch, tree);
} else {
Collection<XYWHRectangleWValue<L>> collection = (Collection<XYWHRectangleWValue<L>>) rectangles;
for (XYWHRectangleWValue<L> rectangle : collection) {
subdivide(rectangle, branch, tree);
}
}
return branch;
}
private static <L> void subdivide(XYWHRectangleWValue<L> rectangle, MXCIFQuadTreeNodeBranch<Object> branch, MXCIFQuadTree<Object> tree) {
double x = rectangle.getX();
double y = rectangle.getY();
double w = rectangle.getW();
double h = rectangle.getH();
QuadrantAppliesEnum quadrant = branch.getBb().getQuadrantApplies(x, y, w, h);
if (quadrant == QuadrantAppliesEnum.NW) {
branch.setNw(setOnNode(x, y, w, h, rectangle, branch.getNw(), tree));
} else if (quadrant == QuadrantAppliesEnum.NE) {
branch.setNe(setOnNode(x, y, w, h, rectangle, branch.getNe(), tree));
} else if (quadrant == QuadrantAppliesEnum.SW) {
branch.setSw(setOnNode(x, y, w, h, rectangle, branch.getSw(), tree));
} else if (quadrant == QuadrantAppliesEnum.SE) {
branch.setSe(setOnNode(x, y, w, h, rectangle, branch.getSe(), tree));
} else if (quadrant == QuadrantAppliesEnum.SOME) {
int numAdded = setOnNode(branch, x, y, w, h, rectangle);
branch.incCount(numAdded);
} else {
throw new IllegalStateException("No intersection");
}
}
private static <L> int setOnNode(MXCIFQuadTreeNode<Object> node, double x, double y, double width, double height, L value) {
Object currentValue = node.getData();
if (value instanceof XYWHRectangleWValue) {
XYWHRectangleWValue<L> rectangle = (XYWHRectangleWValue<L>) value;
if (!rectangle.coordinateEquals(x, y, width, height)) {
throw new IllegalStateException();
}
if (currentValue == null) {
node.setData(rectangle);
return 1;
}
if (currentValue instanceof XYWHRectangleWValue) {
XYWHRectangleWValue<L> other = (XYWHRectangleWValue<L>) currentValue;
if (other.coordinateEquals(x, y, width, height)) {
other.setValue(value);
return 0; // replaced
}
Collection<XYWHRectangleWValue<L>> collection = new LinkedList<>();
collection.add(other);
collection.add(rectangle);
node.setData(collection);
return 1;
}
Collection<XYWHRectangleWValue<L>> collection = (Collection<XYWHRectangleWValue<L>>) currentValue;
for (XYWHRectangleWValue<L> other : collection) {
if (other.coordinateEquals(x, y, width, height)) {
other.setValue(value);
return 0;
}
}
collection.add(rectangle);
return 1;
}
if (currentValue == null) {
XYWHRectangleWValue<L> point = new XYWHRectangleWValue<>(x, y, width, height, value);
node.setData(point);
return 1;
}
if (currentValue instanceof XYWHRectangleWValue) {
XYWHRectangleWValue<L> other = (XYWHRectangleWValue<L>) currentValue;
if (other.coordinateEquals(x, y, width, height)) {
other.setValue(value);
return 0;
}
Collection<XYWHRectangleWValue<L>> collection = new LinkedList<>();
collection.add(other);
collection.add(new XYWHRectangleWValue<>(x, y, width, height, value));
node.setData(collection);
return 1;
}
Collection<XYWHRectangleWValue<L>> collection = (Collection<XYWHRectangleWValue<L>>) currentValue;
for (XYWHRectangleWValue<L> other : collection) {
if (other.coordinateEquals(x, y, width, height)) {
other.setValue(value);
return 0;
}
}
collection.add(new XYWHRectangleWValue<>(x, y, width, height, value));
return 1;
}
}