184 lines
		
	
	
	
		
			5.5 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
			
		
		
	
	
			184 lines
		
	
	
	
		
			5.5 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
| package net.minecraft;
 | |
| 
 | |
| import com.google.common.annotations.VisibleForTesting;
 | |
| import com.mojang.datafixers.util.Pair;
 | |
| import it.unimi.dsi.fastutil.ints.IntArrayList;
 | |
| import it.unimi.dsi.fastutil.ints.IntStack;
 | |
| import java.util.Optional;
 | |
| import java.util.function.Predicate;
 | |
| import net.minecraft.core.BlockPos;
 | |
| import net.minecraft.core.Direction;
 | |
| import net.minecraft.world.level.BlockGetter;
 | |
| import net.minecraft.world.level.block.Block;
 | |
| import net.minecraft.world.level.block.state.BlockState;
 | |
| 
 | |
| public class BlockUtil {
 | |
| 	/**
 | |
| 	 * Finds the rectangle with the largest area containing centerPos within the blocks specified by the predicate
 | |
| 	 */
 | |
| 	public static BlockUtil.FoundRectangle getLargestRectangleAround(
 | |
| 		BlockPos centerPos, Direction.Axis axis1, int max1, Direction.Axis axis2, int max2, Predicate<BlockPos> posPredicate
 | |
| 	) {
 | |
| 		BlockPos.MutableBlockPos mutableBlockPos = centerPos.mutable();
 | |
| 		Direction direction = Direction.get(Direction.AxisDirection.NEGATIVE, axis1);
 | |
| 		Direction direction2 = direction.getOpposite();
 | |
| 		Direction direction3 = Direction.get(Direction.AxisDirection.NEGATIVE, axis2);
 | |
| 		Direction direction4 = direction3.getOpposite();
 | |
| 		int i = getLimit(posPredicate, mutableBlockPos.set(centerPos), direction, max1);
 | |
| 		int j = getLimit(posPredicate, mutableBlockPos.set(centerPos), direction2, max1);
 | |
| 		int k = i;
 | |
| 		BlockUtil.IntBounds[] intBoundss = new BlockUtil.IntBounds[i + 1 + j];
 | |
| 		intBoundss[i] = new BlockUtil.IntBounds(
 | |
| 			getLimit(posPredicate, mutableBlockPos.set(centerPos), direction3, max2), getLimit(posPredicate, mutableBlockPos.set(centerPos), direction4, max2)
 | |
| 		);
 | |
| 		int l = intBoundss[i].min;
 | |
| 
 | |
| 		for (int m = 1; m <= i; m++) {
 | |
| 			BlockUtil.IntBounds intBounds = intBoundss[k - (m - 1)];
 | |
| 			intBoundss[k - m] = new BlockUtil.IntBounds(
 | |
| 				getLimit(posPredicate, mutableBlockPos.set(centerPos).move(direction, m), direction3, intBounds.min),
 | |
| 				getLimit(posPredicate, mutableBlockPos.set(centerPos).move(direction, m), direction4, intBounds.max)
 | |
| 			);
 | |
| 		}
 | |
| 
 | |
| 		for (int m = 1; m <= j; m++) {
 | |
| 			BlockUtil.IntBounds intBounds = intBoundss[k + m - 1];
 | |
| 			intBoundss[k + m] = new BlockUtil.IntBounds(
 | |
| 				getLimit(posPredicate, mutableBlockPos.set(centerPos).move(direction2, m), direction3, intBounds.min),
 | |
| 				getLimit(posPredicate, mutableBlockPos.set(centerPos).move(direction2, m), direction4, intBounds.max)
 | |
| 			);
 | |
| 		}
 | |
| 
 | |
| 		int m = 0;
 | |
| 		int n = 0;
 | |
| 		int o = 0;
 | |
| 		int p = 0;
 | |
| 		int[] is = new int[intBoundss.length];
 | |
| 
 | |
| 		for (int q = l; q >= 0; q--) {
 | |
| 			for (int r = 0; r < intBoundss.length; r++) {
 | |
| 				BlockUtil.IntBounds intBounds2 = intBoundss[r];
 | |
| 				int s = l - intBounds2.min;
 | |
| 				int t = l + intBounds2.max;
 | |
| 				is[r] = q >= s && q <= t ? t + 1 - q : 0;
 | |
| 			}
 | |
| 
 | |
| 			Pair<BlockUtil.IntBounds, Integer> pair = getMaxRectangleLocation(is);
 | |
| 			BlockUtil.IntBounds intBounds2 = pair.getFirst();
 | |
| 			int s = 1 + intBounds2.max - intBounds2.min;
 | |
| 			int t = pair.getSecond();
 | |
| 			if (s * t > o * p) {
 | |
| 				m = intBounds2.min;
 | |
| 				n = q;
 | |
| 				o = s;
 | |
| 				p = t;
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		return new BlockUtil.FoundRectangle(centerPos.relative(axis1, m - k).relative(axis2, n - l), o, p);
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Finds the distance we can travel in the given direction while the predicate returns true
 | |
| 	 */
 | |
| 	private static int getLimit(Predicate<BlockPos> posPredicate, BlockPos.MutableBlockPos centerPos, Direction direction, int max) {
 | |
| 		int i = 0;
 | |
| 
 | |
| 		while (i < max && posPredicate.test(centerPos.move(direction))) {
 | |
| 			i++;
 | |
| 		}
 | |
| 
 | |
| 		return i;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Finds the largest rectangle within the array of heights
 | |
| 	 */
 | |
| 	@VisibleForTesting
 | |
| 	static Pair<BlockUtil.IntBounds, Integer> getMaxRectangleLocation(int[] heights) {
 | |
| 		int i = 0;
 | |
| 		int j = 0;
 | |
| 		int k = 0;
 | |
| 		IntStack intStack = new IntArrayList();
 | |
| 		intStack.push(0);
 | |
| 
 | |
| 		for (int l = 1; l <= heights.length; l++) {
 | |
| 			int m = l == heights.length ? 0 : heights[l];
 | |
| 
 | |
| 			while (!intStack.isEmpty()) {
 | |
| 				int n = heights[intStack.topInt()];
 | |
| 				if (m >= n) {
 | |
| 					intStack.push(l);
 | |
| 					break;
 | |
| 				}
 | |
| 
 | |
| 				intStack.popInt();
 | |
| 				int o = intStack.isEmpty() ? 0 : intStack.topInt() + 1;
 | |
| 				if (n * (l - o) > k * (j - i)) {
 | |
| 					j = l;
 | |
| 					i = o;
 | |
| 					k = n;
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			if (intStack.isEmpty()) {
 | |
| 				intStack.push(l);
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		return new Pair<>(new BlockUtil.IntBounds(i, j - 1), k);
 | |
| 	}
 | |
| 
 | |
| 	public static Optional<BlockPos> getTopConnectedBlock(BlockGetter getter, BlockPos pos, Block baseBlock, Direction direction, Block endBlock) {
 | |
| 		BlockPos.MutableBlockPos mutableBlockPos = pos.mutable();
 | |
| 
 | |
| 		BlockState blockState;
 | |
| 		do {
 | |
| 			mutableBlockPos.move(direction);
 | |
| 			blockState = getter.getBlockState(mutableBlockPos);
 | |
| 		} while (blockState.is(baseBlock));
 | |
| 
 | |
| 		return blockState.is(endBlock) ? Optional.of(mutableBlockPos) : Optional.empty();
 | |
| 	}
 | |
| 
 | |
| 	public static class FoundRectangle {
 | |
| 		/**
 | |
| 		 * Starting position of the rectangle represented by this result
 | |
| 		 */
 | |
| 		public final BlockPos minCorner;
 | |
| 		/**
 | |
| 		 * Distance between minimum and maximum values on the first axis argument
 | |
| 		 */
 | |
| 		public final int axis1Size;
 | |
| 		/**
 | |
| 		 * Distance between minimum and maximum values on the second axis argument
 | |
| 		 */
 | |
| 		public final int axis2Size;
 | |
| 
 | |
| 		public FoundRectangle(BlockPos minCorner, int axis1Size, int axis2Size) {
 | |
| 			this.minCorner = minCorner;
 | |
| 			this.axis1Size = axis1Size;
 | |
| 			this.axis2Size = axis2Size;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	public static class IntBounds {
 | |
| 		/**
 | |
| 		 * The minimum bound
 | |
| 		 */
 | |
| 		public final int min;
 | |
| 		/**
 | |
| 		 * The maximum bound
 | |
| 		 */
 | |
| 		public final int max;
 | |
| 
 | |
| 		public IntBounds(int min, int max) {
 | |
| 			this.min = min;
 | |
| 			this.max = max;
 | |
| 		}
 | |
| 
 | |
| 		public String toString() {
 | |
| 			return "IntBounds{min=" + this.min + ", max=" + this.max + "}";
 | |
| 		}
 | |
| 	}
 | |
| }
 |