package cn.nukkit.utils; import cn.nukkit.block.Block; import cn.nukkit.level.Level; import cn.nukkit.math.BlockFace; import cn.nukkit.math.Vector3; import java.util.Iterator; /** * author: MagicDroidX * Nukkit Project */ public class BlockIterator implements Iterator<Block> { private final Level level; private final int maxDistance; private static final int gridSize = 1 << 24; private boolean end = false; private final Block[] blockQueue; private int currentBlock = 0; private Block currentBlockObject = null; private int currentDistance; private int maxDistanceInt = 0; private int secondError; private int thirdError; private final int secondStep; private final int thirdStep; private BlockFace mainFace; private BlockFace secondFace; private BlockFace thirdFace; public BlockIterator(Level level, Vector3 start, Vector3 direction) { this(level, start, direction, 0); } public BlockIterator(Level level, Vector3 start, Vector3 direction, double yOffset) { this(level, start, direction, yOffset, 0); } public BlockIterator(Level level, Vector3 start, Vector3 direction, double yOffset, int maxDistance) { this.level = level; this.maxDistance = maxDistance; this.blockQueue = new Block[3]; Vector3 startClone = new Vector3(start.x, start.y, start.z); startClone.y += yOffset; this.currentDistance = 0; double mainDirection = 0; double secondDirection = 0; double thirdDirection = 0; double mainPosition = 0; double secondPosition = 0; double thirdPosition = 0; Vector3 pos = new Vector3(startClone.x, startClone.y, startClone.z); Block startBlock = this.level.getBlock(new Vector3(Math.floor(pos.x), Math.floor(pos.y), Math.floor(pos.z))); if (this.getXLength(direction) > mainDirection) { this.mainFace = this.getXFace(direction); mainDirection = this.getXLength(direction); mainPosition = this.getXPosition(direction, startClone, startBlock); this.secondFace = this.getYFace(direction); secondDirection = this.getYLength(direction); secondPosition = this.getYPosition(direction, startClone, startBlock); this.thirdFace = this.getZFace(direction); thirdDirection = this.getZLength(direction); thirdPosition = this.getZPosition(direction, startClone, startBlock); } if (this.getYLength(direction) > mainDirection) { this.mainFace = this.getYFace(direction); mainDirection = this.getYLength(direction); mainPosition = this.getYPosition(direction, startClone, startBlock); this.secondFace = this.getZFace(direction); secondDirection = this.getZLength(direction); secondPosition = this.getZPosition(direction, startClone, startBlock); this.thirdFace = this.getXFace(direction); thirdDirection = this.getXLength(direction); thirdPosition = this.getXPosition(direction, startClone, startBlock); } if (this.getZLength(direction) > mainDirection) { this.mainFace = this.getZFace(direction); mainDirection = this.getZLength(direction); mainPosition = this.getZPosition(direction, startClone, startBlock); this.secondFace = this.getXFace(direction); secondDirection = this.getXLength(direction); secondPosition = this.getXPosition(direction, startClone, startBlock); this.thirdFace = this.getYFace(direction); thirdDirection = this.getYLength(direction); thirdPosition = this.getYPosition(direction, startClone, startBlock); } double d = mainPosition / mainDirection; double secondd = secondPosition - secondDirection * d; double thirdd = thirdPosition - thirdDirection * d; this.secondError = (int) Math.floor(secondd * gridSize); this.secondStep = (int) Math.round(secondDirection / mainDirection * gridSize); this.thirdError = (int) Math.floor(thirdd * gridSize); this.thirdStep = (int) Math.round(thirdDirection / mainDirection * gridSize); if (this.secondError + this.secondStep <= 0) { this.secondError = -this.secondStep + 1; } if (this.thirdError + this.thirdStep <= 0) { this.thirdError = -this.thirdStep + 1; } Block lastBlock = startBlock.getSide(this.mainFace.getOpposite()); if (this.secondError < 0) { this.secondError += gridSize; lastBlock = lastBlock.getSide(this.secondFace.getOpposite()); } if (this.thirdError < 0) { this.thirdError += gridSize; lastBlock = lastBlock.getSide(this.thirdFace.getOpposite()); } this.secondError -= gridSize; this.thirdError -= gridSize; this.blockQueue[0] = lastBlock; this.currentBlock = -1; this.scan(); boolean startBlockFound = false; for (int cnt = this.currentBlock; cnt >= 0; --cnt) { if (this.blockEquals(this.blockQueue[cnt], startBlock)) { this.currentBlock = cnt; startBlockFound = true; break; } } if (!startBlockFound) { throw new IllegalStateException("Start block missed in BlockIterator"); } this.maxDistanceInt = (int) Math.round(maxDistance / (Math.sqrt(mainDirection * mainDirection + secondDirection * secondDirection + thirdDirection * thirdDirection) / mainDirection)); } private boolean blockEquals(Block a, Block b) { return a.x == b.x && a.y == b.y && a.z == b.z; } private BlockFace getXFace(Vector3 direction) { return ((direction.x) > 0) ? BlockFace.EAST : BlockFace.WEST; } private BlockFace getYFace(Vector3 direction) { return ((direction.y) > 0) ? BlockFace.UP : BlockFace.DOWN; } private BlockFace getZFace(Vector3 direction) { return ((direction.z) > 0) ? BlockFace.SOUTH : BlockFace.NORTH; } private double getXLength(Vector3 direction) { return Math.abs(direction.x); } private double getYLength(Vector3 direction) { return Math.abs(direction.y); } private double getZLength(Vector3 direction) { return Math.abs(direction.z); } private double getPosition(double direction, double position, double blockPosition) { return direction > 0 ? (position - blockPosition) : (blockPosition + 1 - position); } private double getXPosition(Vector3 direction, Vector3 position, Block block) { return this.getPosition(direction.x, position.x, block.x); } private double getYPosition(Vector3 direction, Vector3 position, Block block) { return this.getPosition(direction.y, position.y, block.y); } private double getZPosition(Vector3 direction, Vector3 position, Block block) { return this.getPosition(direction.z, position.z, block.z); } @Override public Block next() { this.scan(); if (this.currentBlock <= -1) { throw new IndexOutOfBoundsException(); } else { this.currentBlockObject = this.blockQueue[this.currentBlock--]; } return this.currentBlockObject; } @Override public boolean hasNext() { this.scan(); return this.currentBlock != -1; } private void scan() { if (this.currentBlock >= 0) { return; } if (this.maxDistance != 0 && this.currentDistance > this.maxDistanceInt) { this.end = true; return; } if (this.end) { return; } ++this.currentDistance; this.secondError += this.secondStep; this.thirdError += this.thirdStep; if (this.secondError > 0 && this.thirdError > 0) { this.blockQueue[2] = this.blockQueue[0].getSide(this.mainFace); if ((this.secondStep * this.thirdError) < (this.thirdStep * this.secondError)) { this.blockQueue[1] = this.blockQueue[2].getSide(this.secondFace); this.blockQueue[0] = this.blockQueue[1].getSide(this.thirdFace); } else { this.blockQueue[1] = this.blockQueue[2].getSide(this.thirdFace); this.blockQueue[0] = this.blockQueue[1].getSide(this.secondFace); } this.thirdError -= gridSize; this.secondError -= gridSize; this.currentBlock = 2; } else if (this.secondError > 0) { this.blockQueue[1] = this.blockQueue[0].getSide(this.mainFace); this.blockQueue[0] = this.blockQueue[1].getSide(this.secondFace); this.secondError -= gridSize; this.currentBlock = 1; } else if (this.thirdError > 0) { this.blockQueue[1] = this.blockQueue[0].getSide(this.mainFace); this.blockQueue[0] = this.blockQueue[1].getSide(this.thirdFace); this.thirdError -= gridSize; this.currentBlock = 1; } else { this.blockQueue[0] = this.blockQueue[0].getSide(this.mainFace); this.currentBlock = 0; } } }