diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/mask/SurfaceAngleMaskParser.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/mask/SurfaceAngleMaskParser.java new file mode 100644 index 000000000..ab011f412 --- /dev/null +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/mask/SurfaceAngleMaskParser.java @@ -0,0 +1,45 @@ +package com.fastasyncworldedit.core.extension.factory.parser.mask; + +import com.fastasyncworldedit.core.extension.factory.parser.RichParser; +import com.fastasyncworldedit.core.function.mask.SurfaceAngleMask; +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.command.util.SuggestionHelper; +import com.sk89q.worldedit.extension.input.InputParseException; +import com.sk89q.worldedit.extension.input.ParserContext; +import com.sk89q.worldedit.function.mask.Mask; + +import javax.annotation.Nonnull; +import java.util.stream.Stream; + +public class SurfaceAngleMaskParser extends RichParser { + + /** + * Create a new surface angle mask parser with a defined prefix for the result, e.g. {@code #simplex}. + * + * @param worldEdit the worldedit instance. + */ + public SurfaceAngleMaskParser(WorldEdit worldEdit) { + super(worldEdit, "#surfaceangle"); + } + + @Override + protected Stream getSuggestions(String argumentInput, int index) { + if (index <= 2) { + return SuggestionHelper.suggestPositiveDoubles(argumentInput); + } + return Stream.empty(); + } + + public Mask parseFromInput(@Nonnull String[] arguments, ParserContext context) throws InputParseException { + if (arguments.length > 3) { + return null; + } + + double min = Double.parseDouble(arguments[0]); + double max = Double.parseDouble(arguments[1]); + + int size = (arguments.length > 2 ? Integer.parseInt(arguments[2]) : 1); + return new SurfaceAngleMask(context.getExtent(), min, max, size); + } + +} diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/mask/SurfaceAngleMask.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/mask/SurfaceAngleMask.java new file mode 100644 index 000000000..ce42a2d7e --- /dev/null +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/mask/SurfaceAngleMask.java @@ -0,0 +1,101 @@ +package com.fastasyncworldedit.core.function.mask; + +import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.function.mask.AbstractExtentMask; +import com.sk89q.worldedit.function.mask.Mask; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.math.Vector3; + +import java.util.ArrayList; + +public class SurfaceAngleMask extends AbstractExtentMask { + + private final double min; + private final double max; + private final int size; + + public SurfaceAngleMask(Extent extent, double min, double max, int size) { + super(extent); + this.min = min; + this.max = max; + this.size = size; + } + + @Override + public boolean test(BlockVector3 vector) { + if (!vector.getBlock(getExtent()).isAir() && nextToAir(vector)) { + double angle = 1 - getAverageAirDirection(vector.toVector3(), size).getY(); + return (angle >= (min / 90.0) && angle <= (max / 90.0)); + } + return false; + } + + @Override + public boolean test(Extent extent, BlockVector3 vector) { + if (!vector.getBlock(getExtent()).isAir() && nextToAir(vector)) { + double angle = 1 - getAverageAirDirection(vector.toVector3(), size).getY(); + return (angle >= (min / 90.0) && angle <= (max / 90.0)); + } + return false; + } + + private Vector3 getAverageAirDirection(Vector3 currentLocation, int size) { + ArrayList airDirections = new ArrayList<>(); + for (int i = -size; i <= size; i++) { + for (int j = -size; j <= size; j++) { + for (int k = -size; k <= size; k++) { + Vector3 block = Vector3.at(currentLocation.getX(), currentLocation.getY(), currentLocation.getZ()).add( + 0.5, + 0.5, + 0.5 + ).add(i, j, k); + if (block + .toBlockPoint() + .clampY(getExtent().getMinY(), getExtent().getMaxY()) + .getBlock(getExtent()) + .getMaterial() + .isAir()) { + airDirections.add(block.subtract(currentLocation.add(0.5, 0.5, 0.5))); + } + } + } + } + + double x = 0.0; + double y = 0.0; + double z = 0.0; + for (Vector3 vector3 : airDirections) { + x += vector3.getX(); + y += vector3.getY(); + z += vector3.getZ(); + } + + Vector3 averageAirDirection = Vector3.at(x / airDirections.size(), y / airDirections.size(), z / airDirections.size()); + return (Double.isNaN(averageAirDirection.getY()) ? Vector3.ZERO : averageAirDirection.normalize()); + } + + @Override + public Mask copy() { + return new SurfaceAngleMask(super.getExtent(), min, max, size); + } + + private boolean nextToAir(BlockVector3 blockVector3) { + if (getExtent().getBlock(blockVector3.add(1, 0, 0)).toBaseBlock().getMaterial().isAir()) { + return true; + } + if (getExtent().getBlock(blockVector3.add(-1, 0, 0)).toBaseBlock().getMaterial().isAir()) { + return true; + } + if (getExtent().getBlock(blockVector3.add(0, 1, 0)).toBaseBlock().getMaterial().isAir()) { + return true; + } + if (getExtent().getBlock(blockVector3.add(0, -1, 0)).toBaseBlock().getMaterial().isAir()) { + return true; + } + if (getExtent().getBlock(blockVector3.add(0, 0, 1)).toBaseBlock().getMaterial().isAir()) { + return true; + } + return getExtent().getBlock(blockVector3.add(0, 0, -1)).toBaseBlock().getMaterial().isAir(); + } + +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/MaskFactory.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/MaskFactory.java index f31954b2e..61f8c3015 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/MaskFactory.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/MaskFactory.java @@ -28,6 +28,7 @@ import com.fastasyncworldedit.core.extension.factory.parser.mask.LiquidMaskParse import com.fastasyncworldedit.core.extension.factory.parser.mask.ROCAngleMaskParser; import com.fastasyncworldedit.core.extension.factory.parser.mask.RichOffsetMaskParser; import com.fastasyncworldedit.core.extension.factory.parser.mask.SimplexMaskParser; +import com.fastasyncworldedit.core.extension.factory.parser.mask.SurfaceAngleMaskParser; import com.fastasyncworldedit.core.extension.factory.parser.mask.SurfaceMaskParser; import com.fastasyncworldedit.core.extension.factory.parser.mask.TrueMaskParser; import com.fastasyncworldedit.core.extension.factory.parser.mask.WallMaskParser; @@ -108,6 +109,7 @@ public final class MaskFactory extends AbstractFactory { register(new XAxisMaskParser(worldEdit)); register(new YAxisMaskParser(worldEdit)); register(new ZAxisMaskParser(worldEdit)); + register(new SurfaceAngleMaskParser(worldEdit)); //FAWE end }