Better mask parsing + optimize mask union/intersect
This commit is contained in:
parent
ba62563b72
commit
f5f326bf89
@ -57,6 +57,7 @@ import com.sk89q.worldedit.extent.transform.BlockTransformExtent;
|
|||||||
import com.sk89q.worldedit.function.entity.ExtentEntityCopy;
|
import com.sk89q.worldedit.function.entity.ExtentEntityCopy;
|
||||||
import com.sk89q.worldedit.function.mask.BlockMask;
|
import com.sk89q.worldedit.function.mask.BlockMask;
|
||||||
import com.sk89q.worldedit.function.mask.FuzzyBlockMask;
|
import com.sk89q.worldedit.function.mask.FuzzyBlockMask;
|
||||||
|
import com.sk89q.worldedit.function.mask.MaskUnion;
|
||||||
import com.sk89q.worldedit.function.mask.Masks;
|
import com.sk89q.worldedit.function.mask.Masks;
|
||||||
import com.sk89q.worldedit.function.mask.OffsetMask;
|
import com.sk89q.worldedit.function.mask.OffsetMask;
|
||||||
import com.sk89q.worldedit.function.mask.SolidBlockMask;
|
import com.sk89q.worldedit.function.mask.SolidBlockMask;
|
||||||
@ -408,7 +409,8 @@ public class Fawe {
|
|||||||
FuzzyBlockMask.inject(); // Optimizations
|
FuzzyBlockMask.inject(); // Optimizations
|
||||||
OffsetMask.inject(); // Optimizations
|
OffsetMask.inject(); // Optimizations
|
||||||
DefaultMaskParser.inject(); // Add new masks
|
DefaultMaskParser.inject(); // Add new masks
|
||||||
Masks.inject(); //
|
Masks.inject(); // Optimizations
|
||||||
|
MaskUnion.inject(); // Optimizations
|
||||||
// Operations
|
// Operations
|
||||||
Operations.inject(); // Optimizations
|
Operations.inject(); // Optimizations
|
||||||
ForwardExtentCopy.inject(); // Fixes + optimizations
|
ForwardExtentCopy.inject(); // Fixes + optimizations
|
||||||
|
@ -48,17 +48,18 @@ public class AngleMask extends SolidBlockMask implements ResettableMask {
|
|||||||
public boolean getAngle(Vector vector) {
|
public boolean getAngle(Vector vector) {
|
||||||
int x = vector.getBlockX();
|
int x = vector.getBlockX();
|
||||||
int z = vector.getBlockZ();
|
int z = vector.getBlockZ();
|
||||||
int o = getHighestTerrainBlock(x, z, 0, maxY);
|
// int o = getHighestTerrainBlock(x, z, 0, maxY);
|
||||||
if (getHighestTerrainBlock(x - 1, z, o + min, o + max) != -1) {
|
int y = vector.getBlockY();
|
||||||
|
if (getHighestTerrainBlock(x - 1, z, y + min, y + max) != -1) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (getHighestTerrainBlock(x + 1, z, o + min, o + max) != -1) {
|
if (getHighestTerrainBlock(x + 1, z, y + min, y + max) != -1) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (getHighestTerrainBlock(x, z - 1, o + min, o + max) != -1) {
|
if (getHighestTerrainBlock(x, z - 1, y + min, y + max) != -1) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (getHighestTerrainBlock(x, z + 1, o + min, o + max) != -1) {
|
if (getHighestTerrainBlock(x, z + 1, y + min, y + max) != -1) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@ -70,22 +71,22 @@ public class AngleMask extends SolidBlockMask implements ResettableMask {
|
|||||||
long pair = MathMan.pairInt(x, z);
|
long pair = MathMan.pairInt(x, z);
|
||||||
Integer height = heights.get(pair);
|
Integer height = heights.get(pair);
|
||||||
if (height != null) {
|
if (height != null) {
|
||||||
if (height >= minY && height <= maxY) {
|
if (height >= minY && height <= maxY && height >= 0 && height <= this.maxY) {
|
||||||
return height;
|
return height;
|
||||||
} else {
|
} else {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
maxY = Math.min(this.maxY, Math.max(0, maxY));
|
int maxSearchY = Math.min(this.maxY, Math.max(0, maxY));
|
||||||
minY = Math.max(0, minY);
|
int minSearchY = Math.min(this.maxY, Math.max(0, minY));
|
||||||
mutable.x = x;
|
mutable.x = x;
|
||||||
mutable.z = z;
|
mutable.z = z;
|
||||||
boolean air = false;
|
boolean air = false;
|
||||||
if (maxY != this.maxY) {
|
if (maxSearchY != this.maxY) {
|
||||||
mutable.y = maxY + 1;
|
mutable.y = maxSearchY + 1;
|
||||||
air = !super.test(mutable);
|
air = !super.test(mutable);
|
||||||
}
|
}
|
||||||
for (int y = maxY; y >= minY; --y) {
|
for (int y = maxSearchY; y >= minSearchY; --y) {
|
||||||
mutable.y = y;
|
mutable.y = y;
|
||||||
if (super.test(mutable)) {
|
if (super.test(mutable)) {
|
||||||
if (!air) {
|
if (!air) {
|
||||||
@ -98,7 +99,7 @@ public class AngleMask extends SolidBlockMask implements ResettableMask {
|
|||||||
air = true;
|
air = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (minY == 0 && maxY == this.maxY) {
|
if (minSearchY == 0 && maxSearchY == this.maxY) {
|
||||||
heights.put(pair, -1);
|
heights.put(pair, -1);
|
||||||
} else {
|
} else {
|
||||||
int value = getHighestTerrainBlock(x, z, 0, this.maxY);
|
int value = getHighestTerrainBlock(x, z, 0, this.maxY);
|
||||||
|
@ -90,16 +90,35 @@ public class DefaultMaskParser extends FaweParser<Mask> {
|
|||||||
public Mask parseFromInput(String input, ParserContext context) throws InputParseException {
|
public Mask parseFromInput(String input, ParserContext context) throws InputParseException {
|
||||||
List<Mask> masks = new ArrayList<Mask>();
|
List<Mask> masks = new ArrayList<Mask>();
|
||||||
|
|
||||||
for (String component : input.split(" ")) {
|
for (String component : split(input, ' ')) {
|
||||||
if (component.isEmpty()) {
|
if (component.isEmpty()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
HashSet<BaseBlock> blocks = new HashSet<BaseBlock>();
|
||||||
Mask current = getBlockMaskComponent(masks, component, context);
|
List<Mask> masksUnion = new ArrayList<Mask>();
|
||||||
|
for (String elem : split(component, ',')) {
|
||||||
masks.add(current);
|
ArrayList<Mask> list = new ArrayList<Mask>();
|
||||||
|
list.add(catchSuggestion(input, list, elem, context));
|
||||||
|
if (list.size() == 1) {
|
||||||
|
Mask mask = list.get(0);
|
||||||
|
if (mask.getClass() == BlockMask.class) {
|
||||||
|
blocks.addAll(((BlockMask) mask).getBlocks());
|
||||||
|
} else {
|
||||||
|
masksUnion.add(mask);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
masksUnion.add(new MaskIntersection(list));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!blocks.isEmpty()) {
|
||||||
|
masksUnion.add(new BlockMask(Request.request().getExtent(), blocks));
|
||||||
|
}
|
||||||
|
if (masksUnion.size() == 1) {
|
||||||
|
masks.add(masksUnion.get(0));
|
||||||
|
} else {
|
||||||
|
masks.add(new MaskUnion(masksUnion));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (masks.size()) {
|
switch (masks.size()) {
|
||||||
case 0:
|
case 0:
|
||||||
return null;
|
return null;
|
||||||
@ -123,7 +142,6 @@ public class DefaultMaskParser extends FaweParser<Mask> {
|
|||||||
|
|
||||||
private Mask getBlockMaskComponent(List<Mask> masks, String input, ParserContext context) throws InputParseException {
|
private Mask getBlockMaskComponent(List<Mask> masks, String input, ParserContext context) throws InputParseException {
|
||||||
Extent extent = Request.request().getExtent();
|
Extent extent = Request.request().getExtent();
|
||||||
|
|
||||||
final char firstChar = input.charAt(0);
|
final char firstChar = input.charAt(0);
|
||||||
switch (firstChar) {
|
switch (firstChar) {
|
||||||
case '#':
|
case '#':
|
||||||
@ -234,8 +252,8 @@ public class DefaultMaskParser extends FaweParser<Mask> {
|
|||||||
throw new SuggestInputParseException(input, "/<min-angle>:<max-angle>");
|
throw new SuggestInputParseException(input, "/<min-angle>:<max-angle>");
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
int y1 = (int) Math.abs(Expression.compile(split[0]).evaluate());
|
int y1 = (int) (Expression.compile(split[0]).evaluate());
|
||||||
int y2 = (int) Math.abs(Expression.compile(split[1]).evaluate());
|
int y2 = (int) (Expression.compile(split[1]).evaluate());
|
||||||
return new AngleMask(extent, y1, y2);
|
return new AngleMask(extent, y1, y2);
|
||||||
} catch (NumberFormatException | ExpressionException e) {
|
} catch (NumberFormatException | ExpressionException e) {
|
||||||
throw new SuggestInputParseException(input, "/<min-angle>:<max-angle>");
|
throw new SuggestInputParseException(input, "/<min-angle>:<max-angle>");
|
||||||
@ -338,34 +356,10 @@ public class DefaultMaskParser extends FaweParser<Mask> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
List<String> split = split(input, ',');
|
ParserContext tempContext = new ParserContext(context);
|
||||||
if (split.size() == 1) {
|
tempContext.setRestricted(false);
|
||||||
ParserContext tempContext = new ParserContext(context);
|
tempContext.setPreferringWildcard(true);
|
||||||
tempContext.setRestricted(false);
|
return new BlockMask(extent, worldEdit.getBlockFactory().parseFromListInput(input, tempContext));
|
||||||
tempContext.setPreferringWildcard(true);
|
|
||||||
return new BlockMask(extent, worldEdit.getBlockFactory().parseFromListInput(input, tempContext));
|
|
||||||
}
|
|
||||||
HashSet<BaseBlock> blocks = new HashSet<BaseBlock>();
|
|
||||||
ArrayList<Mask> maskUnion = new ArrayList<Mask>();
|
|
||||||
for (String elem : split) {
|
|
||||||
ArrayList<Mask> list = new ArrayList<Mask>();
|
|
||||||
list.add(catchSuggestion(input, list, elem, context));
|
|
||||||
if (list.size() == 1) {
|
|
||||||
Mask mask = list.get(0);
|
|
||||||
if (mask instanceof BlockMask) {
|
|
||||||
blocks.addAll(((BlockMask) mask).getBlocks());
|
|
||||||
} else {
|
|
||||||
maskUnion.add(mask);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!blocks.isEmpty()) {
|
|
||||||
maskUnion.add(new BlockMask(extent, blocks));
|
|
||||||
}
|
|
||||||
if (maskUnion.size() == 1) {
|
|
||||||
return maskUnion.get(0);
|
|
||||||
}
|
|
||||||
return new MaskUnion(maskUnion);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,129 @@
|
|||||||
|
/*
|
||||||
|
* 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 Lesser 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 Lesser General Public License
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.sk89q.worldedit.function.mask;
|
||||||
|
|
||||||
|
import com.sk89q.worldedit.Vector;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Combines several masks and requires that all masks return true
|
||||||
|
* when a certain position is tested. It serves as a logical AND operation
|
||||||
|
* on a list of masks.
|
||||||
|
*/
|
||||||
|
public class MaskIntersection extends AbstractMask {
|
||||||
|
|
||||||
|
private final Set<Mask> masks = new HashSet<Mask>();
|
||||||
|
private Mask[] masksArray;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new intersection.
|
||||||
|
*
|
||||||
|
* @param masks a list of masks
|
||||||
|
*/
|
||||||
|
public MaskIntersection(Collection<Mask> masks) {
|
||||||
|
checkNotNull(masks);
|
||||||
|
this.masks.addAll(masks);
|
||||||
|
formArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void formArray() {
|
||||||
|
if (masks.isEmpty()) {
|
||||||
|
masksArray = new Mask[] {Masks.alwaysFalse()};
|
||||||
|
} else {
|
||||||
|
masksArray = masks.toArray(new Mask[masks.size()]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new intersection.
|
||||||
|
*
|
||||||
|
* @param mask a list of masks
|
||||||
|
*/
|
||||||
|
public MaskIntersection(Mask... mask) {
|
||||||
|
this(Arrays.asList(checkNotNull(mask)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add some masks to the list.
|
||||||
|
*
|
||||||
|
* @param masks the masks
|
||||||
|
*/
|
||||||
|
public void add(Collection<Mask> masks) {
|
||||||
|
checkNotNull(masks);
|
||||||
|
this.masks.addAll(masks);
|
||||||
|
formArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add some masks to the list.
|
||||||
|
*
|
||||||
|
* @param mask the masks
|
||||||
|
*/
|
||||||
|
public void add(Mask... mask) {
|
||||||
|
add(Arrays.asList(checkNotNull(mask)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the masks that are tested with.
|
||||||
|
*
|
||||||
|
* @return the masks
|
||||||
|
*/
|
||||||
|
public Collection<Mask> getMasks() {
|
||||||
|
return masks;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Mask[] getMasksArray() {
|
||||||
|
return masksArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean test(Vector vector) {
|
||||||
|
for (Mask mask : masksArray) {
|
||||||
|
if (!mask.test(vector)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public Mask2D toMask2D() {
|
||||||
|
List<Mask2D> mask2dList = new ArrayList<Mask2D>();
|
||||||
|
for (Mask mask : masks) {
|
||||||
|
Mask2D mask2d = mask.toMask2D();
|
||||||
|
if (mask2d != null) {
|
||||||
|
mask2dList.add(mask2d);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new MaskIntersection2D(mask2dList);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,83 @@
|
|||||||
|
/*
|
||||||
|
* 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 Lesser 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 Lesser General Public License
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.sk89q.worldedit.function.mask;
|
||||||
|
|
||||||
|
import com.sk89q.worldedit.Vector;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Combines several masks and requires that one or more masks return true
|
||||||
|
* when a certain position is tested. It serves as a logical OR operation
|
||||||
|
* on a list of masks.
|
||||||
|
*/
|
||||||
|
public class MaskUnion extends MaskIntersection {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new union.
|
||||||
|
*
|
||||||
|
* @param masks a list of masks
|
||||||
|
*/
|
||||||
|
public MaskUnion(Collection<Mask> masks) {
|
||||||
|
super(masks);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new union.
|
||||||
|
*
|
||||||
|
* @param mask a list of masks
|
||||||
|
*/
|
||||||
|
public MaskUnion(Mask... mask) {
|
||||||
|
super(mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean test(Vector vector) {
|
||||||
|
for (Mask mask : getMasksArray()) {
|
||||||
|
if (mask.test(vector)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public Mask2D toMask2D() {
|
||||||
|
List<Mask2D> mask2dList = new ArrayList<Mask2D>();
|
||||||
|
for (Mask mask : getMasks()) {
|
||||||
|
Mask2D mask2d = mask.toMask2D();
|
||||||
|
if (mask2d != null) {
|
||||||
|
mask2dList.add(mask2d);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new MaskUnion2D(mask2dList);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Class<MaskUnion> inject() {
|
||||||
|
return MaskUnion.class;
|
||||||
|
}
|
||||||
|
}
|
@ -31,6 +31,10 @@ public final class Masks {
|
|||||||
return ALWAYS_TRUE;
|
return ALWAYS_TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Mask alwaysFalse() {
|
||||||
|
return ALWAYS_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a 2D mask that always returns true;
|
* Return a 2D mask that always returns true;
|
||||||
*
|
*
|
||||||
|
Loading…
Reference in New Issue
Block a user