|
import com.google.common.collect.AbstractIterator; |
|
import com.mojang.datafixers.types.DynamicOps; |
|
import net.minecraft.util.BlockRotation; |
|
import net.minecraft.util.dynamic.DynamicSerializable; |
|
import net.minecraft.util.math.BlockBox; |
|
import net.minecraft.util.math.Direction; |
|
|
|
import java.util.Optional; |
|
import java.util.function.Predicate; |
|
import java.util.stream.IntStream; |
|
import java.util.stream.Stream; |
|
import java.util.stream.StreamSupport; |
|
|
|
public interface BlockPos extends DynamicSerializable { |
|
// methods to implement |
|
int getX(); |
|
|
|
int getY(); |
|
|
|
int getZ(); |
|
|
|
ImmutableBlockPos toImmutable(); // essential conversion |
|
|
|
/** |
|
* Returns a mutable copy of this block position. |
|
* |
|
* <p>If this block position is a mutable one, mutation to this block |
|
* position won't affect the returned position. |
|
*/ |
|
// another essential conversion |
|
default MutableBlockPos mutableCopy() { |
|
return new MutableBlockPos(this.getX(), this.getY(), this.getZ()); |
|
} |
|
|
|
/** |
|
* Iterates block positions around the {@code center}. The iteration order |
|
* is mainly based on the manhattan distance of the position from the |
|
* center. |
|
* |
|
* <p>For the same manhattan distance, the positions are iterated by y |
|
* offset, from negative to positive. For the same y offset, the positions |
|
* are iterated by x offset, from negative to positive. For the two |
|
* positions with the same x and y offsets and the same manhattan distance, |
|
* the one with a positive z offset is visited first before the one with a |
|
* negative z offset. |
|
* |
|
* @param center the center of iteration |
|
* @param xRange the maximum x difference from the center |
|
* @param yRange the maximum y difference from the center |
|
* @param zRange the maximum z difference from the center |
|
*/ |
|
static Iterable<BlockPos> iterateOutwards(BlockPos center, int xRange, int yRange, int zRange) { |
|
int i = xRange + yRange + zRange; |
|
int j = center.getX(); |
|
int k = center.getY(); |
|
int l = center.getZ(); |
|
return () -> { |
|
return new AbstractIterator<BlockPos>() { |
|
private final MutableBlockPos current = new MutableBlockPos(); |
|
private int manhattanDistance; |
|
private int limitX; |
|
private int limitY; |
|
private int dx; |
|
private int dy; |
|
private boolean flipped; |
|
|
|
protected BlockPos computeNext() { |
|
if (this.flipped) { |
|
this.flipped = false; |
|
this.current.setZ(i - (this.current.getZ() - i)); |
|
return this.current; |
|
} else { |
|
MutableBlockPos blockPos; |
|
for (blockPos = null; blockPos == null; ++this.dy) { |
|
if (this.dy > this.limitY) { |
|
++this.dx; |
|
if (this.dx > this.limitX) { |
|
++this.manhattanDistance; |
|
if (this.manhattanDistance > j) { |
|
return this.endOfData(); |
|
} |
|
|
|
this.limitX = Math.min(k, this.manhattanDistance); |
|
this.dx = -this.limitX; |
|
} |
|
|
|
this.limitY = Math.min(l, this.manhattanDistance - Math.abs(this.dx)); |
|
this.dy = -this.limitY; |
|
} |
|
|
|
int ix = this.dx; |
|
int jx = this.dy; |
|
int kx = this.manhattanDistance - Math.abs(ix) - Math.abs(jx); |
|
if (kx <= l) { |
|
this.flipped = kx != 0; |
|
blockPos = this.current.set(j + ix, k + jx, l + kx); |
|
} |
|
} |
|
|
|
return blockPos; |
|
} |
|
} |
|
}; |
|
}; |
|
} |
|
|
|
static Optional<BlockPos> findClosest(BlockPos pos, int horizontalRange, int verticalRange, Predicate<BlockPos> condition) { |
|
return streamOutwards(pos, horizontalRange, verticalRange, horizontalRange).filter(condition).findFirst(); |
|
} |
|
|
|
static Stream<BlockPos> streamOutwards(BlockPos center, int maxX, int maxY, int maxZ) { |
|
return StreamSupport.stream(iterateOutwards(center, maxX, maxY, maxZ).spliterator(), false); |
|
} |
|
|
|
static Iterable<BlockPos> iterate(BlockPos start, BlockPos end) { |
|
return iterate(Math.min(start.getX(), end.getX()), Math.min(start.getY(), end.getY()), Math.min(start.getZ(), end.getZ()), Math.max(start.getX(), end.getX()), Math.max(start.getY(), end.getY()), Math.max(start.getZ(), end.getZ())); |
|
} |
|
|
|
static Stream<BlockPos> stream(BlockPos start, BlockPos end) { |
|
return StreamSupport.stream(iterate(start, end).spliterator(), false); |
|
} |
|
|
|
static Stream<BlockPos> stream(BlockBox box) { |
|
return stream(Math.min(box.minX, box.maxX), Math.min(box.minY, box.maxY), Math.min(box.minZ, box.maxZ), Math.max(box.minX, box.maxX), Math.max(box.minY, box.maxY), Math.max(box.minZ, box.maxZ)); |
|
} |
|
|
|
static Stream<BlockPos> stream(int startX, int startY, int startZ, int endX, int endY, int endZ) { |
|
return StreamSupport.stream(iterate(startX, startY, startZ, endX, endY, endZ).spliterator(), false); |
|
} |
|
|
|
static Iterable<BlockPos> iterate(int startX, int startY, int startZ, int endX, int endY, int endZ) { |
|
int i = endX - startX + 1; |
|
int j = endY - startY + 1; |
|
int k = endZ - startZ + 1; |
|
int l = i * j * k; |
|
return () -> { |
|
return new AbstractIterator<BlockPos>() { |
|
private final MutableBlockPos pos = new MutableBlockPos(); |
|
private int index = 0; |
|
|
|
protected BlockPos computeNext() { |
|
if (this.index == l) { |
|
return this.endOfData(); |
|
} else { |
|
int ix = this.index % j; |
|
int jx = this.index / j; |
|
int kx = jx % k; |
|
int lx = jx / k; |
|
++this.index; |
|
return this.pos.set(startX + ix, startY + kx, startZ + lx); |
|
} |
|
} |
|
}; |
|
}; |
|
} |
|
|
|
@Override |
|
default <T> T serialize(DynamicOps<T> ops) { |
|
return ops.createIntList(IntStream.of(new int[]{this.getX(), this.getY(), this.getZ()})); |
|
} |
|
|
|
default long asLong() { |
|
return ImmutableBlockPos.asLong(this.getX(), this.getY(), this.getZ()); |
|
} |
|
|
|
default ImmutableBlockPos add(double x, double y, double z) { |
|
return x == 0.0D && y == 0.0D && z == 0.0D ? toImmutable() : new ImmutableBlockPos((double) this.getX() + x, (double) this.getY() + y, (double) this.getZ() + z); |
|
} |
|
|
|
default ImmutableBlockPos add(int x, int y, int z) { |
|
return x == 0 && y == 0 && z == 0 ? toImmutable() : new ImmutableBlockPos(this.getX() + x, this.getY() + y, this.getZ() + z); |
|
} |
|
|
|
default ImmutableBlockPos add(Vec3i pos) { |
|
return this.add(pos.getX(), pos.getY(), pos.getZ()); |
|
} |
|
|
|
default ImmutableBlockPos subtract(Vec3i pos) { |
|
return this.add(-pos.getX(), -pos.getY(), -pos.getZ()); |
|
} |
|
|
|
default ImmutableBlockPos up() { |
|
return offset(Direction.UP); |
|
} |
|
|
|
default ImmutableBlockPos up(int distance) { |
|
return this.offset(Direction.UP, distance); |
|
} |
|
|
|
default ImmutableBlockPos down() { |
|
return offset(Direction.DOWN); |
|
} |
|
|
|
default ImmutableBlockPos down(int distance) { |
|
return this.offset(Direction.DOWN, distance); |
|
} |
|
|
|
default ImmutableBlockPos north() { |
|
return offset(Direction.NORTH); |
|
} |
|
|
|
default ImmutableBlockPos north(int distance) { |
|
return this.offset(Direction.NORTH, distance); |
|
} |
|
|
|
default ImmutableBlockPos south() { |
|
return offset(Direction.SOUTH); |
|
} |
|
|
|
default ImmutableBlockPos south(int distance) { |
|
return this.offset(Direction.SOUTH, distance); |
|
} |
|
|
|
default ImmutableBlockPos west() { |
|
return offset(Direction.WEST); |
|
} |
|
|
|
default ImmutableBlockPos west(int distance) { |
|
return this.offset(Direction.WEST, distance); |
|
} |
|
|
|
default ImmutableBlockPos east() { |
|
return offset(Direction.EAST); |
|
} |
|
|
|
default ImmutableBlockPos east(int distance) { |
|
return this.offset(Direction.EAST, distance); |
|
} |
|
|
|
default ImmutableBlockPos offset(Direction direction) { |
|
return new ImmutableBlockPos(this.getX() + direction.getOffsetX(), this.getY() + direction.getOffsetY(), this.getZ() + direction.getOffsetZ()); |
|
} |
|
|
|
default ImmutableBlockPos offset(Direction direction, int i) { |
|
return i == 0 ? toImmutable() : new ImmutableBlockPos(this.getX() + direction.getOffsetX() * i, this.getY() + direction.getOffsetY() * i, this.getZ() + direction.getOffsetZ() * i); |
|
} |
|
|
|
default ImmutableBlockPos rotate(BlockRotation rotation) { |
|
switch (rotation) { |
|
case NONE: |
|
default: |
|
return toImmutable(); |
|
case CLOCKWISE_90: |
|
return new ImmutableBlockPos(-this.getZ(), this.getY(), this.getX()); |
|
case CLOCKWISE_180: |
|
return new ImmutableBlockPos(-this.getX(), this.getY(), -this.getZ()); |
|
case COUNTERCLOCKWISE_90: |
|
return new ImmutableBlockPos(this.getZ(), this.getY(), -this.getX()); |
|
} |
|
} |
|
|
|
default ImmutableBlockPos crossProduct(Vec3i pos) { |
|
return new ImmutableBlockPos(this.getY() * pos.getZ() - this.getZ() * pos.getY(), this.getZ() * pos.getX() - this.getX() * pos.getZ(), this.getX() * pos.getY() - this.getY() * pos.getX()); |
|
} |
|
} |