View Javadoc
1   package com.jed.core;
2   
3   import com.jed.actor.AbstractEntity;
4   import com.jed.util.Rectangle;
5   import com.jed.util.Vector2f;
6   import org.lwjgl.opengl.GL11;
7   import org.slf4j.Logger;
8   import org.slf4j.LoggerFactory;
9   
10  import javax.annotation.Nonnull;
11  import java.util.ArrayList;
12  import java.util.List;
13  
14  /**
15   * 
16   * @author jlinde, Peter Colapietro
17   *
18   */
19  public class QuadTree implements Displayable {
20  
21      /**
22       *
23       */
24      private static final Logger LOGGER = LoggerFactory.getLogger(QuadTree.class);
25  
26      /**
27       * 
28       */
29      private static final int MAX_OBJECTS = 2;
30      
31      /**
32       * 
33       */
34      private static final int MAX_LEVELS = 5;
35  
36      /**
37       * 
38       */
39      private final int level;
40      
41      /**
42       * 
43       */
44      @Nonnull
45      private final List<AbstractEntity> objects;
46      
47      /**
48       * 
49       */
50      private final Rectangle rectangle;
51      
52      /**
53       * 
54       */
55      @Nonnull
56      private final QuadTree[] nodes;
57  
58      /**
59       * 
60       */
61      private final Displayable parent;
62  
63      /**
64       * 
65       */
66      private final Vector2f position;
67  
68      /**
69       * 
70       * @param position position vector
71       * @param level level or quad tree
72       * @param rectangle rectangle
73       * @param parent parent
74       */
75      public QuadTree(Vector2f position, int level, Rectangle rectangle, Displayable parent) {
76          this.position = position;
77          this.level = level;
78          this.objects = new ArrayList<>();
79          this.rectangle = rectangle;
80          this.nodes = new QuadTree[4];
81          this.parent = parent;
82      }
83  
84      /**
85       * 
86       */
87      public void clear() {
88          objects.clear();
89          for (int i = 0; i < nodes.length; i++) {
90              if (nodes[i] != null) {
91                  nodes[i].clear();
92              }
93              nodes[i] = null;
94          }
95      }
96  
97      /**
98       * 
99       * @param returnObjects list of entities
100      * @param o other entity
101      */
102     public void retrieve(@Nonnull List<AbstractEntity> returnObjects, @Nonnull AbstractEntity o) {
103         int index = getIndex(o);
104         if (index != -1) {
105             if (nodes[0] != null) {
106                 nodes[index].retrieve(returnObjects, o);
107             }
108             returnObjects.addAll(objects);
109         } else {
110             returnObjects.addAll(getObjects());
111         }
112     }
113 
114     /**
115      * 
116      * @return ret
117      */
118     @Nonnull
119     List<AbstractEntity> getObjects() {
120         List<AbstractEntity> ret = new ArrayList<>();
121         ret.addAll(objects);
122         if (nodes[0] != null) {
123             ret.addAll(nodes[0].getObjects());
124             ret.addAll(nodes[1].getObjects());
125             ret.addAll(nodes[2].getObjects());
126             ret.addAll(nodes[3].getObjects());
127         }
128         return ret;
129     }
130 
131     /**
132      * 
133      * @param o other entity
134      * @return index in quad tree, range is 0-3 inclusive.
135      */
136     private int getIndex(@Nonnull AbstractEntity o) {
137         int verticalMidpoint = (int) (position.x + (rectangle.getWidth() / 2));
138         int horizontalMidpoint = (int) (position.y + (rectangle.getHeight() / 2));
139 
140         boolean topQuadrant = o.getBounds().getNextWorldPosition().y + o.getBounds().getHeight() < horizontalMidpoint;
141         boolean bottomQuadrant = o.getBounds().getNextWorldPosition().y > horizontalMidpoint;
142 
143         if (o.getBounds().getNextWorldPosition().x + o.getBounds().getWidth() < verticalMidpoint) {
144             if (topQuadrant) {
145                 return 1;
146             } else if (bottomQuadrant) {
147                 return 2;
148             }
149         } else if (o.getBounds().getNextWorldPosition().x > verticalMidpoint) {
150             if (topQuadrant) {
151                 return 0;
152             } else if (bottomQuadrant) {
153                 return 3;
154             }
155         }
156 
157         return -1;
158     }
159 
160     /**
161      * 
162      */
163     private void split() {
164         //TODO START pc 2014-10-31: Test me
165         float halfWidth = rectangle.getWidth() / 2.0f;
166         int subWidth = Math.round(halfWidth);
167         float halfHeight = rectangle.getHeight() / 2.0f;
168         int subHeight = Math.round(halfHeight);
169         //TODO END pc 2014-10-31: Test me
170         int x = Math.round(position.x);
171         int y = Math.round(position.y);
172 
173         Rectangle rect = new Rectangle(subWidth, subHeight);
174 
175         this.nodes[0] = new QuadTree(new Vector2f(x + subWidth, y), this.level + 1, rect, parent);
176         this.nodes[1] = new QuadTree(new Vector2f(x, y), this.level + 1, rect, parent);
177         this.nodes[2] = new QuadTree(new Vector2f(x, y + subHeight), this.level + 1, rect, parent);
178         this.nodes[3] = new QuadTree(new Vector2f(x + subWidth, y + subHeight), this.level + 1, rect, parent);
179     }
180 
181     /**
182      * 
183      * @param o other entity to insert
184      */
185     public void insert(@Nonnull AbstractEntity o) {
186         if (nodes[0] != null) {
187             int index = getIndex(o);
188 
189             if (index != -1) {
190                 this.nodes[index].insert(o);
191                 return;
192             }
193         }
194 
195         objects.add(o);
196 
197         if (objects.size() > MAX_OBJECTS && level < MAX_LEVELS) {
198             if (nodes[0] == null) {
199                 split();
200             }
201 
202             int i = 0;
203             while (i < objects.size()) {
204                 int index = getIndex(objects.get(i));
205                 if (index != -1) {
206                     nodes[index].insert(objects.remove(i));
207                 } else {
208                     i++;
209                 }
210             }
211         }
212     }
213 
214     @Override
215     public void render() {
216         GL11.glColor3f(0.5f, 0.5f, 1.0f);
217 
218         GL11.glBegin(GL11.GL_LINE_LOOP);
219 
220         parent.drawChildVertex2f(position.x, position.y);
221         parent.drawChildVertex2f(position.x + rectangle.getWidth(), position.y);
222         parent.drawChildVertex2f(position.x + rectangle.getWidth(), position.y + rectangle.getHeight());
223         parent.drawChildVertex2f(position.x + rectangle.getWidth(), position.y + rectangle.getHeight());
224         parent.drawChildVertex2f(position.x, position.y + rectangle.getHeight());
225 
226         GL11.glEnd();
227 
228         if (nodes[0] != null) {
229             for (QuadTree eachNode : nodes) {
230                 eachNode.render();
231             }
232         }
233     }
234 
235     @Override
236     public void drawChildVertex2f(float x, float y) {
237         LOGGER.warn("{}","No OP com.jed.core.QuadTree#drawChildVertex2f");
238     }
239 }