Add a builder for OffsetsMask(2D)

This commit is contained in:
Octavia Togami 2020-09-19 16:27:24 -07:00
parent 46bcbe46bc
commit 534e7d3e40
4 changed files with 238 additions and 149 deletions

View File

@ -46,6 +46,8 @@ public List<String> getMatchedAliases() {
@Override
public Mask parseFromSimpleInput(String input, ParserContext context) throws InputParseException {
return new OffsetsMask(Masks.negate(new ExistingBlockMask(context.requireExtent())), true);
return OffsetsMask.builder(Masks.negate(new ExistingBlockMask(context.requireExtent())))
.excludeSelf(true)
.build();
}
}

View File

@ -26,7 +26,7 @@
import com.sk89q.worldedit.function.mask.Mask;
import com.sk89q.worldedit.function.mask.MaskIntersection;
import com.sk89q.worldedit.function.mask.Masks;
import com.sk89q.worldedit.function.mask.OffsetMask;
import com.sk89q.worldedit.function.mask.OffsetsMask;
import com.sk89q.worldedit.internal.registry.InputParser;
import com.sk89q.worldedit.math.BlockVector3;
@ -63,7 +63,7 @@ public Mask parseFromInput(String input, ParserContext context) throws InputPars
} else {
submask = new ExistingBlockMask(context.requireExtent());
}
OffsetMask offsetMask = new OffsetMask(submask, BlockVector3.at(0, firstChar == '>' ? -1 : 1, 0));
Mask offsetMask = OffsetsMask.single(submask, BlockVector3.at(0, firstChar == '>' ? -1 : 1, 0));
return new MaskIntersection(offsetMask, Masks.negate(submask));
}
}

View File

@ -20,10 +20,10 @@
package com.sk89q.worldedit.function.mask;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
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;
@ -42,78 +42,115 @@ public class OffsetsMask extends AbstractMask {
.collect(Collectors.toList())
);
/**
* Create an offsets mask for a single offset.
*
* @param mask the mask to use
* @param offset the offset
* @return the new offsets mask
*/
public static OffsetsMask single(Mask mask, BlockVector3 offset) {
return builder(mask).maxMatches(1).offsets(ImmutableList.of(offset)).build();
}
/**
* Create a new builder, using the given mask.
* @param mask the mask to use
* @return the builder
*/
public static Builder builder(Mask mask) {
return new Builder().mask(mask);
}
/**
* A builder for an {@link OffsetsMask}.
*/
public static final class Builder {
private Mask mask;
private boolean excludeSelf;
private int minMatches = 1;
private int maxMatches = Integer.MAX_VALUE;
private ImmutableList<BlockVector3> offsets = OFFSET_LIST;
private Builder() {
}
/**
* Set the mask to test.
* @param mask the mask to test
* @return this builder, for chaining
*/
public Builder mask(Mask mask) {
this.mask = mask;
return this;
}
/**
* Set whether the mask should fail if the original position matches. Defaults to
* {@code false}.
*
* @param excludeSelf {@code true} to exclude the original position if it matches
* @return this builder, for chaining
*/
public Builder excludeSelf(boolean excludeSelf) {
this.excludeSelf = excludeSelf;
return this;
}
/**
* Set the minimum amount of matches required. Defaults to {@code 1}. Must be smaller than
* or equal to the {@linkplain #maxMatches(int) max matches} and the {@link #offsets} size,
* and greater than or equal to {@code 0}.
*
* @param minMatches the minimum amount of matches required
* @return this builder, for chaining
*/
public Builder minMatches(int minMatches) {
this.minMatches = minMatches;
return this;
}
/**
* Set the maximum amount of matches allowed. Defaults to {@link Integer#MAX_VALUE}. Must
* be greater than or equal to {@linkplain #minMatches(int)}.
*
* @param maxMatches the maximum amount of matches allowed
* @return this builder, for chaining
*/
public Builder maxMatches(int maxMatches) {
this.maxMatches = maxMatches;
return this;
}
/**
* Set the offsets to test. Defaults to all {@linkplain Direction.Flag#CARDINAL cardinal}
* and {@linkplain Direction.Flag#UPRIGHT upright} directions.
*
* @param offsets the offsets to test
* @return this builder, for chaining
*/
public Builder offsets(Iterable<BlockVector3> offsets) {
this.offsets = ImmutableList.copyOf(offsets);
return this;
}
/**
* Build an offsets mask.
*
* @return the new mask
*/
public OffsetsMask build() {
return new OffsetsMask(mask, excludeSelf, minMatches, maxMatches, offsets);
}
}
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) {
private OffsetsMask(Mask mask, boolean excludeSelf, int minMatches, int maxMatches, ImmutableList<BlockVector3> offsets) {
checkNotNull(mask);
checkNotNull(offsets);
// Validate match args. No need to test maxMatches as it must be >=0 based on the conditions here.
@ -126,7 +163,7 @@ public OffsetsMask(Mask mask, boolean excludeSelf, int minMatches, int maxMatche
this.excludeSelf = excludeSelf;
this.minMatches = minMatches;
this.maxMatches = maxMatches;
this.offsets = ImmutableList.copyOf(offsets);
this.offsets = offsets;
}
/**
@ -138,15 +175,6 @@ 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.
*
@ -174,6 +202,15 @@ public int getMaxMatches() {
return this.maxMatches;
}
/**
* Get the offsets.
*
* @return the offsets
*/
public ImmutableList<BlockVector3> getOffsets() {
return this.offsets;
}
@Override
public boolean test(BlockVector3 vector) {
if (excludeSelf && mask.test(vector)) {
@ -199,13 +236,12 @@ public boolean test(BlockVector3 vector) {
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())
);
return OffsetsMask2D.builder(childMask)
.excludeSelf(excludeSelf)
.minMatches(minMatches)
.maxMatches(maxMatches)
.offsets(Lists.transform(offsets, BlockVector3::toBlockVector2))
.build();
} else {
return null;
}

View File

@ -43,64 +43,115 @@ public class OffsetsMask2D extends AbstractMask2D {
.collect(Collectors.toList())
);
/**
* Create an offsets mask for a single offset.
*
* @param mask the mask to use
* @param offset the offset
* @return the new offsets mask
*/
public static OffsetsMask2D single(Mask2D mask, BlockVector2 offset) {
return builder(mask).maxMatches(1).offsets(ImmutableList.of(offset)).build();
}
/**
* Create a new builder, using the given mask.
* @param mask the mask to use
* @return the builder
*/
public static Builder builder(Mask2D mask) {
return new Builder().mask(mask);
}
/**
* A builder for an {@link OffsetsMask}.
*/
public static final class Builder {
private Mask2D mask;
private boolean excludeSelf;
private int minMatches = 1;
private int maxMatches = Integer.MAX_VALUE;
private ImmutableList<BlockVector2> offsets = OFFSET_LIST;
private Builder() {
}
/**
* Set the mask to test.
* @param mask the mask to test
* @return this builder, for chaining
*/
public Builder mask(Mask2D mask) {
this.mask = mask;
return this;
}
/**
* Set whether the mask should fail if the original position matches. Defaults to
* {@code false}.
*
* @param excludeSelf {@code true} to exclude the original position if it matches
* @return this builder, for chaining
*/
public Builder excludeSelf(boolean excludeSelf) {
this.excludeSelf = excludeSelf;
return this;
}
/**
* Set the minimum amount of matches required. Defaults to {@code 1}. Must be smaller than
* or equal to the {@linkplain #maxMatches(int) max matches} and the {@link #offsets} size,
* and greater than or equal to {@code 0}.
*
* @param minMatches the minimum amount of matches required
* @return this builder, for chaining
*/
public Builder minMatches(int minMatches) {
this.minMatches = minMatches;
return this;
}
/**
* Set the maximum amount of matches allowed. Defaults to {@link Integer#MAX_VALUE}. Must
* be greater than or equal to {@linkplain #minMatches(int)}.
*
* @param maxMatches the maximum amount of matches allowed
* @return this builder, for chaining
*/
public Builder maxMatches(int maxMatches) {
this.maxMatches = maxMatches;
return this;
}
/**
* Set the offsets to test. Defaults to all {@linkplain Direction.Flag#CARDINAL cardinal}
* directions.
*
* @param offsets the offsets to test
* @return this builder, for chaining
*/
public Builder offsets(Iterable<BlockVector2> offsets) {
this.offsets = ImmutableList.copyOf(offsets);
return this;
}
/**
* Build an offsets mask.
*
* @return the new mask
*/
public OffsetsMask2D build() {
return new OffsetsMask2D(mask, excludeSelf, minMatches, maxMatches, offsets);
}
}
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) {
private OffsetsMask2D(Mask2D mask, boolean excludeSelf, int minMatches, int maxMatches, ImmutableList<BlockVector2> offsets) {
checkNotNull(mask);
checkNotNull(offsets);
// Validate match args. No need to test maxMatches as it must be >=0 based on the conditions here.
@ -113,7 +164,7 @@ public OffsetsMask2D(Mask2D mask, boolean excludeSelf, int minMatches, int maxMa
this.excludeSelf = excludeSelf;
this.minMatches = minMatches;
this.maxMatches = maxMatches;
this.offsets = ImmutableList.copyOf(offsets);
this.offsets = offsets;
}
/**
@ -125,15 +176,6 @@ 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.
*
@ -161,6 +203,15 @@ public int getMaxMatches() {
return this.maxMatches;
}
/**
* Get the offsets.
*
* @return the offsets
*/
public ImmutableList<BlockVector2> getOffsets() {
return this.offsets;
}
@Override
public boolean test(BlockVector2 vector) {
if (excludeSelf && mask.test(vector)) {