Added an OffsetsMask, with a parser for an Exposed/Surface mask

This commit is contained in:
Matthew Miller 2020-09-18 21:12:41 +10:00 committed by Octavia Togami
parent d47f59e051
commit 46bcbe46bc
6 changed files with 456 additions and 1 deletions

View File

@ -26,6 +26,7 @@
import com.sk89q.worldedit.extension.factory.parser.mask.BlockStateMaskParser;
import com.sk89q.worldedit.extension.factory.parser.mask.BlocksMaskParser;
import com.sk89q.worldedit.extension.factory.parser.mask.ExistingMaskParser;
import com.sk89q.worldedit.extension.factory.parser.mask.ExposedMaskParser;
import com.sk89q.worldedit.extension.factory.parser.mask.ExpressionMaskParser;
import com.sk89q.worldedit.extension.factory.parser.mask.LazyRegionMaskParser;
import com.sk89q.worldedit.extension.factory.parser.mask.NegateMaskParser;
@ -66,6 +67,7 @@ public MaskFactory(WorldEdit worldEdit) {
register(new ExistingMaskParser(worldEdit));
register(new AirMaskParser(worldEdit));
register(new ExposedMaskParser(worldEdit));
register(new SolidMaskParser(worldEdit));
register(new LazyRegionMaskParser(worldEdit));
register(new RegionMaskParser(worldEdit));

View File

@ -0,0 +1,51 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldEdit team and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.sk89q.worldedit.extension.factory.parser.mask;
import com.google.common.collect.ImmutableList;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.extension.input.InputParseException;
import com.sk89q.worldedit.extension.input.ParserContext;
import com.sk89q.worldedit.function.mask.ExistingBlockMask;
import com.sk89q.worldedit.function.mask.Mask;
import com.sk89q.worldedit.function.mask.Masks;
import com.sk89q.worldedit.function.mask.OffsetsMask;
import com.sk89q.worldedit.internal.registry.SimpleInputParser;
import java.util.List;
public class ExposedMaskParser extends SimpleInputParser<Mask> {
private final List<String> aliases = ImmutableList.of("#exposed", "#surface");
public ExposedMaskParser(WorldEdit worldEdit) {
super(worldEdit);
}
@Override
public List<String> getMatchedAliases() {
return this.aliases;
}
@Override
public Mask parseFromSimpleInput(String input, ParserContext context) throws InputParseException {
return new OffsetsMask(Masks.negate(new ExistingBlockMask(context.requireExtent())), true);
}
}

View File

@ -28,7 +28,10 @@
/**
* Checks whether another mask tests true for a position that is offset
* a given vector.
*
* @deprecated Use {@link OffsetsMask#single}
*/
@Deprecated
public class OffsetMask extends AbstractMask {
private Mask mask;
@ -95,7 +98,7 @@ public boolean test(BlockVector3 vector) {
public Mask2D toMask2D() {
Mask2D childMask = getMask().toMask2D();
if (childMask != null) {
return new OffsetMask2D(childMask, getOffset().toBlockVector2());
return OffsetsMask2D.single(childMask, getOffset().toBlockVector2());
} else {
return null;
}

View File

@ -26,7 +26,10 @@
/**
* Checks whether another mask tests true for a position that is offset
* a given vector.
*
* @deprecated Use {@link OffsetsMask2D#single}
*/
@Deprecated
public class OffsetMask2D extends AbstractMask2D {
private Mask2D mask;

View File

@ -0,0 +1,213 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldEdit team and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.sk89q.worldedit.function.mask;
import com.google.common.collect.ImmutableList;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.util.Direction;
import java.util.List;
import java.util.stream.Collectors;
import javax.annotation.Nullable;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Checks whether any face of the given offsets to a block match a given mask.
*/
public class OffsetsMask extends AbstractMask {
private static final ImmutableList<BlockVector3> OFFSET_LIST = ImmutableList.copyOf(
Direction.valuesOf(Direction.Flag.CARDINAL | Direction.Flag.UPRIGHT)
.stream()
.map(Direction::toBlockVector)
.collect(Collectors.toList())
);
private final Mask mask;
private final boolean excludeSelf;
private final int minMatches;
private final int maxMatches;
private final ImmutableList<BlockVector3> offsets;
/**
* Create an OffsetsMask for a single offset.
*
* @param mask The mask to use
* @param offset The offset
* @return The offsets mask
*/
public static OffsetsMask single(Mask mask, BlockVector3 offset) {
return new OffsetsMask(mask, false, 1, 1, ImmutableList.of(offset));
}
/**
* Create a new instance using all adjacent faces excluding diagonals.
*
* <p>
* Note: This passes as long as there is at least one match, and does not
* exclude cases where the block being checked matches the mask.
* </p>
*
* @param mask the mask to test against
*/
public OffsetsMask(Mask mask) {
this(mask, false);
}
/**
* Create a new instance using all adjacent faces excluding diagonals.
*
* <p>
* Note: This passes as long as there is at least one match.
* </p>
*
* @param mask the mask to test against
* @param excludeSelf excludes blocks where the mask matches itself
*/
public OffsetsMask(Mask mask, boolean excludeSelf) {
this(mask, excludeSelf, 1, Integer.MAX_VALUE);
}
/**
* Create a new instance using all adjacent faces excluding diagonals.
*
* @param mask the mask to test against
* @param excludeSelf excludes blocks where the mask matches itself
* @param minMatches the minimum number of matches (inclusive)
* @param maxMatches the maximum number of matches (inclusive)
*/
public OffsetsMask(Mask mask, boolean excludeSelf, int minMatches, int maxMatches) {
this(mask, excludeSelf, minMatches, maxMatches, OFFSET_LIST);
}
/**
* Create a new instance.
*
* <p>
* Note: the minimum number of matches must be 0 or greater, and
* the maximum number of matches cannot be below the minimum.
* </p>
*
* @param mask the mask to test against
* @param excludeSelf excludes blocks where the mask matches itself
* @param minMatches the minimum number of matches (inclusive)
* @param maxMatches the maximum number of matches (inclusive)
* @param offsets the block offsets to test with
*/
public OffsetsMask(Mask mask, boolean excludeSelf, int minMatches, int maxMatches, List<BlockVector3> offsets) {
checkNotNull(mask);
checkNotNull(offsets);
// Validate match args. No need to test maxMatches as it must be >=0 based on the conditions here.
checkArgument(minMatches <= maxMatches, "minMatches must be less than or equal to maxMatches");
checkArgument(minMatches >= 0, "minMatches must be greater than or equal to 0");
checkArgument(minMatches <= offsets.size(), "minMatches must be less than or equal to the number of offsets");
checkArgument(offsets.size() > 0, "offsets must have at least one element");
this.mask = mask;
this.excludeSelf = excludeSelf;
this.minMatches = minMatches;
this.maxMatches = maxMatches;
this.offsets = ImmutableList.copyOf(offsets);
}
/**
* Get the mask.
*
* @return the mask
*/
public Mask getMask() {
return mask;
}
/**
* Get the offsets.
*
* @return the offsets
*/
public ImmutableList<BlockVector3> getOffsets() {
return this.offsets;
}
/**
* Get the flag determining if matching the current block should fail the mask.
*
* @return if it should exclude self-matches
*/
public boolean getExcludeSelf() {
return this.excludeSelf;
}
/**
* Gets the minimum number of matches to pass.
*
* @return the minimum number of matches
*/
public int getMinMatches() {
return this.minMatches;
}
/**
* Gets the maximum number of matches to pass.
*
* @return the maximum number of matches
*/
public int getMaxMatches() {
return this.maxMatches;
}
@Override
public boolean test(BlockVector3 vector) {
if (excludeSelf && mask.test(vector)) {
return false;
}
int matches = 0;
for (BlockVector3 offset : offsets) {
if (mask.test(vector.add(offset))) {
matches++;
if (matches > maxMatches) {
return false;
}
}
}
return minMatches <= matches && matches <= maxMatches;
}
@Nullable
@Override
public Mask2D toMask2D() {
Mask2D childMask = getMask().toMask2D();
if (childMask != null) {
return new OffsetsMask2D(
childMask,
excludeSelf,
minMatches,
maxMatches,
getOffsets().stream().map(BlockVector3::toBlockVector2).collect(Collectors.toList())
);
} else {
return null;
}
}
}

View File

@ -0,0 +1,183 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldEdit team and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.sk89q.worldedit.function.mask;
import com.google.common.collect.ImmutableList;
import com.sk89q.worldedit.math.BlockVector2;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.util.Direction;
import java.util.List;
import java.util.stream.Collectors;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Checks whether any face of the given offsets to a block match a given mask.
*/
public class OffsetsMask2D extends AbstractMask2D {
private static final ImmutableList<BlockVector2> OFFSET_LIST = ImmutableList.copyOf(
Direction.valuesOf(Direction.Flag.CARDINAL)
.stream()
.map(Direction::toBlockVector)
.map(BlockVector3::toBlockVector2)
.collect(Collectors.toList())
);
private final Mask2D mask;
private final boolean excludeSelf;
private final int minMatches;
private final int maxMatches;
private final ImmutableList<BlockVector2> offsets;
/**
* Create an OffsetsMask2D for a single offset.
*
* @param mask The mask to use
* @param offset The offset
* @return The offsets mask
*/
public static OffsetsMask2D single(Mask2D mask, BlockVector2 offset) {
return new OffsetsMask2D(mask, false, 1, 1, ImmutableList.of(offset));
}
/**
* Create a new instance.
*
* @param mask the mask to test against
*/
public OffsetsMask2D(Mask2D mask) {
this(mask, false);
}
/**
* Create a new instance.
*
* @param mask the mask to test against
* @param excludeSelf excludes blocks where the mask matches itself
*/
public OffsetsMask2D(Mask2D mask, boolean excludeSelf) {
this(mask, excludeSelf, 1, Integer.MAX_VALUE);
}
/**
* Create a new instance.
*
* @param mask the mask to test against
* @param excludeSelf excludes blocks where the mask matches itself
* @param minMatches the minimum number of matches (inclusive)
* @param maxMatches the maximum number of matches (inclusive)
*/
public OffsetsMask2D(Mask2D mask, boolean excludeSelf, int minMatches, int maxMatches) {
this(mask, excludeSelf, minMatches, maxMatches, OFFSET_LIST);
}
/**
* Create a new instance.
*
* @param mask the mask to test against
* @param excludeSelf excludes blocks where the mask matches itself
* @param minMatches the minimum number of matches (inclusive)
* @param maxMatches the maximum number of matches (inclusive)
* @param offsets the block offsets to test with
*/
public OffsetsMask2D(Mask2D mask, boolean excludeSelf, int minMatches, int maxMatches, List<BlockVector2> offsets) {
checkNotNull(mask);
checkNotNull(offsets);
// Validate match args. No need to test maxMatches as it must be >=0 based on the conditions here.
checkArgument(minMatches <= maxMatches, "minMatches must be less than or equal to maxMatches");
checkArgument(minMatches >= 0, "minMatches must be greater than or equal to 0");
checkArgument(minMatches <= offsets.size(), "minMatches must be less than or equal to the number of offsets");
checkArgument(offsets.size() > 0, "offsets must have at least one element");
this.mask = mask;
this.excludeSelf = excludeSelf;
this.minMatches = minMatches;
this.maxMatches = maxMatches;
this.offsets = ImmutableList.copyOf(offsets);
}
/**
* Get the mask.
*
* @return the mask
*/
public Mask2D getMask() {
return mask;
}
/**
* Get the offsets.
*
* @return the offsets
*/
public List<BlockVector2> getOffsets() {
return this.offsets;
}
/**
* Get the flag determining if matching the current block should fail the mask.
*
* @return if it should exclude self-matches
*/
public boolean getExcludeSelf() {
return this.excludeSelf;
}
/**
* Gets the minimum number of matches to pass.
*
* @return the minimum number of matches
*/
public int getMinMatches() {
return this.minMatches;
}
/**
* Gets the maximum number of matches to pass.
*
* @return the maximum number of matches
*/
public int getMaxMatches() {
return this.maxMatches;
}
@Override
public boolean test(BlockVector2 vector) {
if (excludeSelf && mask.test(vector)) {
return false;
}
int matches = 0;
for (BlockVector2 offset : offsets) {
if (mask.test(vector.add(offset))) {
matches++;
if (matches > maxMatches) {
return false;
}
}
}
return minMatches <= matches && matches <= maxMatches;
}
}