From 604522f0375ad4e54450dd1251d18d72ca262b8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Sch=C3=BCth?= Date: Sun, 8 May 2011 00:38:03 +0200 Subject: [PATCH] Added first set of BoundQuadTree classes --- .../java/at/jotschi/quadtree/BoundsNode.java | 18 ++ .../jotschi/quadtree/BoundsNodeElement.java | 24 +++ .../at/jotschi/quadtree/BoundsQuadTree.java | 75 ++++++++ src/main/java/at/jotschi/quadtree/Node.java | 22 +-- .../java/at/jotschi/quadtree/NodeElement.java | 3 +- .../java/at/jotschi/quadtree/QuadTree.java | 51 +++++- .../at/jotschi/quadtree/QuadTreePanel.java | 170 ++++++++++++++++++ .../at/jotschi/quadtree/QuadTreeTest.java | 15 +- .../quadtree/RenderBoundsQuadTree.java | 41 +++++ .../at/jotschi/quadtree/RenderQuadTree.java | 160 +---------------- 10 files changed, 402 insertions(+), 177 deletions(-) create mode 100644 src/main/java/at/jotschi/quadtree/BoundsNode.java create mode 100644 src/main/java/at/jotschi/quadtree/BoundsNodeElement.java create mode 100644 src/main/java/at/jotschi/quadtree/BoundsQuadTree.java create mode 100644 src/test/java/at/jotschi/quadtree/QuadTreePanel.java create mode 100644 src/test/java/at/jotschi/quadtree/RenderBoundsQuadTree.java diff --git a/src/main/java/at/jotschi/quadtree/BoundsNode.java b/src/main/java/at/jotschi/quadtree/BoundsNode.java new file mode 100644 index 0000000..3c5cd3e --- /dev/null +++ b/src/main/java/at/jotschi/quadtree/BoundsNode.java @@ -0,0 +1,18 @@ +package at.jotschi.quadtree; + +import java.awt.Dimension; +import java.awt.Point; + +public class BoundsNode extends Node { + + public BoundsNode(Point startCoordinates, Dimension bounds, int depth) { + super(startCoordinates, bounds, depth); + } + + public BoundsNode(Point startCoordinates, Dimension bounds, int depth, + int maxDepth, int maxChildren) { + super(startCoordinates, bounds, depth, maxDepth, maxChildren); + + } + +} diff --git a/src/main/java/at/jotschi/quadtree/BoundsNodeElement.java b/src/main/java/at/jotschi/quadtree/BoundsNodeElement.java new file mode 100644 index 0000000..09d61e4 --- /dev/null +++ b/src/main/java/at/jotschi/quadtree/BoundsNodeElement.java @@ -0,0 +1,24 @@ +package at.jotschi.quadtree; + +import java.awt.Dimension; +import java.awt.Point; + +@SuppressWarnings("serial") +public class BoundsNodeElement extends NodeElement { + + protected Dimension bounds; + + public BoundsNodeElement(Point coordinates, Dimension bounds, T element) { + super(coordinates, element); + this.bounds = bounds; + } + + public int getWidth() { + return this.bounds.width; + } + + public int getHeight() { + return this.bounds.height; + } + +} diff --git a/src/main/java/at/jotschi/quadtree/BoundsQuadTree.java b/src/main/java/at/jotschi/quadtree/BoundsQuadTree.java new file mode 100644 index 0000000..afa40c6 --- /dev/null +++ b/src/main/java/at/jotschi/quadtree/BoundsQuadTree.java @@ -0,0 +1,75 @@ +package at.jotschi.quadtree; + +import java.awt.Dimension; +import java.awt.Point; + +/** + * Creates a new QuadTree that can hold the given type of elements. Each element + * can also have a certain dimension. + * + * @author jotschi + * + * @param + */ +public class BoundsQuadTree extends QuadTree { + + /** + * Create a new QuadTree with the give start coordinates and size + * + * @param startCorrdinates + * @param size + */ + public BoundsQuadTree(Point startCoordinates, Dimension size) { + + super(startCoordinates, size); + + this.rootNode = new BoundsNode(startCoordinates, size, 0); + } + + /** + * Add a new element to the QuadTree that has a specific dimension/size + * + * @param point + * @param size + * @param element + */ + public void insert(Point point, Dimension elementSize, T element) { + + // Check if the element coordinates are within bounds of the quadtree + if (point.x > startCoordinates.x + size.width + || point.x < startCoordinates.x) { + throw new IndexOutOfBoundsException( + "The x coordinate must be within bounds of [" + + startCoordinates.x + "] to [" + size.width + + "] / [" + point.x + "]"); + } + if (point.y > startCoordinates.y + size.height + || point.y < startCoordinates.y) { + throw new IndexOutOfBoundsException( + "The y coordinate must be within bounds of [" + + startCoordinates.y + "] to [" + size.height + + "] / [" + point.y + "]"); + } + + // Check if the right bottom corner is within bounds + if (point.x + elementSize.width > startCoordinates.x + size.width + || point.x < startCoordinates.x) { + throw new IndexOutOfBoundsException( + "The x coordinate must be within bounds of [" + + startCoordinates.x + "] to [" + size.width + + "] / [" + point.x + elementSize.width + "]"); + } + if (point.y + elementSize.height > startCoordinates.y + size.height + || point.y < startCoordinates.y) { + throw new IndexOutOfBoundsException( + "The y coordinate must be within bounds of [" + + startCoordinates.y + "] to [" + size.height + + "] / [" + point.x + elementSize.height + "]"); + } + + this.rootNode.insert(new BoundsNodeElement(point, elementSize, element)); + + } + + +} diff --git a/src/main/java/at/jotschi/quadtree/Node.java b/src/main/java/at/jotschi/quadtree/Node.java index 01bbd0f..7525c52 100644 --- a/src/main/java/at/jotschi/quadtree/Node.java +++ b/src/main/java/at/jotschi/quadtree/Node.java @@ -19,29 +19,29 @@ */ public class Node { - private static Logger log = Logger.getLogger(Node.class); + protected static Logger log = Logger.getLogger(Node.class); public static enum Cell { TOP_LEFT, BOTTOM_RIGHT, BOTTOM_LEFT, TOP_RIGHT } - private Dimension bounds; - private Point startCoordinates; - private int maxDepth; - private int maxElements; - private int depth; + protected Dimension bounds; + protected Point startCoordinates; + protected int maxDepth; + protected int maxElements; + protected int depth; /** * Default value for amount of elements */ - private final int MAX_ELEMENTS = 4; + protected final int MAX_ELEMENTS = 4; /** * Default value for max depth */ - private final int MAX_DEPTH = 4; + protected final int MAX_DEPTH = 4; - private Map> nodes = new HashMap>(); + protected Map> nodes = new HashMap>(); /** * Holds all elements for this node @@ -164,7 +164,7 @@ private Cell findIndex(Point coordinates) { * * @return */ - public Vector> getElements() { + public Vector> getElements() { return this.elements; } @@ -174,7 +174,7 @@ public Vector> getElements() { * @param coordinates * @return */ - public Vector> getElements(Point coordinates) { + public Vector> getElements(Point coordinates) { // Check if this node has already been subdivided. Therefor this node // should contain no elements diff --git a/src/main/java/at/jotschi/quadtree/NodeElement.java b/src/main/java/at/jotschi/quadtree/NodeElement.java index 4fa772a..8e5b2a2 100644 --- a/src/main/java/at/jotschi/quadtree/NodeElement.java +++ b/src/main/java/at/jotschi/quadtree/NodeElement.java @@ -8,10 +8,9 @@ * @author jotschi * */ +@SuppressWarnings("serial") public class NodeElement extends Point { - private static final long serialVersionUID = -6818452324965717494L; - private T element; /** diff --git a/src/main/java/at/jotschi/quadtree/QuadTree.java b/src/main/java/at/jotschi/quadtree/QuadTree.java index c7ea2dc..20de43b 100644 --- a/src/main/java/at/jotschi/quadtree/QuadTree.java +++ b/src/main/java/at/jotschi/quadtree/QuadTree.java @@ -13,9 +13,9 @@ */ public class QuadTree { - private Node rootNode; - private Dimension size; - private Point startCoordinates; + protected Node rootNode; + protected Dimension size; + protected Point startCoordinates; /** * Create a new QuadTree with the give start coordinates and size @@ -59,6 +59,48 @@ public void insert(int x, int y, T element) { insert(new Point(x, y), element); } + /** + * Add a new element to the QuadTree that has a specific dimension/size + * + * @param point + * @param size + * @param element + */ + public void insert(Point point, Dimension size, T element) { + + // Check if the element coordinates are within bounds of the quadtree + if (point.x > startCoordinates.x + size.width + || point.x < startCoordinates.x) { + throw new IndexOutOfBoundsException( + "The x coordinate must be within bounds of [" + + startCoordinates.x + "] to [" + size.width + "]"); + } + if (point.y > startCoordinates.y + size.height + || point.y < startCoordinates.y) { + throw new IndexOutOfBoundsException( + "The y coordinate must be within bounds of [" + + startCoordinates.y + "] to [" + size.height + "]"); + } + + //Check if the right bottom corner is within bounds + if (point.x+size.width > startCoordinates.x + size.width + || point.x < startCoordinates.x) { + throw new IndexOutOfBoundsException( + "The x coordinate must be within bounds of [" + + startCoordinates.x + "] to [" + size.width + "]"); + } + if (point.y+size.width > startCoordinates.y + size.height + || point.y < startCoordinates.y) { + throw new IndexOutOfBoundsException( + "The y coordinate must be within bounds of [" + + startCoordinates.y + "] to [" + size.height + "]"); + } + + + this.rootNode.insert(new NodeElement(point, element)); + + } + /** * Add a new element to the QuadTree * @@ -106,8 +148,7 @@ public Node getRootNode() { * @param coordinates * @return */ - public Vector> getElements(Point coordinates) { + public Vector> getElements(Point coordinates) { return this.rootNode.getElements(coordinates); - } } diff --git a/src/test/java/at/jotschi/quadtree/QuadTreePanel.java b/src/test/java/at/jotschi/quadtree/QuadTreePanel.java new file mode 100644 index 0000000..0e3f4a1 --- /dev/null +++ b/src/test/java/at/jotschi/quadtree/QuadTreePanel.java @@ -0,0 +1,170 @@ +package at.jotschi.quadtree; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Point; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.util.Map; +import java.util.Vector; + +import javax.swing.JFrame; +import javax.swing.JPanel; + +import org.apache.log4j.Logger; + +import at.jotschi.quadtree.Node.Cell; + +@SuppressWarnings("serial") +public class QuadTreePanel extends JPanel implements KeyListener, MouseListener { + + protected QuadTree tree; + protected Vector> selectedElements = new Vector>(); + + protected static Logger log = Logger.getLogger(QuadTreePanel.class); + + /** + * Create a new jframe and display it + */ + protected QuadTreePanel() { + tree = createQuadTree(); + setupGui(); + } + + protected void setupGui() { + JFrame f = new JFrame(); + f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + f.addKeyListener(this); + f.addMouseListener(this); + f.add(this); + f.setLocationRelativeTo(null); + + f.setSize(600, 600); + f.setVisible(true); + + } + + /** + * Create the QuadTree and add some random points + * + * @return + */ + private QuadTree createQuadTree() { + QuadTree tree = new QuadTree(new Point(100, 100), + new Dimension(400, 400)); + + for (int i = 0; i < 200; i++) { + int x = (int) (Math.random() * 1000 * 0.4) + 100; + int y = (int) (Math.random() * 1000 * 0.4) + 100; + System.out.println(x + " " + y); + tree.insert(x, y, "test"); + } + + return tree; + } + + @Override + protected void paintComponent(Graphics g) { + Node rootNode = tree.getRootNode(); + drawCells(rootNode, g); + } + + /** + * Draws the tree content + * + * @param node + * @param g + */ + private void drawCells(Node node, Graphics g) { + + Dimension bounds = node.getBounds(); + Point startCoordinates = node.getStartCoordinates(); + // Draw node bounds + g.drawRect(startCoordinates.x, startCoordinates.y, bounds.width, + bounds.height); + + // Draw subnodes + Map> subNodes = node.getSubNodes(); + for (Node subNode : subNodes.values()) { + drawCells(subNode, g); + } + + // Draw points of this node + Vector> elements = (Vector>) node + .getElements(); + for (NodeElement element : elements) { + g.drawOval((int) element.getX(), (int) element.getY(), 4, 4); + } + + // Draw selected elements + g.setColor(Color.RED); + + for (NodeElement element : selectedElements) { + g.drawOval((int) element.getX(), (int) element.getY(), 4, 4); + } + g.setColor(Color.BLACK); + + } + + public void keyTyped(KeyEvent e) { + // TODO Auto-generated method stub + + } + + public void keyPressed(KeyEvent e) { + // TODO Auto-generated method stub + + } + + public void keyReleased(KeyEvent e) { + + if (e.getKeyCode() == 27) { + System.exit(10); + } + + } + + public void mouseClicked(MouseEvent e) { + + Point p = e.getPoint(); + // Somehow my mousepoints do not match the coordinates at the window + // (maybe this is a xorg releated bug) + p.x -= 8; + p.y -= 30; + + if (e.getButton() == 1) { + try { + tree.insert(p, "MouseClick"); + } catch (IndexOutOfBoundsException e1) { + log.info("Out of bounds - omitting."); + } + } else if (e.getButton() == 3) { + selectedElements = (Vector>) tree + .getElements(p); + } + repaint(); + } + + public void mousePressed(MouseEvent e) { + // TODO Auto-generated method stub + + } + + public void mouseReleased(MouseEvent e) { + // TODO Auto-generated method stub + + } + + public void mouseEntered(MouseEvent e) { + // TODO Auto-generated method stub + + } + + public void mouseExited(MouseEvent e) { + // TODO Auto-generated method stub + + } +} diff --git a/src/test/java/at/jotschi/quadtree/QuadTreeTest.java b/src/test/java/at/jotschi/quadtree/QuadTreeTest.java index 2866bc0..1b2f53b 100644 --- a/src/test/java/at/jotschi/quadtree/QuadTreeTest.java +++ b/src/test/java/at/jotschi/quadtree/QuadTreeTest.java @@ -24,7 +24,7 @@ public void testApp() { tree.insert(552, 555, "6"); tree.insert(551, 555, "7"); - Vector> elements = tree.getElements(new Point(500, + Vector> elements = (Vector>) tree.getElements(new Point(500, 550)); for (NodeElement element : elements) { @@ -32,4 +32,17 @@ public void testApp() { + "] " + element.getElement()); } } + + @Test + public void quadTreeBoundsTest() { + BoundsQuadTree tree = new BoundsQuadTree(new Point(0, 0), + new Dimension(600, 600)); + tree.insert(new Point(11, 3),new Dimension(10,10), "1"); + tree.insert(new Point(22, 3),new Dimension(30,10), "2"); + tree.insert(new Point(33, 3),new Dimension(40,10), "3"); + + + + } + } diff --git a/src/test/java/at/jotschi/quadtree/RenderBoundsQuadTree.java b/src/test/java/at/jotschi/quadtree/RenderBoundsQuadTree.java new file mode 100644 index 0000000..7003d82 --- /dev/null +++ b/src/test/java/at/jotschi/quadtree/RenderBoundsQuadTree.java @@ -0,0 +1,41 @@ +package at.jotschi.quadtree; + +import java.awt.Dimension; +import java.awt.Point; + +@SuppressWarnings("serial") +public class RenderBoundsQuadTree extends QuadTreePanel { + + public static void main(String[] args) { + new RenderBoundsQuadTree(); + } + + /** + * Create a new jframe and display it + */ + protected RenderBoundsQuadTree() { + tree = createQuadTree(); + setupGui(); + } + + /** + * Create the QuadTree and add some random points + * + * @return + */ + private QuadTree createQuadTree() { + BoundsQuadTree tree = new BoundsQuadTree(new Point(100, + 100), new Dimension(400, 400)); + + for (int i = 0; i < 200; i++) { + int x = (int) (Math.random() * 1000 * 0.4) + 100; + int y = (int) (Math.random() * 1000 * 0.4) + 100; + Point point = new Point(x, y); + Dimension dimension = new Dimension(50, 50); + tree.insert(point, dimension, "test"); + } + + return tree; + } + +} diff --git a/src/test/java/at/jotschi/quadtree/RenderQuadTree.java b/src/test/java/at/jotschi/quadtree/RenderQuadTree.java index 788a911..bce4730 100644 --- a/src/test/java/at/jotschi/quadtree/RenderQuadTree.java +++ b/src/test/java/at/jotschi/quadtree/RenderQuadTree.java @@ -1,168 +1,12 @@ package at.jotschi.quadtree; -import java.awt.Color; -import java.awt.Dimension; -import java.awt.Graphics; -import java.awt.Point; -import java.awt.event.KeyEvent; -import java.awt.event.KeyListener; -import java.awt.event.MouseEvent; -import java.awt.event.MouseListener; -import java.util.Map; -import java.util.Vector; - -import javax.swing.JFrame; -import javax.swing.JPanel; - -import org.apache.log4j.Logger; - -import at.jotschi.quadtree.Node.Cell; @SuppressWarnings("serial") -public class RenderQuadTree extends JPanel implements KeyListener, - MouseListener { - - private QuadTree tree; - Vector> selectedElements = new Vector>(); - - private static Logger log = Logger.getLogger(RenderQuadTree.class); - - /** - * Create a new jframe and display it - */ - private RenderQuadTree() { - JFrame f = new JFrame(); - tree = createQuadTree(); - f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - f.addKeyListener(this); - f.addMouseListener(this); - f.add(this); - f.setLocationRelativeTo(null); - - f.setSize(600, 600); - f.setVisible(true); - - } - - /** - * Create the QuadTree and add some random points - * @return - */ - private QuadTree createQuadTree() { - QuadTree tree = new QuadTree(new Point(100, 100), - new Dimension(400, 400)); - - for (int i = 0; i < 200; i++) { - int x = (int) (Math.random() * 1000 * 0.4) + 100; - int y = (int) (Math.random() * 1000 * 0.4) + 100; - System.out.println(x + " " + y); - tree.insert(x, y, "test"); - } - - return tree; - } - - @Override - protected void paintComponent(Graphics g) { - Node rootNode = tree.getRootNode(); - drawCells(rootNode, g); - } - - /** - * Draws the tree content - * - * @param node - * @param g - */ - private void drawCells(Node node, Graphics g) { - - Dimension bounds = node.getBounds(); - Point startCoordinates = node.getStartCoordinates(); - // Draw node bounds - g.drawRect(startCoordinates.x, startCoordinates.y, bounds.width, - bounds.height); - - // Draw subnodes - Map> subNodes = node.getSubNodes(); - for (Node subNode : subNodes.values()) { - drawCells(subNode, g); - } - - // Draw points of this node - Vector> elements = node.getElements(); - for (NodeElement element : elements) { - g.drawOval((int) element.getX(), (int) element.getY(), 4, 4); - } - - // Draw selected elements - g.setColor(Color.RED); - - for (NodeElement element : selectedElements) { - g.drawOval((int) element.getX(), (int) element.getY(), 4, 4); - } - g.setColor(Color.BLACK); - - } +public class RenderQuadTree extends QuadTreePanel { public static void main(String[] args) { new RenderQuadTree(); } - public void keyTyped(KeyEvent e) { - // TODO Auto-generated method stub - - } - - public void keyPressed(KeyEvent e) { - // TODO Auto-generated method stub - - } - - public void keyReleased(KeyEvent e) { - - if (e.getKeyCode() == 27) { - System.exit(10); - } - - } - - public void mouseClicked(MouseEvent e) { - - Point p = e.getPoint(); - // Somehow my mousepoints do not match the coordinates at the window - // (maybe this is a xorg releated bug) - p.x -= 8; - p.y -= 30; - - if (e.getButton() == 1) { - try { - tree.insert(p, "MouseClick"); - } catch (IndexOutOfBoundsException e1) { - log.info("Out of bounds - omitting."); - } - } else if (e.getButton() == 3) { - selectedElements = tree.getElements(p); - } - repaint(); - } - - public void mousePressed(MouseEvent e) { - // TODO Auto-generated method stub - - } - - public void mouseReleased(MouseEvent e) { - // TODO Auto-generated method stub - - } - - public void mouseEntered(MouseEvent e) { - // TODO Auto-generated method stub - - } - - public void mouseExited(MouseEvent e) { - // TODO Auto-generated method stub - - } + }