Better mask parsing + optimize mask union/intersect

This commit is contained in:
Jesse Boyd 2016-12-21 05:44:01 +11:00
parent ba62563b72
commit f5f326bf89
No known key found for this signature in database
GPG Key ID: 59F1DE6293AF6E1F
6 changed files with 263 additions and 50 deletions

View File

@ -57,6 +57,7 @@ import com.sk89q.worldedit.extent.transform.BlockTransformExtent;
import com.sk89q.worldedit.function.entity.ExtentEntityCopy;
import com.sk89q.worldedit.function.mask.BlockMask;
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.OffsetMask;
import com.sk89q.worldedit.function.mask.SolidBlockMask;
@ -408,7 +409,8 @@ public class Fawe {
FuzzyBlockMask.inject(); // Optimizations
OffsetMask.inject(); // Optimizations
DefaultMaskParser.inject(); // Add new masks
Masks.inject(); //
Masks.inject(); // Optimizations
MaskUnion.inject(); // Optimizations
// Operations
Operations.inject(); // Optimizations
ForwardExtentCopy.inject(); // Fixes + optimizations

View File

@ -48,17 +48,18 @@ public class AngleMask extends SolidBlockMask implements ResettableMask {
public boolean getAngle(Vector vector) {
int x = vector.getBlockX();
int z = vector.getBlockZ();
int o = getHighestTerrainBlock(x, z, 0, maxY);
if (getHighestTerrainBlock(x - 1, z, o + min, o + max) != -1) {
// int o = getHighestTerrainBlock(x, z, 0, maxY);
int y = vector.getBlockY();
if (getHighestTerrainBlock(x - 1, z, y + min, y + max) != -1) {
return true;
}
if (getHighestTerrainBlock(x + 1, z, o + min, o + max) != -1) {
if (getHighestTerrainBlock(x + 1, z, y + min, y + max) != -1) {
return true;
}
if (getHighestTerrainBlock(x, z - 1, o + min, o + max) != -1) {
if (getHighestTerrainBlock(x, z - 1, y + min, y + max) != -1) {
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 false;
@ -70,22 +71,22 @@ public class AngleMask extends SolidBlockMask implements ResettableMask {
long pair = MathMan.pairInt(x, z);
Integer height = heights.get(pair);
if (height != null) {
if (height >= minY && height <= maxY) {
if (height >= minY && height <= maxY && height >= 0 && height <= this.maxY) {
return height;
} else {
return -1;
}
}
maxY = Math.min(this.maxY, Math.max(0, maxY));
minY = Math.max(0, minY);
int maxSearchY = Math.min(this.maxY, Math.max(0, maxY));
int minSearchY = Math.min(this.maxY, Math.max(0, minY));
mutable.x = x;
mutable.z = z;
boolean air = false;
if (maxY != this.maxY) {
mutable.y = maxY + 1;
if (maxSearchY != this.maxY) {
mutable.y = maxSearchY + 1;
air = !super.test(mutable);
}
for (int y = maxY; y >= minY; --y) {
for (int y = maxSearchY; y >= minSearchY; --y) {
mutable.y = y;
if (super.test(mutable)) {
if (!air) {
@ -98,7 +99,7 @@ public class AngleMask extends SolidBlockMask implements ResettableMask {
air = true;
}
}
if (minY == 0 && maxY == this.maxY) {
if (minSearchY == 0 && maxSearchY == this.maxY) {
heights.put(pair, -1);
} else {
int value = getHighestTerrainBlock(x, z, 0, this.maxY);

View File

@ -90,16 +90,35 @@ public class DefaultMaskParser extends FaweParser<Mask> {
public Mask parseFromInput(String input, ParserContext context) throws InputParseException {
List<Mask> masks = new ArrayList<Mask>();
for (String component : input.split(" ")) {
for (String component : split(input, ' ')) {
if (component.isEmpty()) {
continue;
}
Mask current = getBlockMaskComponent(masks, component, context);
masks.add(current);
HashSet<BaseBlock> blocks = new HashSet<BaseBlock>();
List<Mask> masksUnion = new ArrayList<Mask>();
for (String elem : split(component, ',')) {
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()) {
case 0:
return null;
@ -123,7 +142,6 @@ public class DefaultMaskParser extends FaweParser<Mask> {
private Mask getBlockMaskComponent(List<Mask> masks, String input, ParserContext context) throws InputParseException {
Extent extent = Request.request().getExtent();
final char firstChar = input.charAt(0);
switch (firstChar) {
case '#':
@ -234,8 +252,8 @@ public class DefaultMaskParser extends FaweParser<Mask> {
throw new SuggestInputParseException(input, "/<min-angle>:<max-angle>");
}
try {
int y1 = (int) Math.abs(Expression.compile(split[0]).evaluate());
int y2 = (int) Math.abs(Expression.compile(split[1]).evaluate());
int y1 = (int) (Expression.compile(split[0]).evaluate());
int y2 = (int) (Expression.compile(split[1]).evaluate());
return new AngleMask(extent, y1, y2);
} catch (NumberFormatException | ExpressionException e) {
throw new SuggestInputParseException(input, "/<min-angle>:<max-angle>");
@ -338,34 +356,10 @@ public class DefaultMaskParser extends FaweParser<Mask> {
}
}
}
List<String> split = split(input, ',');
if (split.size() == 1) {
ParserContext tempContext = new ParserContext(context);
tempContext.setRestricted(false);
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);
ParserContext tempContext = new ParserContext(context);
tempContext.setRestricted(false);
tempContext.setPreferringWildcard(true);
return new BlockMask(extent, worldEdit.getBlockFactory().parseFromListInput(input, tempContext));
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -31,6 +31,10 @@ public final class Masks {
return ALWAYS_TRUE;
}
public static Mask alwaysFalse() {
return ALWAYS_FALSE;
}
/**
* Return a 2D mask that always returns true;
*