Bukkit-API  1.7.9-R0.2
The inofficial Bukkit-API
BlockIterator.java
1 package org.bukkit.util;
2 
3 import static org.bukkit.util.NumberConversions.*;
4 
5 import org.bukkit.World;
6 import org.bukkit.Location;
7 import org.bukkit.block.Block;
10 
11 import java.util.Iterator;
12 import java.util.NoSuchElementException;
13 
14 /**
15  * This class performs ray tracing and iterates along blocks on a line
16  */
17 public class BlockIterator implements Iterator<Block> {
18 
19  private final World world;
20  private final int maxDistance;
21 
22  private static final int gridSize = 1 << 24;
23 
24  private boolean end = false;
25 
26  private Block[] blockQueue = new Block[3];
27  private int currentBlock = 0;
28  private int currentDistance = 0;
29  private int maxDistanceInt;
30 
31  private int secondError;
32  private int thirdError;
33 
34  private int secondStep;
35  private int thirdStep;
36 
37  private BlockFace mainFace;
38  private BlockFace secondFace;
39  private BlockFace thirdFace;
40 
41  /**
42  * Constructs the BlockIterator
43  *
44  * @param world The world to use for tracing
45  * @param start A Vector giving the initial location for the trace
46  * @param direction A Vector pointing in the direction for the trace
47  * @param yOffset The trace begins vertically offset from the start vector
48  * by this value
49  * @param maxDistance This is the maximum distance in blocks for the
50  * trace. Setting this value above 140 may lead to problems with
51  * unloaded chunks. A value of 0 indicates no limit
52  *
53  */
54  public BlockIterator(World world, Vector start, Vector direction, double yOffset, int maxDistance) {
55  this.world = world;
56  this.maxDistance = maxDistance;
57 
58  Vector startClone = start.clone();
59 
60  startClone.setY(startClone.getY() + yOffset);
61 
62  currentDistance = 0;
63 
64  double mainDirection = 0;
65  double secondDirection = 0;
66  double thirdDirection = 0;
67 
68  double mainPosition = 0;
69  double secondPosition = 0;
70  double thirdPosition = 0;
71 
72  Block startBlock = this.world.getBlockAt(floor(startClone.getX()), floor(startClone.getY()), floor(startClone.getZ()));
73 
74  if (getXLength(direction) > mainDirection) {
75  mainFace = getXFace(direction);
76  mainDirection = getXLength(direction);
77  mainPosition = getXPosition(direction, startClone, startBlock);
78 
79  secondFace = getYFace(direction);
80  secondDirection = getYLength(direction);
81  secondPosition = getYPosition(direction, startClone, startBlock);
82 
83  thirdFace = getZFace(direction);
84  thirdDirection = getZLength(direction);
85  thirdPosition = getZPosition(direction, startClone, startBlock);
86  }
87  if (getYLength(direction) > mainDirection) {
88  mainFace = getYFace(direction);
89  mainDirection = getYLength(direction);
90  mainPosition = getYPosition(direction, startClone, startBlock);
91 
92  secondFace = getZFace(direction);
93  secondDirection = getZLength(direction);
94  secondPosition = getZPosition(direction, startClone, startBlock);
95 
96  thirdFace = getXFace(direction);
97  thirdDirection = getXLength(direction);
98  thirdPosition = getXPosition(direction, startClone, startBlock);
99  }
100  if (getZLength(direction) > mainDirection) {
101  mainFace = getZFace(direction);
102  mainDirection = getZLength(direction);
103  mainPosition = getZPosition(direction, startClone, startBlock);
104 
105  secondFace = getXFace(direction);
106  secondDirection = getXLength(direction);
107  secondPosition = getXPosition(direction, startClone, startBlock);
108 
109  thirdFace = getYFace(direction);
110  thirdDirection = getYLength(direction);
111  thirdPosition = getYPosition(direction, startClone, startBlock);
112  }
113 
114  // trace line backwards to find intercept with plane perpendicular to the main axis
115 
116  double d = mainPosition / mainDirection; // how far to hit face behind
117  double secondd = secondPosition - secondDirection * d;
118  double thirdd = thirdPosition - thirdDirection * d;
119 
120  // Guarantee that the ray will pass though the start block.
121  // It is possible that it would miss due to rounding
122  // This should only move the ray by 1 grid position
123  secondError = floor(secondd * gridSize);
124  secondStep = round(secondDirection / mainDirection * gridSize);
125  thirdError = floor(thirdd * gridSize);
126  thirdStep = round(thirdDirection / mainDirection * gridSize);
127 
128  if (secondError + secondStep <= 0) {
129  secondError = -secondStep + 1;
130  }
131 
132  if (thirdError + thirdStep <= 0) {
133  thirdError = -thirdStep + 1;
134  }
135 
136  Block lastBlock;
137 
138  lastBlock = startBlock.getRelative(mainFace.getOppositeFace());
139 
140  if (secondError < 0) {
141  secondError += gridSize;
142  lastBlock = lastBlock.getRelative(secondFace.getOppositeFace());
143  }
144 
145  if (thirdError < 0) {
146  thirdError += gridSize;
147  lastBlock = lastBlock.getRelative(thirdFace.getOppositeFace());
148  }
149 
150  // This means that when the variables are positive, it means that the coord=1 boundary has been crossed
151  secondError -= gridSize;
152  thirdError -= gridSize;
153 
154  blockQueue[0] = lastBlock;
155  currentBlock = -1;
156 
157  scan();
158 
159  boolean startBlockFound = false;
160 
161  for (int cnt = currentBlock; cnt >= 0; cnt--) {
162  if (blockEquals(blockQueue[cnt], startBlock)) {
163  currentBlock = cnt;
164  startBlockFound = true;
165  break;
166  }
167  }
168 
169  if (!startBlockFound) {
170  throw new IllegalStateException("Start block missed in BlockIterator");
171  }
172 
173  // Calculate the number of planes passed to give max distance
174  maxDistanceInt = round(maxDistance / (Math.sqrt(mainDirection * mainDirection + secondDirection * secondDirection + thirdDirection * thirdDirection) / mainDirection));
175 
176  }
177 
178  private boolean blockEquals(Block a, Block b) {
179  return a.getX() == b.getX() && a.getY() == b.getY() && a.getZ() == b.getZ();
180  }
181 
182  private BlockFace getXFace(Vector direction) {
183  return ((direction.getX() > 0) ? BlockFace.EAST : BlockFace.WEST);
184  }
185 
186  private BlockFace getYFace(Vector direction) {
187  return ((direction.getY() > 0) ? BlockFace.UP : BlockFace.DOWN);
188  }
189 
190  private BlockFace getZFace(Vector direction) {
191  return ((direction.getZ() > 0) ? BlockFace.SOUTH : BlockFace.NORTH);
192  }
193 
194  private double getXLength(Vector direction) {
195  return Math.abs(direction.getX());
196  }
197 
198  private double getYLength(Vector direction) {
199  return Math.abs(direction.getY());
200  }
201 
202  private double getZLength(Vector direction) {
203  return Math.abs(direction.getZ());
204  }
205 
206  private double getPosition(double direction, double position, int blockPosition) {
207  return direction > 0 ? (position - blockPosition) : (blockPosition + 1 - position);
208  }
209 
210  private double getXPosition(Vector direction, Vector position, Block block) {
211  return getPosition(direction.getX(), position.getX(), block.getX());
212  }
213 
214  private double getYPosition(Vector direction, Vector position, Block block) {
215  return getPosition(direction.getY(), position.getY(), block.getY());
216  }
217 
218  private double getZPosition(Vector direction, Vector position, Block block) {
219  return getPosition(direction.getZ(), position.getZ(), block.getZ());
220  }
221 
222  /**
223  * Constructs the BlockIterator
224  *
225  * @param loc The location for the start of the ray trace
226  * @param yOffset The trace begins vertically offset from the start vector
227  * by this value
228  * @param maxDistance This is the maximum distance in blocks for the
229  * trace. Setting this value above 140 may lead to problems with
230  * unloaded chunks. A value of 0 indicates no limit
231  */
232  public BlockIterator(Location loc, double yOffset, int maxDistance) {
233  this(loc.getWorld(), loc.toVector(), loc.getDirection(), yOffset, maxDistance);
234  }
235 
236  /**
237  * Constructs the BlockIterator.
238  *
239  * @param loc The location for the start of the ray trace
240  * @param yOffset The trace begins vertically offset from the start vector
241  * by this value
242  */
243 
244  public BlockIterator(Location loc, double yOffset) {
245  this(loc.getWorld(), loc.toVector(), loc.getDirection(), yOffset, 0);
246  }
247 
248  /**
249  * Constructs the BlockIterator.
250  *
251  * @param loc The location for the start of the ray trace
252  */
253 
254  public BlockIterator(Location loc) {
255  this(loc, 0D);
256  }
257 
258  /**
259  * Constructs the BlockIterator.
260  *
261  * @param entity Information from the entity is used to set up the trace
262  * @param maxDistance This is the maximum distance in blocks for the
263  * trace. Setting this value above 140 may lead to problems with
264  * unloaded chunks. A value of 0 indicates no limit
265  */
266 
267  public BlockIterator(LivingEntity entity, int maxDistance) {
268  this(entity.getLocation(), entity.getEyeHeight(), maxDistance);
269  }
270 
271  /**
272  * Constructs the BlockIterator.
273  *
274  * @param entity Information from the entity is used to set up the trace
275  */
276 
277  public BlockIterator(LivingEntity entity) {
278  this(entity, 0);
279  }
280 
281  /**
282  * Returns true if the iteration has more elements
283  */
284 
285  public boolean hasNext() {
286  scan();
287  return currentBlock != -1;
288  }
289 
290  /**
291  * Returns the next Block in the trace
292  *
293  * @return the next Block in the trace
294  */
295 
296  public Block next() {
297  scan();
298  if (currentBlock <= -1) {
299  throw new NoSuchElementException();
300  } else {
301  return blockQueue[currentBlock--];
302  }
303  }
304 
305  public void remove() {
306  throw new UnsupportedOperationException("[BlockIterator] doesn't support block removal");
307  }
308 
309  private void scan() {
310  if (currentBlock >= 0) {
311  return;
312  }
313  if (maxDistance != 0 && currentDistance > maxDistanceInt) {
314  end = true;
315  return;
316  }
317  if (end) {
318  return;
319  }
320 
321  currentDistance++;
322 
323  secondError += secondStep;
324  thirdError += thirdStep;
325 
326  if (secondError > 0 && thirdError > 0) {
327  blockQueue[2] = blockQueue[0].getRelative(mainFace);
328  if (((long) secondStep) * ((long) thirdError) < ((long) thirdStep) * ((long) secondError)) {
329  blockQueue[1] = blockQueue[2].getRelative(secondFace);
330  blockQueue[0] = blockQueue[1].getRelative(thirdFace);
331  } else {
332  blockQueue[1] = blockQueue[2].getRelative(thirdFace);
333  blockQueue[0] = blockQueue[1].getRelative(secondFace);
334  }
335  thirdError -= gridSize;
336  secondError -= gridSize;
337  currentBlock = 2;
338  return;
339  } else if (secondError > 0) {
340  blockQueue[1] = blockQueue[0].getRelative(mainFace);
341  blockQueue[0] = blockQueue[1].getRelative(secondFace);
342  secondError -= gridSize;
343  currentBlock = 1;
344  return;
345  } else if (thirdError > 0) {
346  blockQueue[1] = blockQueue[0].getRelative(mainFace);
347  blockQueue[0] = blockQueue[1].getRelative(thirdFace);
348  thirdError -= gridSize;
349  currentBlock = 1;
350  return;
351  } else {
352  blockQueue[0] = blockQueue[0].getRelative(mainFace);
353  currentBlock = 0;
354  return;
355  }
356  }
357 }
BlockIterator(Location loc, double yOffset, int maxDistance)
BlockIterator(LivingEntity entity)
Block getRelative(int modX, int modY, int modZ)
Block getBlockAt(int x, int y, int z)
BlockIterator(World world, Vector start, Vector direction, double yOffset, int maxDistance)
Vector getDirection()
Definition: Location.java:244
Vector setY(int y)
Definition: Vector.java:453
BlockIterator(LivingEntity entity, int maxDistance)
BlockIterator(Location loc, double yOffset)