349 lines
11 KiB
Java
349 lines
11 KiB
Java
package net.minecraft.world.level.levelgen.structure;
|
|
|
|
import com.google.common.base.MoreObjects;
|
|
import com.mojang.logging.LogUtils;
|
|
import com.mojang.serialization.Codec;
|
|
import java.util.Iterator;
|
|
import java.util.Objects;
|
|
import java.util.Optional;
|
|
import java.util.function.Consumer;
|
|
import java.util.stream.IntStream;
|
|
import java.util.stream.Stream;
|
|
import net.minecraft.Util;
|
|
import net.minecraft.core.BlockPos;
|
|
import net.minecraft.core.Direction;
|
|
import net.minecraft.core.SectionPos;
|
|
import net.minecraft.core.Vec3i;
|
|
import net.minecraft.world.level.ChunkPos;
|
|
import org.slf4j.Logger;
|
|
|
|
/**
|
|
* A simple three-dimensional mutable integer bounding box.
|
|
* Note that this box is both mutable, and has an implementation of {@code hashCode()} and {@code equals()}.
|
|
* This can be used as {@code HashMap} keys for example, if the user can ensure the instances themselves are not modified.
|
|
*/
|
|
public class BoundingBox {
|
|
private static final Logger LOGGER = LogUtils.getLogger();
|
|
public static final Codec<BoundingBox> CODEC = Codec.INT_STREAM
|
|
.<BoundingBox>comapFlatMap(
|
|
intStream -> Util.fixedSize(intStream, 6).map(is -> new BoundingBox(is[0], is[1], is[2], is[3], is[4], is[5])),
|
|
boundingBox -> IntStream.of(new int[]{boundingBox.minX, boundingBox.minY, boundingBox.minZ, boundingBox.maxX, boundingBox.maxY, boundingBox.maxZ})
|
|
)
|
|
.stable();
|
|
private int minX;
|
|
private int minY;
|
|
private int minZ;
|
|
private int maxX;
|
|
private int maxY;
|
|
private int maxZ;
|
|
|
|
public BoundingBox(BlockPos pos) {
|
|
this(pos.getX(), pos.getY(), pos.getZ(), pos.getX(), pos.getY(), pos.getZ());
|
|
}
|
|
|
|
public BoundingBox(int minX, int minY, int minZ, int maxX, int maxY, int maxZ) {
|
|
this.minX = minX;
|
|
this.minY = minY;
|
|
this.minZ = minZ;
|
|
this.maxX = maxX;
|
|
this.maxY = maxY;
|
|
this.maxZ = maxZ;
|
|
if (maxX < minX || maxY < minY || maxZ < minZ) {
|
|
Util.logAndPauseIfInIde("Invalid bounding box data, inverted bounds for: " + this);
|
|
this.minX = Math.min(minX, maxX);
|
|
this.minY = Math.min(minY, maxY);
|
|
this.minZ = Math.min(minZ, maxZ);
|
|
this.maxX = Math.max(minX, maxX);
|
|
this.maxY = Math.max(minY, maxY);
|
|
this.maxZ = Math.max(minZ, maxZ);
|
|
}
|
|
}
|
|
|
|
public static BoundingBox fromCorners(Vec3i first, Vec3i second) {
|
|
return new BoundingBox(
|
|
Math.min(first.getX(), second.getX()),
|
|
Math.min(first.getY(), second.getY()),
|
|
Math.min(first.getZ(), second.getZ()),
|
|
Math.max(first.getX(), second.getX()),
|
|
Math.max(first.getY(), second.getY()),
|
|
Math.max(first.getZ(), second.getZ())
|
|
);
|
|
}
|
|
|
|
public static BoundingBox infinite() {
|
|
return new BoundingBox(Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE);
|
|
}
|
|
|
|
/**
|
|
* Create a bounding box with the specified dimensions and rotate it. Used to project a possible new component Bounding Box - to check if it would cut anything already spawned.
|
|
*/
|
|
public static BoundingBox orientBox(
|
|
int structureMinX, int structureMinY, int structureMinZ, int xMin, int yMin, int zMin, int xMax, int yMax, int zMax, Direction facing
|
|
) {
|
|
switch (facing) {
|
|
case SOUTH:
|
|
default:
|
|
return new BoundingBox(
|
|
structureMinX + xMin,
|
|
structureMinY + yMin,
|
|
structureMinZ + zMin,
|
|
structureMinX + xMax - 1 + xMin,
|
|
structureMinY + yMax - 1 + yMin,
|
|
structureMinZ + zMax - 1 + zMin
|
|
);
|
|
case NORTH:
|
|
return new BoundingBox(
|
|
structureMinX + xMin,
|
|
structureMinY + yMin,
|
|
structureMinZ - zMax + 1 + zMin,
|
|
structureMinX + xMax - 1 + xMin,
|
|
structureMinY + yMax - 1 + yMin,
|
|
structureMinZ + zMin
|
|
);
|
|
case WEST:
|
|
return new BoundingBox(
|
|
structureMinX - zMax + 1 + zMin,
|
|
structureMinY + yMin,
|
|
structureMinZ + xMin,
|
|
structureMinX + zMin,
|
|
structureMinY + yMax - 1 + yMin,
|
|
structureMinZ + xMax - 1 + xMin
|
|
);
|
|
case EAST:
|
|
return new BoundingBox(
|
|
structureMinX + zMin,
|
|
structureMinY + yMin,
|
|
structureMinZ + xMin,
|
|
structureMinX + zMax - 1 + zMin,
|
|
structureMinY + yMax - 1 + yMin,
|
|
structureMinZ + xMax - 1 + xMin
|
|
);
|
|
}
|
|
}
|
|
|
|
public Stream<ChunkPos> intersectingChunks() {
|
|
int i = SectionPos.blockToSectionCoord(this.minX());
|
|
int j = SectionPos.blockToSectionCoord(this.minZ());
|
|
int k = SectionPos.blockToSectionCoord(this.maxX());
|
|
int l = SectionPos.blockToSectionCoord(this.maxZ());
|
|
return ChunkPos.rangeClosed(new ChunkPos(i, j), new ChunkPos(k, l));
|
|
}
|
|
|
|
/**
|
|
* @return {@code true} if {@code box} intersects this box.
|
|
*/
|
|
public boolean intersects(BoundingBox box) {
|
|
return this.maxX >= box.minX && this.minX <= box.maxX && this.maxZ >= box.minZ && this.minZ <= box.maxZ && this.maxY >= box.minY && this.minY <= box.maxY;
|
|
}
|
|
|
|
/**
|
|
* @return {@code true} if this bounding box intersects the horizontal x/z region described by the min and max parameters.
|
|
*/
|
|
public boolean intersects(int minX, int minZ, int maxX, int maxZ) {
|
|
return this.maxX >= minX && this.minX <= maxX && this.maxZ >= minZ && this.minZ <= maxZ;
|
|
}
|
|
|
|
public static Optional<BoundingBox> encapsulatingPositions(Iterable<BlockPos> positions) {
|
|
Iterator<BlockPos> iterator = positions.iterator();
|
|
if (!iterator.hasNext()) {
|
|
return Optional.empty();
|
|
} else {
|
|
BoundingBox boundingBox = new BoundingBox((BlockPos)iterator.next());
|
|
iterator.forEachRemaining(boundingBox::encapsulate);
|
|
return Optional.of(boundingBox);
|
|
}
|
|
}
|
|
|
|
public static Optional<BoundingBox> encapsulatingBoxes(Iterable<BoundingBox> boxes) {
|
|
Iterator<BoundingBox> iterator = boxes.iterator();
|
|
if (!iterator.hasNext()) {
|
|
return Optional.empty();
|
|
} else {
|
|
BoundingBox boundingBox = (BoundingBox)iterator.next();
|
|
BoundingBox boundingBox2 = new BoundingBox(boundingBox.minX, boundingBox.minY, boundingBox.minZ, boundingBox.maxX, boundingBox.maxY, boundingBox.maxZ);
|
|
iterator.forEachRemaining(boundingBox2::encapsulate);
|
|
return Optional.of(boundingBox2);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Expands this box to be at least large enough to contain {@code box}.
|
|
*/
|
|
@Deprecated
|
|
public BoundingBox encapsulate(BoundingBox box) {
|
|
this.minX = Math.min(this.minX, box.minX);
|
|
this.minY = Math.min(this.minY, box.minY);
|
|
this.minZ = Math.min(this.minZ, box.minZ);
|
|
this.maxX = Math.max(this.maxX, box.maxX);
|
|
this.maxY = Math.max(this.maxY, box.maxY);
|
|
this.maxZ = Math.max(this.maxZ, box.maxZ);
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Expands this box to be at least large enough to contain {@code pos}.
|
|
*/
|
|
@Deprecated
|
|
public BoundingBox encapsulate(BlockPos pos) {
|
|
this.minX = Math.min(this.minX, pos.getX());
|
|
this.minY = Math.min(this.minY, pos.getY());
|
|
this.minZ = Math.min(this.minZ, pos.getZ());
|
|
this.maxX = Math.max(this.maxX, pos.getX());
|
|
this.maxY = Math.max(this.maxY, pos.getY());
|
|
this.maxZ = Math.max(this.maxZ, pos.getZ());
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Translates this box by the given coordinates, modifying the current box.
|
|
*/
|
|
@Deprecated
|
|
public BoundingBox move(int x, int y, int z) {
|
|
this.minX += x;
|
|
this.minY += y;
|
|
this.minZ += z;
|
|
this.maxX += x;
|
|
this.maxY += y;
|
|
this.maxZ += z;
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Translates this box by the given vector, modifying the current box.
|
|
*/
|
|
@Deprecated
|
|
public BoundingBox move(Vec3i vector) {
|
|
return this.move(vector.getX(), vector.getY(), vector.getZ());
|
|
}
|
|
|
|
/**
|
|
* @return A new bounding box equal to this box, translated by the given coordinates.
|
|
*/
|
|
public BoundingBox moved(int x, int y, int z) {
|
|
return new BoundingBox(this.minX + x, this.minY + y, this.minZ + z, this.maxX + x, this.maxY + y, this.maxZ + z);
|
|
}
|
|
|
|
/**
|
|
* Expands this box by a fixed {@code value} in all directions.
|
|
*/
|
|
public BoundingBox inflatedBy(int value) {
|
|
return this.inflatedBy(value, value, value);
|
|
}
|
|
|
|
public BoundingBox inflatedBy(int x, int y, int z) {
|
|
return new BoundingBox(this.minX() - x, this.minY() - y, this.minZ() - z, this.maxX() + x, this.maxY() + y, this.maxZ() + z);
|
|
}
|
|
|
|
/**
|
|
* @return {@code true} if the bounding box contains the {@code vector}.
|
|
*/
|
|
public boolean isInside(Vec3i vector) {
|
|
return this.isInside(vector.getX(), vector.getY(), vector.getZ());
|
|
}
|
|
|
|
public boolean isInside(int x, int y, int z) {
|
|
return x >= this.minX && x <= this.maxX && z >= this.minZ && z <= this.maxZ && y >= this.minY && y <= this.maxY;
|
|
}
|
|
|
|
/**
|
|
* Returns a vector describing the dimensions of this bounding box.
|
|
* Note that unlike {@code getXSpan()}, {@code getYSpan()}, and {@code getZSpan()}, the length is interpreted here as the difference in coordinates. So a box over a 1x1x1 area, which still contains a single point, will report length zero.
|
|
*/
|
|
public Vec3i getLength() {
|
|
return new Vec3i(this.maxX - this.minX, this.maxY - this.minY, this.maxZ - this.minZ);
|
|
}
|
|
|
|
/**
|
|
* @return The length of this bounding box along the x-axis.
|
|
*/
|
|
public int getXSpan() {
|
|
return this.maxX - this.minX + 1;
|
|
}
|
|
|
|
/**
|
|
* @return The length of this bounding box along the y-axis.
|
|
*/
|
|
public int getYSpan() {
|
|
return this.maxY - this.minY + 1;
|
|
}
|
|
|
|
/**
|
|
* @return The length of this bounding box along the z-axis.
|
|
*/
|
|
public int getZSpan() {
|
|
return this.maxZ - this.minZ + 1;
|
|
}
|
|
|
|
/**
|
|
* @return The center of this bounding box. Note in even-sized dimensions the center position will be offset in the positive direction.
|
|
*/
|
|
public BlockPos getCenter() {
|
|
return new BlockPos(this.minX + (this.maxX - this.minX + 1) / 2, this.minY + (this.maxY - this.minY + 1) / 2, this.minZ + (this.maxZ - this.minZ + 1) / 2);
|
|
}
|
|
|
|
public void forAllCorners(Consumer<BlockPos> pos) {
|
|
BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos();
|
|
pos.accept(mutableBlockPos.set(this.maxX, this.maxY, this.maxZ));
|
|
pos.accept(mutableBlockPos.set(this.minX, this.maxY, this.maxZ));
|
|
pos.accept(mutableBlockPos.set(this.maxX, this.minY, this.maxZ));
|
|
pos.accept(mutableBlockPos.set(this.minX, this.minY, this.maxZ));
|
|
pos.accept(mutableBlockPos.set(this.maxX, this.maxY, this.minZ));
|
|
pos.accept(mutableBlockPos.set(this.minX, this.maxY, this.minZ));
|
|
pos.accept(mutableBlockPos.set(this.maxX, this.minY, this.minZ));
|
|
pos.accept(mutableBlockPos.set(this.minX, this.minY, this.minZ));
|
|
}
|
|
|
|
public String toString() {
|
|
return MoreObjects.toStringHelper(this)
|
|
.add("minX", this.minX)
|
|
.add("minY", this.minY)
|
|
.add("minZ", this.minZ)
|
|
.add("maxX", this.maxX)
|
|
.add("maxY", this.maxY)
|
|
.add("maxZ", this.maxZ)
|
|
.toString();
|
|
}
|
|
|
|
public boolean equals(Object object) {
|
|
if (this == object) {
|
|
return true;
|
|
} else {
|
|
return !(object instanceof BoundingBox boundingBox)
|
|
? false
|
|
: this.minX == boundingBox.minX
|
|
&& this.minY == boundingBox.minY
|
|
&& this.minZ == boundingBox.minZ
|
|
&& this.maxX == boundingBox.maxX
|
|
&& this.maxY == boundingBox.maxY
|
|
&& this.maxZ == boundingBox.maxZ;
|
|
}
|
|
}
|
|
|
|
public int hashCode() {
|
|
return Objects.hash(new Object[]{this.minX, this.minY, this.minZ, this.maxX, this.maxY, this.maxZ});
|
|
}
|
|
|
|
public int minX() {
|
|
return this.minX;
|
|
}
|
|
|
|
public int minY() {
|
|
return this.minY;
|
|
}
|
|
|
|
public int minZ() {
|
|
return this.minZ;
|
|
}
|
|
|
|
public int maxX() {
|
|
return this.maxX;
|
|
}
|
|
|
|
public int maxY() {
|
|
return this.maxY;
|
|
}
|
|
|
|
public int maxZ() {
|
|
return this.maxZ;
|
|
}
|
|
}
|