/****************************************************************************** * Copyright (c) 2011, 2016 Stephan Schwiebert and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Stephan Schwiebert - initial API and implementation * *******************************************************************************/ package org.eclipse.gef.cloudio.internal.ui.util; import java.util.Collection; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Set; import org.eclipse.swt.graphics.Point; /** * A two-dimensional tree structure to store non-overlapping rectangles. * * @author sschwieb * */ public class RectTree { private final int minResolution; private short xOffset, yOffset; private RectNode root; private LinkedList<RectNode> leaves; public static short EMPTY = -3, MISC = -2, BACKGROUND = -1; class RectNode { final SmallRect rect; private RectNode[] children; private final SmallRect[] childAreas; short filled = EMPTY; public RectNode(SmallRect rect) { this.rect = rect; final int width = rect.width / 2; final int height = rect.height / 2; if (rect.width > minResolution) { this.childAreas = new SmallRect[4]; // top left childAreas[0] = new SmallRect(rect.x, rect.y, width, height); // top right childAreas[1] = new SmallRect(rect.x + width, rect.y, width, height); // bottom left childAreas[2] = new SmallRect(rect.x, rect.y + height, width, height); // bottom right childAreas[3] = new SmallRect(rect.x + width, rect.y + height, width, height); } else { this.childAreas = null; } } private int getChildIndex(SmallRect r) { int index = 0; if (r.y >= childAreas[3].y) { if (r.x >= childAreas[3].x) { index = 3; } else { index = 2; } } else { if (r.x >= childAreas[1].x) { index = 1; } } return index; } public boolean insert(SmallRect r, short id) { if (rect.width == minResolution) { filled = id; return true; } int i = getChildIndex(r); if (children == null) { children = new RectNode[4]; } if (children[i] == null) { children[i] = new RectNode(childAreas[i]); } boolean filledChild = children[i].insert(r, id); if (filledChild) { Set<Short> ids = new HashSet<>(); boolean filled = true; for (int j = 0; j < children.length; j++) { if (i == j) continue; if (children[j] == null || children[j].filled == EMPTY) { filled = false; break; } ids.add(children[j].filled); } if (filled) { if (ids.size() == 1) { this.filled = ids.iterator().next(); if (this.filled == BACKGROUND) { children = null; } } else { this.filled = MISC; } } return filled; } return false; } public boolean isAvailable(final SmallRect oRect) { if (filled >= MISC) return false; if (children == null) { return filled == EMPTY; } final int i = getChildIndex(oRect); if (children[i] == null) return true; return children[i].isAvailable(oRect); } public short getWordId(int x, int y) { if (filled > BACKGROUND) return filled; if (children == null) { return filled; } for (int i = 0; i < childAreas.length; i++) { if (childAreas[i].intersects(x, y, minResolution, minResolution) && children[i] != null) { return children[i].getWordId(x, y); } } return EMPTY; } public short getWordId(Point position) { return getWordId(position.x, position.y); } } public RectTree(SmallRect root, int minResolution) { this.minResolution = minResolution; this.root = new RectNode(root); } public void insert(SmallRect r, short id) { root.insert(r, id); } public void move(int x, int y) { this.xOffset = (short) x; this.yOffset = (short) y; } public boolean fits(final CloudMatrix mainTree) { LinkedList<RectNode> leaves = getLeaves(); Iterator<RectNode> nodes = leaves.iterator(); while (nodes.hasNext()) { RectNode node = nodes.next(); if (!mainTree.isEmpty((node.rect.x + xOffset) / minResolution, (node.rect.y + yOffset) / minResolution)) { nodes.remove(); leaves.addFirst(node); return false; } } return true; } LinkedList<RectNode> getLeaves() { if (leaves == null) { leaves = new LinkedList<>(); addLeaves(leaves, root); } return leaves; } private void addLeaves(List<RectNode> leaves, RectNode current) { if (current.children == null) { if (current.filled != EMPTY) { leaves.add(current); } } else { for (int i = 0; i < 4; i++) { if (current.children[i] == null) continue; addLeaves(leaves, current.children[i]); } } } public void place(final CloudMatrix mainTree, short id) { Collection<RectNode> leaves = getLeaves(); for (RectNode node : leaves) { mainTree.set(node, id, xOffset, yOffset, minResolution); } } public void releaseRects() { getLeaves(); root.children = null; } public RectNode getRoot() { return root; } public void reset() { root = new RectNode(root.rect); } }