Bukkit  1.5.2-R1.0
 All Classes Namespaces Files Functions Variables Enumerator Pages
BlockIterator.java
Go to the documentation of this file.
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;
8 import org.bukkit.block.BlockFace;
9 import org.bukkit.entity.LivingEntity;
10 
11 import java.util.Iterator;
12 import java.util.NoSuchElementException;
13 
18 public class BlockIterator implements Iterator<Block> {
19 
20  private final World world;
21  private final int maxDistance;
22 
23  private static final int gridSize = 1 << 24;
24 
25  private boolean end = false;
26 
27  private Block[] blockQueue = new Block[3];
28  private int currentBlock = 0;
29  private int currentDistance = 0;
30  private int maxDistanceInt;
31 
32  private int secondError;
33  private int thirdError;
34 
35  private int secondStep;
36  private int thirdStep;
37 
38  private BlockFace mainFace;
39  private BlockFace secondFace;
40  private BlockFace thirdFace;
41 
53  public BlockIterator(World world, Vector start, Vector direction, double yOffset, int maxDistance) {
54  this.world = world;
55  this.maxDistance = maxDistance;
56 
57  Vector startClone = start.clone();
58 
59  startClone.setY(startClone.getY() + yOffset);
60 
61  currentDistance = 0;
62 
63  double mainDirection = 0;
64  double secondDirection = 0;
65  double thirdDirection = 0;
66 
67  double mainPosition = 0;
68  double secondPosition = 0;
69  double thirdPosition = 0;
70 
71  Block startBlock = this.world.getBlockAt(floor(startClone.getX()), floor(startClone.getY()), floor(startClone.getZ()));
72 
73  if (getXLength(direction) > mainDirection) {
74  mainFace = getXFace(direction);
75  mainDirection = getXLength(direction);
76  mainPosition = getXPosition(direction, startClone, startBlock);
77 
78  secondFace = getYFace(direction);
79  secondDirection = getYLength(direction);
80  secondPosition = getYPosition(direction, startClone, startBlock);
81 
82  thirdFace = getZFace(direction);
83  thirdDirection = getZLength(direction);
84  thirdPosition = getZPosition(direction, startClone, startBlock);
85  }
86  if (getYLength(direction) > mainDirection) {
87  mainFace = getYFace(direction);
88  mainDirection = getYLength(direction);
89  mainPosition = getYPosition(direction, startClone, startBlock);
90 
91  secondFace = getZFace(direction);
92  secondDirection = getZLength(direction);
93  secondPosition = getZPosition(direction, startClone, startBlock);
94 
95  thirdFace = getXFace(direction);
96  thirdDirection = getXLength(direction);
97  thirdPosition = getXPosition(direction, startClone, startBlock);
98  }
99  if (getZLength(direction) > mainDirection) {
100  mainFace = getZFace(direction);
101  mainDirection = getZLength(direction);
102  mainPosition = getZPosition(direction, startClone, startBlock);
103 
104  secondFace = getXFace(direction);
105  secondDirection = getXLength(direction);
106  secondPosition = getXPosition(direction, startClone, startBlock);
107 
108  thirdFace = getYFace(direction);
109  thirdDirection = getYLength(direction);
110  thirdPosition = getYPosition(direction, startClone, startBlock);
111  }
112 
113  // trace line backwards to find intercept with plane perpendicular to the main axis
114 
115  double d = mainPosition / mainDirection; // how far to hit face behind
116  double secondd = secondPosition - secondDirection * d;
117  double thirdd = thirdPosition - thirdDirection * d;
118 
119  // Guarantee that the ray will pass though the start block.
120  // It is possible that it would miss due to rounding
121  // This should only move the ray by 1 grid position
122  secondError = floor(secondd * gridSize);
123  secondStep = round(secondDirection / mainDirection * gridSize);
124  thirdError = floor(thirdd * gridSize);
125  thirdStep = round(thirdDirection / mainDirection * gridSize);
126 
127  if (secondError + secondStep <= 0) {
128  secondError = -secondStep + 1;
129  }
130 
131  if (thirdError + thirdStep <= 0) {
132  thirdError = -thirdStep + 1;
133  }
134 
135  Block lastBlock;
136 
137  lastBlock = startBlock.getRelative(mainFace.getOppositeFace());
138 
139  if (secondError < 0) {
140  secondError += gridSize;
141  lastBlock = lastBlock.getRelative(secondFace.getOppositeFace());
142  }
143 
144  if (thirdError < 0) {
145  thirdError += gridSize;
146  lastBlock = lastBlock.getRelative(thirdFace.getOppositeFace());
147  }
148 
149  // This means that when the variables are positive, it means that the coord=1 boundary has been crossed
150  secondError -= gridSize;
151  thirdError -= gridSize;
152 
153  blockQueue[0] = lastBlock;
154  currentBlock = -1;
155 
156  scan();
157 
158  boolean startBlockFound = false;
159 
160  for (int cnt = currentBlock; cnt >= 0; cnt--) {
161  if (blockEquals(blockQueue[cnt], startBlock)) {
162  currentBlock = cnt;
163  startBlockFound = true;
164  break;
165  }
166  }
167 
168  if (!startBlockFound) {
169  throw new IllegalStateException("Start block missed in BlockIterator");
170  }
171 
172  // Calculate the number of planes passed to give max distance
173  maxDistanceInt = round(maxDistance / (Math.sqrt(mainDirection * mainDirection + secondDirection * secondDirection + thirdDirection * thirdDirection) / mainDirection));
174 
175  }
176 
177  private boolean blockEquals(Block a, Block b) {
178  return a.getX() == b.getX() && a.getY() == b.getY() && a.getZ() == b.getZ();
179  }
180 
181  private BlockFace getXFace(Vector direction) {
182  return ((direction.getX() > 0) ? BlockFace.EAST : BlockFace.WEST);
183  }
184 
185  private BlockFace getYFace(Vector direction) {
186  return ((direction.getY() > 0) ? BlockFace.UP : BlockFace.DOWN);
187  }
188 
189  private BlockFace getZFace(Vector direction) {
190  return ((direction.getZ() > 0) ? BlockFace.SOUTH : BlockFace.NORTH);
191  }
192 
193  private double getXLength(Vector direction) {
194  return Math.abs(direction.getX());
195  }
196 
197  private double getYLength(Vector direction) {
198  return Math.abs(direction.getY());
199  }
200 
201  private double getZLength(Vector direction) {
202  return Math.abs(direction.getZ());
203  }
204 
205  private double getPosition(double direction, double position, int blockPosition) {
206  return direction > 0 ? (position - blockPosition) : (blockPosition + 1 - position);
207  }
208 
209  private double getXPosition(Vector direction, Vector position, Block block) {
210  return getPosition(direction.getX(), position.getX(), block.getX());
211  }
212 
213  private double getYPosition(Vector direction, Vector position, Block block) {
214  return getPosition(direction.getY(), position.getY(), block.getY());
215  }
216 
217  private double getZPosition(Vector direction, Vector position, Block block) {
218  return getPosition(direction.getZ(), position.getZ(), block.getZ());
219  }
220 
230  public BlockIterator(Location loc, double yOffset, int maxDistance) {
231  this(loc.getWorld(), loc.toVector(), loc.getDirection(), yOffset, maxDistance);
232  }
233 
241  public BlockIterator(Location loc, double yOffset) {
242  this(loc.getWorld(), loc.toVector(), loc.getDirection(), yOffset, 0);
243  }
244 
251  public BlockIterator(Location loc) {
252  this(loc, 0D);
253  }
254 
263  public BlockIterator(LivingEntity entity, int maxDistance) {
264  this(entity.getLocation(), entity.getEyeHeight(), maxDistance);
265  }
266 
273  public BlockIterator(LivingEntity entity) {
274  this(entity, 0);
275  }
276 
281  public boolean hasNext() {
282  scan();
283  return currentBlock != -1;
284  }
285 
292  public Block next() {
293  scan();
294  if (currentBlock <= -1) {
295  throw new NoSuchElementException();
296  } else {
297  return blockQueue[currentBlock--];
298  }
299  }
300 
301  public void remove() {
302  throw new UnsupportedOperationException("[BlockIterator] doesn't support block removal");
303  }
304 
305  private void scan() {
306  if (currentBlock >= 0) {
307  return;
308  }
309  if (maxDistance != 0 && currentDistance > maxDistanceInt) {
310  end = true;
311  return;
312  }
313  if (end) {
314  return;
315  }
316 
317  currentDistance++;
318 
319  secondError += secondStep;
320  thirdError += thirdStep;
321 
322  if (secondError > 0 && thirdError > 0) {
323  blockQueue[2] = blockQueue[0].getRelative(mainFace);
324  if (((long) secondStep) * ((long) thirdError) < ((long) thirdStep) * ((long) secondError)) {
325  blockQueue[1] = blockQueue[2].getRelative(secondFace);
326  blockQueue[0] = blockQueue[1].getRelative(thirdFace);
327  } else {
328  blockQueue[1] = blockQueue[2].getRelative(thirdFace);
329  blockQueue[0] = blockQueue[1].getRelative(secondFace);
330  }
331  thirdError -= gridSize;
332  secondError -= gridSize;
333  currentBlock = 2;
334  return;
335  } else if (secondError > 0) {
336  blockQueue[1] = blockQueue[0].getRelative(mainFace);
337  blockQueue[0] = blockQueue[1].getRelative(secondFace);
338  secondError -= gridSize;
339  currentBlock = 1;
340  return;
341  } else if (thirdError > 0) {
342  blockQueue[1] = blockQueue[0].getRelative(mainFace);
343  blockQueue[0] = blockQueue[1].getRelative(thirdFace);
344  thirdError -= gridSize;
345  currentBlock = 1;
346  return;
347  } else {
348  blockQueue[0] = blockQueue[0].getRelative(mainFace);
349  currentBlock = 0;
350  return;
351  }
352  }
353 }