geforkt von Mirrors/FastAsyncWorldEdit
Allow parallelisation of masks (#636)
* Allow parallelisation of masks Increasing performance improvement with increased masking complexity and edit size. * Address comments - Rename Mask#clone to Mask#copy - Rename Mask2D#copy to Mask2D#copy2D - Correct formatting * cx -> centerX * Make various operations relying on a single SingleThreadQueueExtent instance (mainly brushes) thread safe
Dieser Commit ist enthalten in:
Ursprung
3f1e8fa4d4
Commit
855389c785
@ -17,7 +17,7 @@ public abstract class CharBlocks implements IBlocks {
|
|||||||
};
|
};
|
||||||
public static final Section EMPTY = new Section() {
|
public static final Section EMPTY = new Section() {
|
||||||
@Override
|
@Override
|
||||||
public final char[] get(CharBlocks blocks, int layer) {
|
public synchronized final char[] get(CharBlocks blocks, int layer) {
|
||||||
char[] arr = blocks.blocks[layer];
|
char[] arr = blocks.blocks[layer];
|
||||||
if (arr == null) {
|
if (arr == null) {
|
||||||
arr = blocks.blocks[layer] = blocks.update(layer, null);
|
arr = blocks.blocks[layer] = blocks.update(layer, null);
|
||||||
|
@ -6,6 +6,7 @@ import com.boydti.fawe.beta.implementation.filter.block.FilterBlock;
|
|||||||
import com.sk89q.worldedit.function.mask.AbstractExtentMask;
|
import com.sk89q.worldedit.function.mask.AbstractExtentMask;
|
||||||
import com.sk89q.worldedit.function.mask.Mask;
|
import com.sk89q.worldedit.function.mask.Mask;
|
||||||
|
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -16,7 +17,7 @@ import java.util.function.Supplier;
|
|||||||
public class MaskFilter<T extends Filter> extends DelegateFilter<T> {
|
public class MaskFilter<T extends Filter> extends DelegateFilter<T> {
|
||||||
private final Supplier<Mask> supplier;
|
private final Supplier<Mask> supplier;
|
||||||
private final Mask mask;
|
private final Mask mask;
|
||||||
private int changes;
|
private final AtomicInteger changes;
|
||||||
|
|
||||||
public MaskFilter(T other, Mask mask) {
|
public MaskFilter(T other, Mask mask) {
|
||||||
this(other, () -> mask);
|
this(other, () -> mask);
|
||||||
@ -27,9 +28,14 @@ public class MaskFilter<T extends Filter> extends DelegateFilter<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public MaskFilter(T other, Supplier<Mask> supplier, Mask root) {
|
public MaskFilter(T other, Supplier<Mask> supplier, Mask root) {
|
||||||
|
this(other, supplier, root, new AtomicInteger());
|
||||||
|
}
|
||||||
|
|
||||||
|
public MaskFilter(T other, Supplier<Mask> supplier, Mask root, AtomicInteger changes) {
|
||||||
super(other);
|
super(other);
|
||||||
this.supplier = supplier;
|
this.supplier = supplier;
|
||||||
this.mask = root;
|
this.mask = root;
|
||||||
|
this.changes = changes;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -37,11 +43,11 @@ public class MaskFilter<T extends Filter> extends DelegateFilter<T> {
|
|||||||
if (mask instanceof AbstractExtentMask) {
|
if (mask instanceof AbstractExtentMask) {
|
||||||
if (((AbstractExtentMask) mask).test(block, block)) {
|
if (((AbstractExtentMask) mask).test(block, block)) {
|
||||||
getParent().applyBlock(block);
|
getParent().applyBlock(block);
|
||||||
this.changes++;
|
this.changes.incrementAndGet();
|
||||||
}
|
}
|
||||||
} else if (mask.test(block)) {
|
} else if (mask.test(block)) {
|
||||||
getParent().applyBlock(block);
|
getParent().applyBlock(block);
|
||||||
this.changes++;
|
this.changes.incrementAndGet();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,11 +57,16 @@ public class MaskFilter<T extends Filter> extends DelegateFilter<T> {
|
|||||||
* @return number of blocks which passed the Mask test and were applied to
|
* @return number of blocks which passed the Mask test and were applied to
|
||||||
*/
|
*/
|
||||||
public int getBlocksApplied(){
|
public int getBlocksApplied(){
|
||||||
return this.changes;
|
return this.changes.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MaskFilter newInstance(Filter other) {
|
public MaskFilter<?> newInstance(Filter other) {
|
||||||
return new MaskFilter<>(other, supplier);
|
return new MaskFilter<>(other, supplier);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Filter fork() {
|
||||||
|
return new MaskFilter<>(getParent().fork(), mask::copy, mask.copy(), changes);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,18 +1,26 @@
|
|||||||
package com.boydti.fawe.beta.implementation.processors;
|
package com.boydti.fawe.beta.implementation.processors;
|
||||||
|
|
||||||
|
import com.boydti.fawe.FaweCache;
|
||||||
|
import com.boydti.fawe.beta.Filter;
|
||||||
import com.boydti.fawe.beta.IBatchProcessor;
|
import com.boydti.fawe.beta.IBatchProcessor;
|
||||||
import com.boydti.fawe.beta.IChunk;
|
import com.boydti.fawe.beta.IChunk;
|
||||||
import com.boydti.fawe.beta.IChunkGet;
|
import com.boydti.fawe.beta.IChunkGet;
|
||||||
import com.boydti.fawe.beta.IChunkSet;
|
import com.boydti.fawe.beta.IChunkSet;
|
||||||
import com.boydti.fawe.util.StringMan;
|
import com.boydti.fawe.util.StringMan;
|
||||||
|
import com.google.common.cache.LoadingCache;
|
||||||
import com.sk89q.worldedit.extent.Extent;
|
import com.sk89q.worldedit.extent.Extent;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
public class MultiBatchProcessor implements IBatchProcessor {
|
public class MultiBatchProcessor implements IBatchProcessor {
|
||||||
private IBatchProcessor[] processors;
|
private IBatchProcessor[] processors;
|
||||||
|
private final LoadingCache<Class<?>, Map<Long, Filter>> classToThreadIdToFilter =
|
||||||
|
FaweCache.IMP.createCache((Supplier<Map<Long, Filter>>) HashMap::new);
|
||||||
|
|
||||||
public MultiBatchProcessor(IBatchProcessor... processors) {
|
public MultiBatchProcessor(IBatchProcessor... processors) {
|
||||||
this.processors = processors;
|
this.processors = processors;
|
||||||
@ -56,14 +64,20 @@ public class MultiBatchProcessor implements IBatchProcessor {
|
|||||||
@Override
|
@Override
|
||||||
public IChunkSet processSet(IChunk chunk, IChunkGet get, IChunkSet set) {
|
public IChunkSet processSet(IChunk chunk, IChunkGet get, IChunkSet set) {
|
||||||
try {
|
try {
|
||||||
for (int i = processors.length - 1 ; i >= 0; i--) {
|
IChunkSet chunkSet = set;
|
||||||
|
for (int i = processors.length - 1; i >= 0; i--) {
|
||||||
IBatchProcessor processor = processors[i];
|
IBatchProcessor processor = processors[i];
|
||||||
set = processor.processSet(chunk, get, set);
|
if (processor instanceof Filter) {
|
||||||
if (set == null) {
|
chunkSet = ((IBatchProcessor) classToThreadIdToFilter.getUnchecked(processor.getClass())
|
||||||
|
.computeIfAbsent(Thread.currentThread().getId(), k -> ((Filter) processor).fork())).processSet(chunk, get, chunkSet);
|
||||||
|
} else {
|
||||||
|
chunkSet = processor.processSet(chunk, get, chunkSet);
|
||||||
|
}
|
||||||
|
if (chunkSet == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return set;
|
return chunkSet;
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
throw e;
|
throw e;
|
||||||
|
@ -24,6 +24,7 @@ import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap;
|
|||||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Single threaded implementation for IQueueExtent (still abstract) - Does not implement creation of
|
* Single threaded implementation for IQueueExtent (still abstract) - Does not implement creation of
|
||||||
@ -52,6 +53,8 @@ public class SingleThreadQueueExtent extends ExtentBatchProcessorHolder implemen
|
|||||||
|
|
||||||
private boolean fastmode = false;
|
private boolean fastmode = false;
|
||||||
|
|
||||||
|
private final ReentrantLock getChunkLock = new ReentrantLock();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Safety check to ensure that the thread being used matches the one being initialized on. - Can
|
* Safety check to ensure that the thread being used matches the one being initialized on. - Can
|
||||||
* be removed later
|
* be removed later
|
||||||
@ -217,6 +220,8 @@ public class SingleThreadQueueExtent extends ExtentBatchProcessorHolder implemen
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final IQueueChunk getOrCreateChunk(int x, int z) {
|
public final IQueueChunk getOrCreateChunk(int x, int z) {
|
||||||
|
getChunkLock.lock();
|
||||||
|
try {
|
||||||
final long pair = (long) x << 32 | z & 0xffffffffL;
|
final long pair = (long) x << 32 | z & 0xffffffffL;
|
||||||
if (pair == lastPair) {
|
if (pair == lastPair) {
|
||||||
return lastChunk;
|
return lastChunk;
|
||||||
@ -261,6 +266,9 @@ public class SingleThreadQueueExtent extends ExtentBatchProcessorHolder implemen
|
|||||||
lastChunk = chunk;
|
lastChunk = chunk;
|
||||||
|
|
||||||
return chunk;
|
return chunk;
|
||||||
|
} finally {
|
||||||
|
getChunkLock.unlock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package com.boydti.fawe.object.brush;
|
package com.boydti.fawe.object.brush;
|
||||||
|
|
||||||
|
import com.boydti.fawe.object.brush.mask.ImageBrushMask;
|
||||||
import com.boydti.fawe.object.collection.SummedColorTable;
|
import com.boydti.fawe.object.collection.SummedColorTable;
|
||||||
import com.boydti.fawe.object.mask.SurfaceMask;
|
import com.boydti.fawe.object.mask.SurfaceMask;
|
||||||
import com.boydti.fawe.util.TextureUtil;
|
import com.boydti.fawe.util.TextureUtil;
|
||||||
@ -8,18 +9,14 @@ import com.sk89q.worldedit.LocalSession;
|
|||||||
import com.sk89q.worldedit.MaxChangedBlocksException;
|
import com.sk89q.worldedit.MaxChangedBlocksException;
|
||||||
import com.sk89q.worldedit.command.tool.brush.Brush;
|
import com.sk89q.worldedit.command.tool.brush.Brush;
|
||||||
import com.sk89q.worldedit.extent.Extent;
|
import com.sk89q.worldedit.extent.Extent;
|
||||||
import com.sk89q.worldedit.function.mask.AbstractExtentMask;
|
|
||||||
import com.sk89q.worldedit.function.mask.Mask;
|
import com.sk89q.worldedit.function.mask.Mask;
|
||||||
import com.sk89q.worldedit.function.operation.Operations;
|
import com.sk89q.worldedit.function.operation.Operations;
|
||||||
import com.sk89q.worldedit.function.pattern.Pattern;
|
import com.sk89q.worldedit.function.pattern.Pattern;
|
||||||
import com.sk89q.worldedit.function.visitor.RecursiveVisitor;
|
import com.sk89q.worldedit.function.visitor.RecursiveVisitor;
|
||||||
import com.sk89q.worldedit.math.BlockVector3;
|
import com.sk89q.worldedit.math.BlockVector3;
|
||||||
import com.sk89q.worldedit.math.MutableVector3;
|
|
||||||
import com.sk89q.worldedit.math.Vector3;
|
|
||||||
import com.sk89q.worldedit.math.transform.AffineTransform;
|
import com.sk89q.worldedit.math.transform.AffineTransform;
|
||||||
import com.sk89q.worldedit.util.Location;
|
import com.sk89q.worldedit.util.Location;
|
||||||
import com.sk89q.worldedit.world.block.BlockState;
|
import com.sk89q.worldedit.world.block.BlockState;
|
||||||
import com.sk89q.worldedit.world.block.BlockType;
|
|
||||||
|
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -30,8 +27,8 @@ public class ImageBrush implements Brush {
|
|||||||
private final SummedColorTable table;
|
private final SummedColorTable table;
|
||||||
private final int width;
|
private final int width;
|
||||||
private final int height;
|
private final int height;
|
||||||
private final double centerX;
|
private final double centerImageX;
|
||||||
private final double centerZ;
|
private final double centerImageZ;
|
||||||
|
|
||||||
private final ColorFunction colorFunction;
|
private final ColorFunction colorFunction;
|
||||||
|
|
||||||
@ -40,8 +37,8 @@ public class ImageBrush implements Brush {
|
|||||||
this.table = new SummedColorTable(image, alpha);
|
this.table = new SummedColorTable(image, alpha);
|
||||||
this.width = image.getWidth();
|
this.width = image.getWidth();
|
||||||
this.height = image.getHeight();
|
this.height = image.getHeight();
|
||||||
this.centerX = width / 2d;
|
this.centerImageX = width / 2d;
|
||||||
this.centerZ = height / 2d;
|
this.centerImageZ = height / 2d;
|
||||||
|
|
||||||
if (alpha) {
|
if (alpha) {
|
||||||
colorFunction = (x1, z1, x2, z2, extent, pos) -> {
|
colorFunction = (x1, z1, x2, z2, extent, pos) -> {
|
||||||
@ -65,17 +62,12 @@ public class ImageBrush implements Brush {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private interface ColorFunction {
|
public interface ColorFunction {
|
||||||
int call(int x1, int z1, int x2, int z2, Extent extent, BlockVector3 pos);
|
int call(int x1, int z1, int x2, int z2, Extent extent, BlockVector3 pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void build(EditSession editSession, BlockVector3 position, Pattern pattern, double sizeDouble) throws MaxChangedBlocksException {
|
public void build(EditSession editSession, BlockVector3 center, Pattern pattern, double sizeDouble) throws MaxChangedBlocksException {
|
||||||
TextureUtil texture = session.getTextureUtil();
|
|
||||||
|
|
||||||
final int cx = position.getBlockX();
|
|
||||||
final int cy = position.getBlockY();
|
|
||||||
final int cz = position.getBlockZ();
|
|
||||||
final Mask solid = new SurfaceMask(editSession);
|
final Mask solid = new SurfaceMask(editSession);
|
||||||
|
|
||||||
double scale = Math.max(width, height) / sizeDouble;
|
double scale = Math.max(width, height) / sizeDouble;
|
||||||
@ -85,56 +77,11 @@ public class ImageBrush implements Brush {
|
|||||||
float pitch = loc.getPitch();
|
float pitch = loc.getPitch();
|
||||||
AffineTransform transform = new AffineTransform().rotateY((-yaw) % 360).rotateX((pitch - 90) % 360).inverse();
|
AffineTransform transform = new AffineTransform().rotateY((-yaw) % 360).rotateX((pitch - 90) % 360).inverse();
|
||||||
|
|
||||||
RecursiveVisitor visitor = new RecursiveVisitor(new AbstractExtentMask(editSession) {
|
RecursiveVisitor visitor = new RecursiveVisitor(
|
||||||
private final MutableVector3 mutable = new MutableVector3();
|
new ImageBrushMask(solid, center, transform, scale, centerImageX, centerImageZ, width, height, colorFunction, editSession,
|
||||||
|
session.getTextureUtil()), vector -> true, Integer.MAX_VALUE);
|
||||||
@Override
|
|
||||||
public boolean test(Extent extent, BlockVector3 vector) {
|
|
||||||
return test(vector);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean test(BlockVector3 vector) {
|
|
||||||
if (solid.test(vector)) {
|
|
||||||
int dx = vector.getBlockX() - cx;
|
|
||||||
int dy = vector.getBlockY() - cy;
|
|
||||||
int dz = vector.getBlockZ() - cz;
|
|
||||||
|
|
||||||
Vector3 pos1 = transform.apply(mutable.setComponents(dx - 0.5, dy - 0.5, dz - 0.5));
|
|
||||||
int x1 = (int) (pos1.getX() * scale + centerX);
|
|
||||||
int z1 = (int) (pos1.getZ() * scale + centerZ);
|
|
||||||
|
|
||||||
Vector3 pos2 = transform.apply(mutable.setComponents(dx + 0.5, dy + 0.5, dz + 0.5));
|
|
||||||
int x2 = (int) (pos2.getX() * scale + centerX);
|
|
||||||
int z2 = (int) (pos2.getZ() * scale + centerZ);
|
|
||||||
if (x2 < x1) {
|
|
||||||
int tmp = x1;
|
|
||||||
x1 = x2;
|
|
||||||
x2 = tmp;
|
|
||||||
}
|
|
||||||
if (z2 < z1) {
|
|
||||||
int tmp = z1;
|
|
||||||
z1 = z2;
|
|
||||||
z2 = tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (x1 >= width || x2 < 0 || z1 >= height || z2 < 0) return false;
|
|
||||||
|
|
||||||
|
|
||||||
int color = colorFunction.call(x1, z1, x2, z2, editSession, vector);
|
|
||||||
if (color != 0) {
|
|
||||||
BlockType block = texture.getNearestBlock(color);
|
|
||||||
if (block != null) {
|
|
||||||
editSession.setBlock(vector, block.getDefaultState());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}, vector -> true, Integer.MAX_VALUE);
|
|
||||||
visitor.setDirections(Arrays.asList(visitor.DIAGONAL_DIRECTIONS));
|
visitor.setDirections(Arrays.asList(visitor.DIAGONAL_DIRECTIONS));
|
||||||
visitor.visit(position);
|
visitor.visit(center);
|
||||||
Operations.completeBlindly(visitor);
|
Operations.completeBlindly(visitor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,22 +1,20 @@
|
|||||||
package com.boydti.fawe.object.brush;
|
package com.boydti.fawe.object.brush;
|
||||||
|
|
||||||
|
import com.boydti.fawe.object.brush.mask.LayerBrushMask;
|
||||||
import com.boydti.fawe.object.collection.BlockVectorSet;
|
import com.boydti.fawe.object.collection.BlockVectorSet;
|
||||||
import com.boydti.fawe.object.mask.AdjacentAnyMask;
|
import com.boydti.fawe.object.mask.AdjacentAnyMask;
|
||||||
import com.boydti.fawe.object.mask.RadiusMask;
|
import com.boydti.fawe.object.mask.RadiusMask;
|
||||||
import com.sk89q.worldedit.EditSession;
|
import com.sk89q.worldedit.EditSession;
|
||||||
import com.sk89q.worldedit.MaxChangedBlocksException;
|
import com.sk89q.worldedit.MaxChangedBlocksException;
|
||||||
import com.sk89q.worldedit.command.tool.brush.Brush;
|
import com.sk89q.worldedit.command.tool.brush.Brush;
|
||||||
import com.sk89q.worldedit.extent.Extent;
|
|
||||||
import com.sk89q.worldedit.function.mask.AbstractExtentMask;
|
|
||||||
import com.sk89q.worldedit.function.mask.BlockMask;
|
import com.sk89q.worldedit.function.mask.BlockMask;
|
||||||
import com.sk89q.worldedit.function.mask.Mask;
|
import com.sk89q.worldedit.function.mask.MaskIntersection;
|
||||||
import com.sk89q.worldedit.function.mask.SolidBlockMask;
|
import com.sk89q.worldedit.function.mask.SolidBlockMask;
|
||||||
import com.sk89q.worldedit.function.operation.Operations;
|
import com.sk89q.worldedit.function.operation.Operations;
|
||||||
import com.sk89q.worldedit.function.pattern.Pattern;
|
import com.sk89q.worldedit.function.pattern.Pattern;
|
||||||
import com.sk89q.worldedit.function.visitor.BreadthFirstSearch;
|
import com.sk89q.worldedit.function.visitor.BreadthFirstSearch;
|
||||||
import com.sk89q.worldedit.function.visitor.RecursiveVisitor;
|
import com.sk89q.worldedit.function.visitor.RecursiveVisitor;
|
||||||
import com.sk89q.worldedit.math.BlockVector3;
|
import com.sk89q.worldedit.math.BlockVector3;
|
||||||
import com.sk89q.worldedit.math.MutableBlockVector3;
|
|
||||||
import com.sk89q.worldedit.world.block.BlockState;
|
import com.sk89q.worldedit.world.block.BlockState;
|
||||||
import com.sk89q.worldedit.world.block.BlockTypes;
|
import com.sk89q.worldedit.world.block.BlockTypes;
|
||||||
|
|
||||||
@ -26,7 +24,6 @@ public class LayerBrush implements Brush {
|
|||||||
|
|
||||||
private final BlockState[] layers;
|
private final BlockState[] layers;
|
||||||
private RecursiveVisitor visitor;
|
private RecursiveVisitor visitor;
|
||||||
private MutableBlockVector3 mutable = new MutableBlockVector3();
|
|
||||||
|
|
||||||
public LayerBrush(BlockState[] layers) {
|
public LayerBrush(BlockState[] layers) {
|
||||||
this.layers = layers;
|
this.layers = layers;
|
||||||
@ -37,48 +34,12 @@ public class LayerBrush implements Brush {
|
|||||||
final AdjacentAnyMask adjacent = new AdjacentAnyMask(new BlockMask(editSession).add(BlockTypes.AIR, BlockTypes.CAVE_AIR, BlockTypes.VOID_AIR));
|
final AdjacentAnyMask adjacent = new AdjacentAnyMask(new BlockMask(editSession).add(BlockTypes.AIR, BlockTypes.CAVE_AIR, BlockTypes.VOID_AIR));
|
||||||
final SolidBlockMask solid = new SolidBlockMask(editSession);
|
final SolidBlockMask solid = new SolidBlockMask(editSession);
|
||||||
final RadiusMask radius = new RadiusMask(0, (int) size);
|
final RadiusMask radius = new RadiusMask(0, (int) size);
|
||||||
visitor = new RecursiveVisitor(new Mask() {
|
visitor = new RecursiveVisitor(new MaskIntersection(adjacent, solid, radius), function -> true);
|
||||||
@Override
|
|
||||||
public boolean test(BlockVector3 vector) {
|
|
||||||
return solid.test(vector) && radius.test(vector) && adjacent.test(vector);
|
|
||||||
}
|
|
||||||
}, function -> true);
|
|
||||||
visitor.visit(position);
|
visitor.visit(position);
|
||||||
visitor.setDirections(Arrays.asList(BreadthFirstSearch.DIAGONAL_DIRECTIONS));
|
visitor.setDirections(Arrays.asList(BreadthFirstSearch.DIAGONAL_DIRECTIONS));
|
||||||
Operations.completeBlindly(visitor);
|
Operations.completeBlindly(visitor);
|
||||||
BlockVectorSet visited = visitor.getVisited();
|
BlockVectorSet visited = visitor.getVisited();
|
||||||
visitor = new RecursiveVisitor(new AbstractExtentMask(editSession) {
|
visitor = new RecursiveVisitor(new LayerBrushMask(editSession, visitor, layers, adjacent), pos -> {
|
||||||
@Override
|
|
||||||
public boolean test(Extent extent, BlockVector3 vector) {
|
|
||||||
return test(vector);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean test(BlockVector3 pos) {
|
|
||||||
int depth = visitor.getDepth() + 1;
|
|
||||||
if (depth > 1) {
|
|
||||||
boolean found = false;
|
|
||||||
BlockState previous = layers[depth - 1];
|
|
||||||
BlockState previous2 = layers[depth - 2];
|
|
||||||
for (BlockVector3 dir : BreadthFirstSearch.DEFAULT_DIRECTIONS) {
|
|
||||||
mutable.setComponents(pos.getBlockX() + dir.getBlockX(), pos.getBlockY() + dir.getBlockY(), pos.getBlockZ() + dir.getBlockZ());
|
|
||||||
if (visitor.isVisited(mutable) && editSession.getBlock(mutable.getBlockX(), mutable.getBlockY(), mutable.getBlockZ()) == previous) {
|
|
||||||
mutable.setComponents(pos.getBlockX() + dir.getBlockX() * 2, pos.getBlockY() + dir.getBlockY() * 2, pos.getBlockZ() + dir.getBlockZ() * 2);
|
|
||||||
if (visitor.isVisited(mutable) && editSession.getBlock(mutable.getBlockX(), mutable.getBlockY(), mutable.getBlockZ()) == previous2) {
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!found) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return !adjacent.test(pos);
|
|
||||||
}
|
|
||||||
}, pos -> {
|
|
||||||
int depth = visitor.getDepth();
|
int depth = visitor.getDepth();
|
||||||
BlockState currentPattern = layers[depth];
|
BlockState currentPattern = layers[depth];
|
||||||
return currentPattern.apply(editSession, pos, pos);
|
return currentPattern.apply(editSession, pos, pos);
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
package com.boydti.fawe.object.brush;
|
package com.boydti.fawe.object.brush;
|
||||||
|
|
||||||
|
import com.boydti.fawe.object.brush.mask.SplatterBrushMask;
|
||||||
import com.boydti.fawe.object.collection.LocalBlockVectorSet;
|
import com.boydti.fawe.object.collection.LocalBlockVectorSet;
|
||||||
import com.boydti.fawe.object.mask.SurfaceMask;
|
import com.boydti.fawe.object.mask.SurfaceMask;
|
||||||
import com.sk89q.worldedit.EditSession;
|
import com.sk89q.worldedit.EditSession;
|
||||||
import com.sk89q.worldedit.MaxChangedBlocksException;
|
import com.sk89q.worldedit.MaxChangedBlocksException;
|
||||||
import com.sk89q.worldedit.extent.Extent;
|
|
||||||
import com.sk89q.worldedit.function.mask.AbstractExtentMask;
|
|
||||||
import com.sk89q.worldedit.function.operation.Operations;
|
import com.sk89q.worldedit.function.operation.Operations;
|
||||||
import com.sk89q.worldedit.function.pattern.Pattern;
|
import com.sk89q.worldedit.function.pattern.Pattern;
|
||||||
import com.sk89q.worldedit.function.visitor.BreadthFirstSearch;
|
import com.sk89q.worldedit.function.visitor.BreadthFirstSearch;
|
||||||
@ -13,7 +12,6 @@ import com.sk89q.worldedit.function.visitor.RecursiveVisitor;
|
|||||||
import com.sk89q.worldedit.math.BlockVector3;
|
import com.sk89q.worldedit.math.BlockVector3;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.concurrent.ThreadLocalRandom;
|
|
||||||
|
|
||||||
public class SplatterBrush extends ScatterBrush {
|
public class SplatterBrush extends ScatterBrush {
|
||||||
private final boolean solid;
|
private final boolean solid;
|
||||||
@ -36,23 +34,8 @@ public class SplatterBrush extends ScatterBrush {
|
|||||||
final int size2 = (int) (size * size);
|
final int size2 = (int) (size * size);
|
||||||
SurfaceMask surface = new SurfaceMask(editSession);
|
SurfaceMask surface = new SurfaceMask(editSession);
|
||||||
|
|
||||||
RecursiveVisitor visitor = new RecursiveVisitor(new AbstractExtentMask(editSession) {
|
RecursiveVisitor visitor = new RecursiveVisitor(new SplatterBrushMask(editSession, position, size2, surface, placed),
|
||||||
@Override
|
vector -> editSession.setBlock(vector, finalPattern), recursion);
|
||||||
public boolean test(Extent extent, BlockVector3 vector) {
|
|
||||||
return test(vector);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean test(BlockVector3 vector) {
|
|
||||||
double dist = vector.distanceSq(position);
|
|
||||||
if (dist < size2 && !placed.contains(vector) && ThreadLocalRandom.current().nextInt(5) < 2
|
|
||||||
&& surface.test(vector)) {
|
|
||||||
placed.add(vector);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}, vector -> editSession.setBlock(vector, finalPattern), recursion);
|
|
||||||
visitor.setMaxBranch(2);
|
visitor.setMaxBranch(2);
|
||||||
visitor.setDirections(Arrays.asList(BreadthFirstSearch.DIAGONAL_DIRECTIONS));
|
visitor.setDirections(Arrays.asList(BreadthFirstSearch.DIAGONAL_DIRECTIONS));
|
||||||
visitor.visit(position);
|
visitor.visit(position);
|
||||||
|
@ -1,26 +1,21 @@
|
|||||||
package com.boydti.fawe.object.brush;
|
package com.boydti.fawe.object.brush;
|
||||||
|
|
||||||
import com.boydti.fawe.object.brush.heightmap.HeightMap;
|
import com.boydti.fawe.object.brush.heightmap.HeightMap;
|
||||||
import com.boydti.fawe.util.MathMan;
|
import com.boydti.fawe.object.brush.mask.StencilBrushMask;
|
||||||
import com.sk89q.worldedit.EditSession;
|
import com.sk89q.worldedit.EditSession;
|
||||||
import com.sk89q.worldedit.MaxChangedBlocksException;
|
import com.sk89q.worldedit.MaxChangedBlocksException;
|
||||||
import com.sk89q.worldedit.extent.Extent;
|
|
||||||
import com.sk89q.worldedit.extent.clipboard.Clipboard;
|
import com.sk89q.worldedit.extent.clipboard.Clipboard;
|
||||||
import com.sk89q.worldedit.function.mask.AbstractExtentMask;
|
|
||||||
import com.sk89q.worldedit.function.mask.SolidBlockMask;
|
import com.sk89q.worldedit.function.mask.SolidBlockMask;
|
||||||
import com.sk89q.worldedit.function.operation.Operations;
|
import com.sk89q.worldedit.function.operation.Operations;
|
||||||
import com.sk89q.worldedit.function.pattern.Pattern;
|
import com.sk89q.worldedit.function.pattern.Pattern;
|
||||||
import com.sk89q.worldedit.function.visitor.BreadthFirstSearch;
|
import com.sk89q.worldedit.function.visitor.BreadthFirstSearch;
|
||||||
import com.sk89q.worldedit.function.visitor.RecursiveVisitor;
|
import com.sk89q.worldedit.function.visitor.RecursiveVisitor;
|
||||||
import com.sk89q.worldedit.math.BlockVector3;
|
import com.sk89q.worldedit.math.BlockVector3;
|
||||||
import com.sk89q.worldedit.math.MutableVector3;
|
|
||||||
import com.sk89q.worldedit.math.Vector3;
|
|
||||||
import com.sk89q.worldedit.math.transform.AffineTransform;
|
import com.sk89q.worldedit.math.transform.AffineTransform;
|
||||||
import com.sk89q.worldedit.util.Location;
|
import com.sk89q.worldedit.util.Location;
|
||||||
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.concurrent.ThreadLocalRandom;
|
|
||||||
|
|
||||||
public class StencilBrush extends HeightBrush {
|
public class StencilBrush extends HeightBrush {
|
||||||
private final boolean onlyWhite;
|
private final boolean onlyWhite;
|
||||||
@ -31,10 +26,7 @@ public class StencilBrush extends HeightBrush {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void build(EditSession editSession, BlockVector3 position, Pattern pattern, double sizeDouble) throws MaxChangedBlocksException {
|
public void build(EditSession editSession, BlockVector3 center, Pattern pattern, double sizeDouble) throws MaxChangedBlocksException {
|
||||||
final int cx = position.getBlockX();
|
|
||||||
final int cy = position.getBlockY();
|
|
||||||
final int cz = position.getBlockZ();
|
|
||||||
int size = (int) sizeDouble;
|
int size = (int) sizeDouble;
|
||||||
int size2 = (int) (sizeDouble * sizeDouble);
|
int size2 = (int) (sizeDouble * sizeDouble);
|
||||||
int maxY = editSession.getMaxY();
|
int maxY = editSession.getMaxY();
|
||||||
@ -55,42 +47,11 @@ public class StencilBrush extends HeightBrush {
|
|||||||
AffineTransform transform = new AffineTransform().rotateY((-yaw) % 360).rotateX(pitch - 90).inverse();
|
AffineTransform transform = new AffineTransform().rotateY((-yaw) % 360).rotateX(pitch - 90).inverse();
|
||||||
|
|
||||||
double scale = (yscale / sizeDouble) * (maxY + 1);
|
double scale = (yscale / sizeDouble) * (maxY + 1);
|
||||||
RecursiveVisitor visitor = new RecursiveVisitor(new AbstractExtentMask(editSession) {
|
RecursiveVisitor visitor =
|
||||||
@Override
|
new RecursiveVisitor(new StencilBrushMask(editSession, solid, center, transform, size2, map, scale, add, cutoff, maxY, pattern),
|
||||||
public boolean test(Extent extent, BlockVector3 vector) {
|
vector -> true, Integer.MAX_VALUE);
|
||||||
return test(vector);
|
|
||||||
}
|
|
||||||
|
|
||||||
private final MutableVector3 mutable = new MutableVector3();
|
|
||||||
@Override
|
|
||||||
public boolean test(BlockVector3 vector) {
|
|
||||||
if (solid.test(vector)) {
|
|
||||||
int dx = vector.getBlockX() - cx;
|
|
||||||
int dy = vector.getBlockY() - cy;
|
|
||||||
int dz = vector.getBlockZ() - cz;
|
|
||||||
|
|
||||||
Vector3 srcPos = transform.apply(mutable.setComponents(dx, dy, dz));
|
|
||||||
dx = MathMan.roundInt(srcPos.getX());
|
|
||||||
dz = MathMan.roundInt(srcPos.getZ());
|
|
||||||
|
|
||||||
int distance = dx * dx + dz * dz;
|
|
||||||
if (distance > size2 || Math.abs(dx) > 256 || Math.abs(dz) > 256) return false;
|
|
||||||
|
|
||||||
double raise = map.getHeight(dx, dz);
|
|
||||||
int val = (int) Math.ceil(raise * scale) + add;
|
|
||||||
if (val < cutoff) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (val >= 255 || ThreadLocalRandom.current().nextInt(maxY) < val) {
|
|
||||||
editSession.setBlock(vector.getBlockX(), vector.getBlockY(), vector.getBlockZ(), pattern);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}, vector -> true, Integer.MAX_VALUE);
|
|
||||||
visitor.setDirections(Arrays.asList(BreadthFirstSearch.DIAGONAL_DIRECTIONS));
|
visitor.setDirections(Arrays.asList(BreadthFirstSearch.DIAGONAL_DIRECTIONS));
|
||||||
visitor.visit(position);
|
visitor.visit(center);
|
||||||
Operations.completeBlindly(visitor);
|
Operations.completeBlindly(visitor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,107 @@
|
|||||||
|
package com.boydti.fawe.object.brush.mask;
|
||||||
|
|
||||||
|
import com.boydti.fawe.object.brush.ImageBrush;
|
||||||
|
import com.boydti.fawe.util.TextureUtil;
|
||||||
|
import com.sk89q.worldedit.EditSession;
|
||||||
|
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.MutableVector3;
|
||||||
|
import com.sk89q.worldedit.math.Vector3;
|
||||||
|
import com.sk89q.worldedit.math.transform.Transform;
|
||||||
|
import com.sk89q.worldedit.world.block.BlockType;
|
||||||
|
|
||||||
|
public class ImageBrushMask extends AbstractExtentMask {
|
||||||
|
|
||||||
|
private final MutableVector3 mutable = new MutableVector3();
|
||||||
|
private final Mask solid;
|
||||||
|
private final BlockVector3 center;
|
||||||
|
private final Transform transform;
|
||||||
|
private final double scale;
|
||||||
|
private final double centerImageX;
|
||||||
|
private final double centerImageZ;
|
||||||
|
private final int width;
|
||||||
|
private final int height;
|
||||||
|
private final ImageBrush.ColorFunction colorFunction;
|
||||||
|
private final EditSession session;
|
||||||
|
private final TextureUtil texture;
|
||||||
|
|
||||||
|
public ImageBrushMask(Mask solid,
|
||||||
|
BlockVector3 center,
|
||||||
|
Transform transform,
|
||||||
|
double scale,
|
||||||
|
double centerImageX,
|
||||||
|
double centerImageZ,
|
||||||
|
int width,
|
||||||
|
int height,
|
||||||
|
ImageBrush.ColorFunction colorFunction,
|
||||||
|
EditSession session,
|
||||||
|
TextureUtil texture) {
|
||||||
|
super(session);
|
||||||
|
this.solid = solid;
|
||||||
|
this.center = center;
|
||||||
|
this.transform = transform;
|
||||||
|
this.scale = scale;
|
||||||
|
this.centerImageX = centerImageX;
|
||||||
|
this.centerImageZ = centerImageZ;
|
||||||
|
this.width = width;
|
||||||
|
this.height = height;
|
||||||
|
this.colorFunction = colorFunction;
|
||||||
|
this.session = session;
|
||||||
|
this.texture = texture;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean test(Extent extent, BlockVector3 vector) {
|
||||||
|
return test(vector);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean test(BlockVector3 vector) {
|
||||||
|
if (solid.test(vector)) {
|
||||||
|
int dx = vector.getBlockX() - center.getBlockX();
|
||||||
|
int dy = vector.getBlockY() - center.getBlockY();
|
||||||
|
int dz = vector.getBlockZ() - center.getBlockZ();
|
||||||
|
|
||||||
|
Vector3 pos1 = transform.apply(mutable.setComponents(dx - 0.5, dy - 0.5, dz - 0.5));
|
||||||
|
int x1 = (int) (pos1.getX() * scale + centerImageX);
|
||||||
|
int z1 = (int) (pos1.getZ() * scale + centerImageZ);
|
||||||
|
|
||||||
|
Vector3 pos2 = transform.apply(mutable.setComponents(dx + 0.5, dy + 0.5, dz + 0.5));
|
||||||
|
int x2 = (int) (pos2.getX() * scale + centerImageX);
|
||||||
|
int z2 = (int) (pos2.getZ() * scale + centerImageZ);
|
||||||
|
if (x2 < x1) {
|
||||||
|
int tmp = x1;
|
||||||
|
x1 = x2;
|
||||||
|
x2 = tmp;
|
||||||
|
}
|
||||||
|
if (z2 < z1) {
|
||||||
|
int tmp = z1;
|
||||||
|
z1 = z2;
|
||||||
|
z2 = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (x1 >= width || x2 < 0 || z1 >= height || z2 < 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int color = colorFunction.call(x1, z1, x2, z2, session, vector);
|
||||||
|
if (color != 0) {
|
||||||
|
BlockType block = texture.getNearestBlock(color);
|
||||||
|
if (block != null) {
|
||||||
|
session.setBlock(vector, block.getDefaultState());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mask copy() {
|
||||||
|
return new ImageBrushMask(solid.copy(), center.toImmutable(), transform, scale, centerImageX, centerImageZ, width, height, colorFunction,
|
||||||
|
session, texture);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,66 @@
|
|||||||
|
package com.boydti.fawe.object.brush.mask;
|
||||||
|
|
||||||
|
import com.sk89q.worldedit.EditSession;
|
||||||
|
import com.sk89q.worldedit.extent.Extent;
|
||||||
|
import com.sk89q.worldedit.function.mask.AbstractExtentMask;
|
||||||
|
import com.sk89q.worldedit.function.mask.Mask;
|
||||||
|
import com.sk89q.worldedit.function.visitor.BreadthFirstSearch;
|
||||||
|
import com.sk89q.worldedit.function.visitor.RecursiveVisitor;
|
||||||
|
import com.sk89q.worldedit.math.BlockVector3;
|
||||||
|
import com.sk89q.worldedit.math.MutableBlockVector3;
|
||||||
|
import com.sk89q.worldedit.world.block.BlockState;
|
||||||
|
|
||||||
|
public class LayerBrushMask extends AbstractExtentMask {
|
||||||
|
|
||||||
|
private final EditSession editSession;
|
||||||
|
private final RecursiveVisitor visitor;
|
||||||
|
private final BlockState[] layers;
|
||||||
|
private final MutableBlockVector3 mutable = new MutableBlockVector3();
|
||||||
|
private final Mask adjacent;
|
||||||
|
|
||||||
|
public LayerBrushMask(EditSession editSession, RecursiveVisitor visitor, BlockState[] layers, Mask adjacent) {
|
||||||
|
super(editSession);
|
||||||
|
this.editSession = editSession;
|
||||||
|
this.visitor = visitor;
|
||||||
|
this.layers = layers;
|
||||||
|
this.adjacent = adjacent;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean test(Extent extent, BlockVector3 vector) {
|
||||||
|
return test(vector);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean test(BlockVector3 pos) {
|
||||||
|
int depth = visitor.getDepth() + 1;
|
||||||
|
if (depth > 1) {
|
||||||
|
boolean found = false;
|
||||||
|
BlockState previous = layers[depth - 1];
|
||||||
|
BlockState previous2 = layers[depth - 2];
|
||||||
|
for (BlockVector3 dir : BreadthFirstSearch.DEFAULT_DIRECTIONS) {
|
||||||
|
mutable.setComponents(pos.getBlockX() + dir.getBlockX(), pos.getBlockY() + dir.getBlockY(), pos.getBlockZ() + dir.getBlockZ());
|
||||||
|
if (visitor.isVisited(mutable) && editSession.getBlock(mutable.getBlockX(), mutable.getBlockY(), mutable.getBlockZ()) == previous) {
|
||||||
|
mutable.setComponents(pos.getBlockX() + dir.getBlockX() * 2, pos.getBlockY() + dir.getBlockY() * 2,
|
||||||
|
pos.getBlockZ() + dir.getBlockZ() * 2);
|
||||||
|
if (visitor.isVisited(mutable)
|
||||||
|
&& editSession.getBlock(mutable.getBlockX(), mutable.getBlockY(), mutable.getBlockZ()) == previous2) {
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return !adjacent.test(pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mask copy() {
|
||||||
|
return new LayerBrushMask(editSession, visitor, layers.clone(), adjacent.copy());
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,49 @@
|
|||||||
|
package com.boydti.fawe.object.brush.mask;
|
||||||
|
|
||||||
|
import com.boydti.fawe.object.collection.LocalBlockVectorSet;
|
||||||
|
import com.sk89q.worldedit.EditSession;
|
||||||
|
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 java.util.concurrent.ThreadLocalRandom;
|
||||||
|
|
||||||
|
public class SplatterBrushMask extends AbstractExtentMask {
|
||||||
|
|
||||||
|
private final BlockVector3 position;
|
||||||
|
private final int size2;
|
||||||
|
private final Mask surface;
|
||||||
|
private final LocalBlockVectorSet placed;
|
||||||
|
|
||||||
|
public SplatterBrushMask(EditSession editSession, BlockVector3 position, int size2, Mask surface, LocalBlockVectorSet placed) {
|
||||||
|
super(editSession);
|
||||||
|
this.position = position;
|
||||||
|
this.size2 = size2;
|
||||||
|
this.surface = surface;
|
||||||
|
this.placed = placed;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean test(Extent extent, BlockVector3 vector) {
|
||||||
|
return test(vector);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean test(BlockVector3 vector) {
|
||||||
|
double dist = vector.distanceSq(position);
|
||||||
|
synchronized (placed) {
|
||||||
|
if (dist < size2 && !placed.contains(vector) && ThreadLocalRandom.current().nextInt(5) < 2 && surface.test(vector)) {
|
||||||
|
placed.add(vector);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mask copy() {
|
||||||
|
// There should not be multiple instances to be thread safe
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,95 @@
|
|||||||
|
package com.boydti.fawe.object.brush.mask;
|
||||||
|
|
||||||
|
import com.boydti.fawe.object.brush.heightmap.HeightMap;
|
||||||
|
import com.boydti.fawe.util.MathMan;
|
||||||
|
import com.sk89q.worldedit.EditSession;
|
||||||
|
import com.sk89q.worldedit.extent.Extent;
|
||||||
|
import com.sk89q.worldedit.function.mask.AbstractExtentMask;
|
||||||
|
import com.sk89q.worldedit.function.mask.Mask;
|
||||||
|
import com.sk89q.worldedit.function.pattern.Pattern;
|
||||||
|
import com.sk89q.worldedit.math.BlockVector3;
|
||||||
|
import com.sk89q.worldedit.math.MutableVector3;
|
||||||
|
import com.sk89q.worldedit.math.Vector3;
|
||||||
|
import com.sk89q.worldedit.math.transform.Transform;
|
||||||
|
|
||||||
|
import java.util.concurrent.ThreadLocalRandom;
|
||||||
|
|
||||||
|
public class StencilBrushMask extends AbstractExtentMask {
|
||||||
|
|
||||||
|
private final MutableVector3 mutable = new MutableVector3();
|
||||||
|
private final EditSession editSession;
|
||||||
|
private final Mask solid;
|
||||||
|
private final BlockVector3 center;
|
||||||
|
private final Transform transform;
|
||||||
|
private final int size2;
|
||||||
|
private final HeightMap map;
|
||||||
|
private final double scale;
|
||||||
|
private final int add;
|
||||||
|
private final int cutoff;
|
||||||
|
private final int maxY;
|
||||||
|
private final Pattern pattern;
|
||||||
|
|
||||||
|
public StencilBrushMask(EditSession editSession,
|
||||||
|
Mask solid,
|
||||||
|
BlockVector3 center,
|
||||||
|
Transform transform,
|
||||||
|
int size2,
|
||||||
|
HeightMap map,
|
||||||
|
double scale,
|
||||||
|
int add,
|
||||||
|
int cutoff,
|
||||||
|
int maxY,
|
||||||
|
Pattern pattern) {
|
||||||
|
super(editSession);
|
||||||
|
this.editSession = editSession;
|
||||||
|
this.solid = solid;
|
||||||
|
this.center = center;
|
||||||
|
this.transform = transform;
|
||||||
|
this.size2 = size2;
|
||||||
|
this.map = map;
|
||||||
|
this.scale = scale;
|
||||||
|
this.add = add;
|
||||||
|
this.cutoff = cutoff;
|
||||||
|
this.maxY = maxY;
|
||||||
|
this.pattern = pattern;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean test(Extent extent, BlockVector3 vector) {
|
||||||
|
return test(vector);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean test(BlockVector3 vector) {
|
||||||
|
if (solid.test(vector)) {
|
||||||
|
int dx = vector.getBlockX() - center.getBlockX();
|
||||||
|
int dy = vector.getBlockY() - center.getBlockY();
|
||||||
|
int dz = vector.getBlockZ() - center.getBlockZ();
|
||||||
|
|
||||||
|
Vector3 srcPos = transform.apply(mutable.setComponents(dx, dy, dz));
|
||||||
|
dx = MathMan.roundInt(srcPos.getX());
|
||||||
|
dz = MathMan.roundInt(srcPos.getZ());
|
||||||
|
|
||||||
|
int distance = dx * dx + dz * dz;
|
||||||
|
if (distance > size2 || Math.abs(dx) > 256 || Math.abs(dz) > 256) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
double raise = map.getHeight(dx, dz);
|
||||||
|
int val = (int) Math.ceil(raise * scale) + add;
|
||||||
|
if (val < cutoff) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (val >= 255 || ThreadLocalRandom.current().nextInt(maxY) < val) {
|
||||||
|
editSession.setBlock(vector.getBlockX(), vector.getBlockY(), vector.getBlockZ(), pattern);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mask copy() {
|
||||||
|
return new StencilBrushMask(editSession, solid.copy(), center.toImmutable(), transform, size2, map, scale, add, cutoff, maxY, pattern);
|
||||||
|
}
|
||||||
|
}
|
@ -30,4 +30,9 @@ public class AbstractDelegateMask extends AbstractMask {
|
|||||||
return mask.toMask2D();
|
return mask.toMask2D();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mask copy() {
|
||||||
|
return new AbstractDelegateMask(mask.copy());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package com.boydti.fawe.object.mask;
|
package com.boydti.fawe.object.mask;
|
||||||
|
|
||||||
import com.sk89q.worldedit.extent.Extent;
|
|
||||||
import com.sk89q.worldedit.function.mask.AbstractMask;
|
import com.sk89q.worldedit.function.mask.AbstractMask;
|
||||||
import com.sk89q.worldedit.function.mask.Mask;
|
import com.sk89q.worldedit.function.mask.Mask;
|
||||||
import com.sk89q.worldedit.math.BlockVector3;
|
import com.sk89q.worldedit.math.BlockVector3;
|
||||||
@ -53,4 +52,9 @@ public class AdjacentAnyMask extends AbstractMask implements ResettableMask {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mask copy() {
|
||||||
|
return new AdjacentAnyMask(mask.copy());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@ public class AdjacentMask extends AbstractMask {
|
|||||||
private final int min;
|
private final int min;
|
||||||
private final int max;
|
private final int max;
|
||||||
private final Mask mask;
|
private final Mask mask;
|
||||||
private MutableBlockVector3 vector;
|
private final MutableBlockVector3 vector;
|
||||||
|
|
||||||
public AdjacentMask(Mask mask, int requiredMin, int requiredMax) {
|
public AdjacentMask(Mask mask, int requiredMin, int requiredMax) {
|
||||||
this.mask = mask;
|
this.mask = mask;
|
||||||
@ -60,4 +60,9 @@ public class AdjacentMask extends AbstractMask {
|
|||||||
vector.mutZ(z);
|
vector.mutZ(z);
|
||||||
return count >= min && count <= max;
|
return count >= min && count <= max;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mask copy() {
|
||||||
|
return new AdjacentMask(mask.copy(), min, max);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
package com.boydti.fawe.function.mask;
|
package com.boydti.fawe.object.mask;
|
||||||
|
|
||||||
import com.sk89q.worldedit.extent.Extent;
|
import com.sk89q.worldedit.extent.Extent;
|
||||||
import com.sk89q.worldedit.function.mask.BlockMask;
|
import com.sk89q.worldedit.function.mask.BlockMask;
|
||||||
|
import com.sk89q.worldedit.function.mask.Mask;
|
||||||
|
|
||||||
public class AirMask extends BlockMask {
|
public class AirMask extends BlockMask {
|
||||||
|
|
||||||
@ -10,4 +11,9 @@ public class AirMask extends BlockMask {
|
|||||||
add(state -> state.getMaterial().isAir());
|
add(state -> state.getMaterial().isAir());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mask copy() {
|
||||||
|
return new AirMask(getExtent());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -1,6 +1,7 @@
|
|||||||
package com.boydti.fawe.object.mask;
|
package com.boydti.fawe.object.mask;
|
||||||
|
|
||||||
import com.sk89q.worldedit.extent.Extent;
|
import com.sk89q.worldedit.extent.Extent;
|
||||||
|
import com.sk89q.worldedit.function.mask.Mask;
|
||||||
import com.sk89q.worldedit.function.mask.SolidBlockMask;
|
import com.sk89q.worldedit.function.mask.SolidBlockMask;
|
||||||
import com.sk89q.worldedit.math.BlockVector3;
|
import com.sk89q.worldedit.math.BlockVector3;
|
||||||
import com.sk89q.worldedit.math.MutableBlockVector3;
|
import com.sk89q.worldedit.math.MutableBlockVector3;
|
||||||
@ -44,7 +45,6 @@ public class AngleMask extends SolidBlockMask implements ResettableMask {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected transient int cacheCenX;
|
|
||||||
protected transient int cacheCenZ;
|
protected transient int cacheCenZ;
|
||||||
protected transient int cacheBotX = Integer.MIN_VALUE;
|
protected transient int cacheBotX = Integer.MIN_VALUE;
|
||||||
protected transient int cacheBotZ = Integer.MIN_VALUE;
|
protected transient int cacheBotZ = Integer.MIN_VALUE;
|
||||||
@ -173,4 +173,9 @@ public class AngleMask extends SolidBlockMask implements ResettableMask {
|
|||||||
return testSlope(getExtent(), x, y, z);
|
return testSlope(getExtent(), x, y, z);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mask copy() {
|
||||||
|
return new AngleMask(getExtent(), min, max, overlay, distance);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -65,4 +65,9 @@ public class CachedMask extends AbstractDelegateMask implements ResettableMask {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mask copy() {
|
||||||
|
return new CachedMask(getMask().copy());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ package com.boydti.fawe.object.mask;
|
|||||||
|
|
||||||
import com.sk89q.worldedit.extent.Extent;
|
import com.sk89q.worldedit.extent.Extent;
|
||||||
import com.sk89q.worldedit.function.mask.AbstractExtentMask;
|
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.BlockVector3;
|
||||||
|
|
||||||
public class DataMask extends AbstractExtentMask implements ResettableMask {
|
public class DataMask extends AbstractExtentMask implements ResettableMask {
|
||||||
@ -37,4 +38,9 @@ public class DataMask extends AbstractExtentMask implements ResettableMask {
|
|||||||
this.data = -1;
|
this.data = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mask copy() {
|
||||||
|
return new DataMask(getExtent());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package com.boydti.fawe.object.mask;
|
package com.boydti.fawe.object.mask;
|
||||||
|
|
||||||
import com.sk89q.worldedit.extent.Extent;
|
import com.sk89q.worldedit.extent.Extent;
|
||||||
|
import com.sk89q.worldedit.function.mask.Mask;
|
||||||
|
|
||||||
public class ExtremaMask extends AngleMask {
|
public class ExtremaMask extends AngleMask {
|
||||||
public ExtremaMask(Extent extent, double min, double max, boolean overlay, int distance) {
|
public ExtremaMask(Extent extent, double min, double max, boolean overlay, int distance) {
|
||||||
@ -62,4 +63,9 @@ public class ExtremaMask extends AngleMask {
|
|||||||
}
|
}
|
||||||
return (lastHeight1 - base) + (lastHeight2 - base);
|
return (lastHeight1 - base) + (lastHeight2 - base);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mask copy() {
|
||||||
|
return new ExtremaMask(getExtent(), min, max, overlay, distance);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ package com.boydti.fawe.object.mask;
|
|||||||
|
|
||||||
import com.sk89q.worldedit.extent.Extent;
|
import com.sk89q.worldedit.extent.Extent;
|
||||||
import com.sk89q.worldedit.function.mask.AbstractExtentMask;
|
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.BlockVector3;
|
||||||
|
|
||||||
public class IdDataMask extends AbstractExtentMask implements ResettableMask {
|
public class IdDataMask extends AbstractExtentMask implements ResettableMask {
|
||||||
@ -31,4 +32,9 @@ public class IdDataMask extends AbstractExtentMask implements ResettableMask {
|
|||||||
this.combined = -1;
|
this.combined = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mask copy() {
|
||||||
|
return new IdDataMask(getExtent());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ package com.boydti.fawe.object.mask;
|
|||||||
|
|
||||||
import com.sk89q.worldedit.extent.Extent;
|
import com.sk89q.worldedit.extent.Extent;
|
||||||
import com.sk89q.worldedit.function.mask.AbstractExtentMask;
|
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.BlockVector3;
|
||||||
|
|
||||||
public class IdMask extends AbstractExtentMask implements ResettableMask {
|
public class IdMask extends AbstractExtentMask implements ResettableMask {
|
||||||
@ -32,4 +33,9 @@ public class IdMask extends AbstractExtentMask implements ResettableMask {
|
|||||||
this.id = -1;
|
this.id = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mask copy() {
|
||||||
|
return new IdMask(getExtent());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
package com.boydti.fawe.function.mask;
|
package com.boydti.fawe.object.mask;
|
||||||
|
|
||||||
import com.sk89q.worldedit.extent.Extent;
|
import com.sk89q.worldedit.extent.Extent;
|
||||||
import com.sk89q.worldedit.function.mask.BlockMask;
|
import com.sk89q.worldedit.function.mask.BlockMask;
|
||||||
|
import com.sk89q.worldedit.function.mask.Mask;
|
||||||
|
|
||||||
public class LiquidMask extends BlockMask {
|
public class LiquidMask extends BlockMask {
|
||||||
|
|
||||||
@ -10,4 +11,9 @@ public class LiquidMask extends BlockMask {
|
|||||||
add(state -> state.getMaterial().isLiquid());
|
add(state -> state.getMaterial().isLiquid());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mask copy() {
|
||||||
|
return new LiquidMask(getExtent());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -1,6 +1,7 @@
|
|||||||
package com.boydti.fawe.object.mask;
|
package com.boydti.fawe.object.mask;
|
||||||
|
|
||||||
import com.sk89q.worldedit.function.mask.AbstractMask;
|
import com.sk89q.worldedit.function.mask.AbstractMask;
|
||||||
|
import com.sk89q.worldedit.function.mask.Mask;
|
||||||
import com.sk89q.worldedit.math.BlockVector3;
|
import com.sk89q.worldedit.math.BlockVector3;
|
||||||
|
|
||||||
public class PlaneMask extends AbstractMask implements ResettableMask {
|
public class PlaneMask extends AbstractMask implements ResettableMask {
|
||||||
@ -55,4 +56,9 @@ public class PlaneMask extends AbstractMask implements ResettableMask {
|
|||||||
mode = -1;
|
mode = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mask copy() {
|
||||||
|
return new PlaneMask();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package com.boydti.fawe.object.mask;
|
package com.boydti.fawe.object.mask;
|
||||||
|
|
||||||
import com.sk89q.worldedit.extent.Extent;
|
import com.sk89q.worldedit.extent.Extent;
|
||||||
|
import com.sk89q.worldedit.function.mask.Mask;
|
||||||
|
|
||||||
public class ROCAngleMask extends AngleMask {
|
public class ROCAngleMask extends AngleMask {
|
||||||
|
|
||||||
@ -31,4 +32,9 @@ public class ROCAngleMask extends AngleMask {
|
|||||||
|
|
||||||
return lastValue = slope >= min && slope <= max;
|
return lastValue = slope >= min && slope <= max;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mask copy() {
|
||||||
|
return new ROCAngleMask(getExtent(), min, max, overlay, distance);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package com.boydti.fawe.object.mask;
|
package com.boydti.fawe.object.mask;
|
||||||
|
|
||||||
import com.sk89q.worldedit.function.mask.AbstractMask;
|
import com.sk89q.worldedit.function.mask.AbstractMask;
|
||||||
|
import com.sk89q.worldedit.function.mask.Mask;
|
||||||
import com.sk89q.worldedit.math.BlockVector3;
|
import com.sk89q.worldedit.math.BlockVector3;
|
||||||
|
|
||||||
public class RadiusMask extends AbstractMask implements ResettableMask {
|
public class RadiusMask extends AbstractMask implements ResettableMask {
|
||||||
@ -13,6 +14,11 @@ public class RadiusMask extends AbstractMask implements ResettableMask {
|
|||||||
this.maxSqr = max * max;
|
this.maxSqr = max * max;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private RadiusMask(Integer minSqr, Integer maxSqr) {
|
||||||
|
this.minSqr = minSqr;
|
||||||
|
this.maxSqr = maxSqr;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void reset() {
|
public void reset() {
|
||||||
pos = null;
|
pos = null;
|
||||||
@ -41,4 +47,9 @@ public class RadiusMask extends AbstractMask implements ResettableMask {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mask copy() {
|
||||||
|
return new RadiusMask(minSqr, maxSqr);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package com.boydti.fawe.object.mask;
|
package com.boydti.fawe.object.mask;
|
||||||
|
|
||||||
import com.sk89q.worldedit.function.mask.AbstractMask;
|
import com.sk89q.worldedit.function.mask.AbstractMask;
|
||||||
|
import com.sk89q.worldedit.function.mask.Mask;
|
||||||
import com.sk89q.worldedit.math.BlockVector3;
|
import com.sk89q.worldedit.math.BlockVector3;
|
||||||
|
|
||||||
import java.util.SplittableRandom;
|
import java.util.SplittableRandom;
|
||||||
@ -23,4 +24,9 @@ public class RandomMask extends AbstractMask implements ResettableMask {
|
|||||||
public void reset() {
|
public void reset() {
|
||||||
random = new SplittableRandom();
|
random = new SplittableRandom();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mask copy() {
|
||||||
|
return new RandomMask(threshold);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ package com.boydti.fawe.object.mask;
|
|||||||
|
|
||||||
import com.boydti.fawe.object.random.SimplexNoise;
|
import com.boydti.fawe.object.random.SimplexNoise;
|
||||||
import com.sk89q.worldedit.function.mask.AbstractMask;
|
import com.sk89q.worldedit.function.mask.AbstractMask;
|
||||||
|
import com.sk89q.worldedit.function.mask.Mask;
|
||||||
import com.sk89q.worldedit.math.BlockVector3;
|
import com.sk89q.worldedit.math.BlockVector3;
|
||||||
|
|
||||||
public class SimplexMask extends AbstractMask {
|
public class SimplexMask extends AbstractMask {
|
||||||
@ -18,4 +19,10 @@ public class SimplexMask extends AbstractMask {
|
|||||||
double value = SimplexNoise.noise(vector.getBlockX() * scale, vector.getBlockY() * scale, vector.getBlockZ() * scale);
|
double value = SimplexNoise.noise(vector.getBlockX() * scale, vector.getBlockY() * scale, vector.getBlockZ() * scale);
|
||||||
return value >= min && value <= max;
|
return value >= min && value <= max;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mask copy() {
|
||||||
|
// The mask is not mutable. There is no need to clone it.
|
||||||
|
return this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,10 +3,12 @@ package com.boydti.fawe.object.mask;
|
|||||||
import com.sk89q.worldedit.extent.Extent;
|
import com.sk89q.worldedit.extent.Extent;
|
||||||
import com.sk89q.worldedit.function.mask.AbstractExtentMask;
|
import com.sk89q.worldedit.function.mask.AbstractExtentMask;
|
||||||
import com.sk89q.worldedit.function.mask.BlockMaskBuilder;
|
import com.sk89q.worldedit.function.mask.BlockMaskBuilder;
|
||||||
|
import com.sk89q.worldedit.function.mask.Mask;
|
||||||
import com.sk89q.worldedit.math.BlockVector3;
|
import com.sk89q.worldedit.math.BlockVector3;
|
||||||
import com.sk89q.worldedit.world.block.BlockTypes;
|
import com.sk89q.worldedit.world.block.BlockTypes;
|
||||||
|
|
||||||
public class SurfaceMask extends AdjacentAnyMask {
|
public class SurfaceMask extends AdjacentAnyMask {
|
||||||
|
|
||||||
public SurfaceMask(Extent extent) {
|
public SurfaceMask(Extent extent) {
|
||||||
super(getMask(extent));
|
super(getMask(extent));
|
||||||
}
|
}
|
||||||
@ -22,4 +24,10 @@ public class SurfaceMask extends AdjacentAnyMask {
|
|||||||
public boolean test(BlockVector3 v) {
|
public boolean test(BlockVector3 v) {
|
||||||
return !getParentMask().test(v.getBlockX(), v.getBlockY(), v.getBlockZ()) && super.test(v);
|
return !getParentMask().test(v.getBlockX(), v.getBlockY(), v.getBlockZ()) && super.test(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mask copy() {
|
||||||
|
// The mask is not mutable. There is no need to clone it.
|
||||||
|
return this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,4 +48,9 @@ public class WallMask extends AbstractMask {
|
|||||||
v.mutZ(z);
|
v.mutZ(z);
|
||||||
return count >= min && count <= max;
|
return count >= min && count <= max;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mask copy() {
|
||||||
|
return new WallMask(mask.copy(), min, max);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
package com.boydti.fawe.function.mask;
|
package com.boydti.fawe.object.mask;
|
||||||
|
|
||||||
import com.boydti.fawe.object.mask.ResettableMask;
|
|
||||||
import com.sk89q.worldedit.extent.Extent;
|
import com.sk89q.worldedit.extent.Extent;
|
||||||
import com.sk89q.worldedit.function.mask.AbstractMask;
|
import com.sk89q.worldedit.function.mask.AbstractMask;
|
||||||
|
import com.sk89q.worldedit.function.mask.Mask;
|
||||||
import com.sk89q.worldedit.math.BlockVector3;
|
import com.sk89q.worldedit.math.BlockVector3;
|
||||||
|
|
||||||
public class XAxisMask extends AbstractMask implements ResettableMask {
|
public class XAxisMask extends AbstractMask implements ResettableMask {
|
||||||
@ -26,4 +26,9 @@ public class XAxisMask extends AbstractMask implements ResettableMask {
|
|||||||
this.layer = -1;
|
this.layer = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mask copy() {
|
||||||
|
return new XAxisMask(null);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -1,8 +1,8 @@
|
|||||||
package com.boydti.fawe.function.mask;
|
package com.boydti.fawe.object.mask;
|
||||||
|
|
||||||
import com.boydti.fawe.object.mask.ResettableMask;
|
|
||||||
import com.sk89q.worldedit.extent.Extent;
|
import com.sk89q.worldedit.extent.Extent;
|
||||||
import com.sk89q.worldedit.function.mask.AbstractMask;
|
import com.sk89q.worldedit.function.mask.AbstractMask;
|
||||||
|
import com.sk89q.worldedit.function.mask.Mask;
|
||||||
import com.sk89q.worldedit.math.BlockVector3;
|
import com.sk89q.worldedit.math.BlockVector3;
|
||||||
|
|
||||||
public class YAxisMask extends AbstractMask implements ResettableMask {
|
public class YAxisMask extends AbstractMask implements ResettableMask {
|
||||||
@ -25,4 +25,9 @@ public class YAxisMask extends AbstractMask implements ResettableMask {
|
|||||||
this.layer = -1;
|
this.layer = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mask copy() {
|
||||||
|
return new YAxisMask(null);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -1,7 +1,7 @@
|
|||||||
package com.boydti.fawe.function.mask;
|
package com.boydti.fawe.object.mask;
|
||||||
|
|
||||||
import com.boydti.fawe.object.mask.ResettableMask;
|
|
||||||
import com.sk89q.worldedit.function.mask.AbstractMask;
|
import com.sk89q.worldedit.function.mask.AbstractMask;
|
||||||
|
import com.sk89q.worldedit.function.mask.Mask;
|
||||||
import com.sk89q.worldedit.math.BlockVector3;
|
import com.sk89q.worldedit.math.BlockVector3;
|
||||||
|
|
||||||
public class ZAxisMask extends AbstractMask implements ResettableMask {
|
public class ZAxisMask extends AbstractMask implements ResettableMask {
|
||||||
@ -24,4 +24,9 @@ public class ZAxisMask extends AbstractMask implements ResettableMask {
|
|||||||
this.layer = -1;
|
this.layer = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mask copy() {
|
||||||
|
return new ZAxisMask();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -71,6 +71,7 @@ import com.sk89q.worldedit.function.mask.NoiseFilter2D;
|
|||||||
import com.sk89q.worldedit.function.mask.RegionMask;
|
import com.sk89q.worldedit.function.mask.RegionMask;
|
||||||
import com.sk89q.worldedit.function.mask.SingleBlockTypeMask;
|
import com.sk89q.worldedit.function.mask.SingleBlockTypeMask;
|
||||||
import com.sk89q.worldedit.function.mask.SolidBlockMask;
|
import com.sk89q.worldedit.function.mask.SolidBlockMask;
|
||||||
|
import com.sk89q.worldedit.function.mask.WallMakeMask;
|
||||||
import com.sk89q.worldedit.function.operation.ChangeSetExecutor;
|
import com.sk89q.worldedit.function.operation.ChangeSetExecutor;
|
||||||
import com.sk89q.worldedit.function.operation.ForwardExtentCopy;
|
import com.sk89q.worldedit.function.operation.ForwardExtentCopy;
|
||||||
import com.sk89q.worldedit.function.operation.Operations;
|
import com.sk89q.worldedit.function.operation.Operations;
|
||||||
@ -1406,15 +1407,7 @@ public class EditSession extends PassthroughExtent implements AutoCloseable {
|
|||||||
if (region instanceof CuboidRegion) {
|
if (region instanceof CuboidRegion) {
|
||||||
return makeCuboidWalls(region, pattern);
|
return makeCuboidWalls(region, pattern);
|
||||||
} else {
|
} else {
|
||||||
replaceBlocks(region, new Mask() {
|
replaceBlocks(region, new WallMakeMask(region), pattern);
|
||||||
@Override
|
|
||||||
public boolean test(BlockVector3 position) {
|
|
||||||
int x = position.getBlockX();
|
|
||||||
int z = position.getBlockZ();
|
|
||||||
return !region.contains(x, z + 1) || !region.contains(x, z - 1) || !region
|
|
||||||
.contains(x + 1, z) || !region.contains(x - 1, z);
|
|
||||||
}
|
|
||||||
}, pattern);
|
|
||||||
}
|
}
|
||||||
return changes;
|
return changes;
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package com.sk89q.worldedit.extension.factory.parser.mask;
|
package com.sk89q.worldedit.extension.factory.parser.mask;
|
||||||
|
|
||||||
import com.boydti.fawe.function.mask.LiquidMask;
|
import com.boydti.fawe.object.mask.LiquidMask;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.sk89q.worldedit.WorldEdit;
|
import com.sk89q.worldedit.WorldEdit;
|
||||||
import com.sk89q.worldedit.extension.input.ParserContext;
|
import com.sk89q.worldedit.extension.input.ParserContext;
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
|
|
||||||
package com.sk89q.worldedit.extension.factory.parser.mask;
|
package com.sk89q.worldedit.extension.factory.parser.mask;
|
||||||
|
|
||||||
import com.boydti.fawe.function.mask.XAxisMask;
|
import com.boydti.fawe.object.mask.XAxisMask;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.sk89q.worldedit.WorldEdit;
|
import com.sk89q.worldedit.WorldEdit;
|
||||||
import com.sk89q.worldedit.extension.input.ParserContext;
|
import com.sk89q.worldedit.extension.input.ParserContext;
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
|
|
||||||
package com.sk89q.worldedit.extension.factory.parser.mask;
|
package com.sk89q.worldedit.extension.factory.parser.mask;
|
||||||
|
|
||||||
import com.boydti.fawe.function.mask.YAxisMask;
|
import com.boydti.fawe.object.mask.YAxisMask;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.sk89q.worldedit.WorldEdit;
|
import com.sk89q.worldedit.WorldEdit;
|
||||||
import com.sk89q.worldedit.extension.input.ParserContext;
|
import com.sk89q.worldedit.extension.input.ParserContext;
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
|
|
||||||
package com.sk89q.worldedit.extension.factory.parser.mask;
|
package com.sk89q.worldedit.extension.factory.parser.mask;
|
||||||
|
|
||||||
import com.boydti.fawe.function.mask.ZAxisMask;
|
import com.boydti.fawe.object.mask.ZAxisMask;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.sk89q.worldedit.WorldEdit;
|
import com.sk89q.worldedit.WorldEdit;
|
||||||
import com.sk89q.worldedit.extension.input.ParserContext;
|
import com.sk89q.worldedit.extension.input.ParserContext;
|
||||||
|
@ -44,7 +44,7 @@ import static com.google.common.base.Preconditions.checkNotNull;
|
|||||||
public class MaskingExtent extends AbstractDelegateExtent implements IBatchProcessor, Filter {
|
public class MaskingExtent extends AbstractDelegateExtent implements IBatchProcessor, Filter {
|
||||||
|
|
||||||
private Mask mask;
|
private Mask mask;
|
||||||
private LoadingCache<Long, ChunkFilterBlock> threadIdToFilter = FaweCache.IMP.createCache(() -> new CharFilterBlock(getExtent()));
|
private final LoadingCache<Long, ChunkFilterBlock> threadIdToFilter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new instance.
|
* Create a new instance.
|
||||||
@ -56,6 +56,14 @@ public class MaskingExtent extends AbstractDelegateExtent implements IBatchProce
|
|||||||
super(extent);
|
super(extent);
|
||||||
checkNotNull(mask);
|
checkNotNull(mask);
|
||||||
this.mask = mask;
|
this.mask = mask;
|
||||||
|
this.threadIdToFilter = FaweCache.IMP.createCache(() -> new CharFilterBlock(getExtent()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private MaskingExtent(Extent extent, Mask mask, LoadingCache<Long, ChunkFilterBlock> threadIdToFilter) {
|
||||||
|
super(extent);
|
||||||
|
checkNotNull(mask);
|
||||||
|
this.mask = mask;
|
||||||
|
this.threadIdToFilter = threadIdToFilter;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -64,7 +72,7 @@ public class MaskingExtent extends AbstractDelegateExtent implements IBatchProce
|
|||||||
* @return the mask
|
* @return the mask
|
||||||
*/
|
*/
|
||||||
public Mask getMask() {
|
public Mask getMask() {
|
||||||
return mask;
|
return this.mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -79,38 +87,40 @@ public class MaskingExtent extends AbstractDelegateExtent implements IBatchProce
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <B extends BlockStateHolder<B>> boolean setBlock(BlockVector3 location, B block) throws WorldEditException {
|
public <B extends BlockStateHolder<B>> boolean setBlock(BlockVector3 location, B block) throws WorldEditException {
|
||||||
return mask.test(location) && super.setBlock(location, block);
|
return this.mask.test(location) && super.setBlock(location, block);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean setBiome(BlockVector2 position, BiomeType biome) {
|
public boolean setBiome(BlockVector2 position, BiomeType biome) {
|
||||||
return mask.test(position.toBlockVector3()) && super.setBiome(position, biome);
|
return this.mask.test(position.toBlockVector3()) && super.setBiome(position, biome);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean setBiome(int x, int y, int z, BiomeType biome) {
|
public boolean setBiome(int x, int y, int z, BiomeType biome) {
|
||||||
return mask.test(BlockVector3.at(x, y, z)) && super.setBiome(x, y, z, biome);
|
return this.mask.test(BlockVector3.at(x, y, z)) && super.setBiome(x, y, z, biome);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IChunkSet processSet(IChunk chunk, IChunkGet get, IChunkSet set) {
|
public IChunkSet processSet(final IChunk chunk, final IChunkGet get, final IChunkSet set) {
|
||||||
ChunkFilterBlock filter = threadIdToFilter.getUnchecked(Thread.currentThread().getId());
|
final ChunkFilterBlock filter = threadIdToFilter.getUnchecked(Thread.currentThread().getId());
|
||||||
return filter.filter(chunk, get, set, this);
|
return filter.filter(chunk, get, set, MaskingExtent.this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void applyBlock(FilterBlock block) {
|
public void applyBlock(final FilterBlock block) {
|
||||||
//TODO: Find a way to make masking thread safe without having to synchonise the whole extent
|
if (!this.mask.test(block)) {
|
||||||
synchronized (this) {
|
|
||||||
if (!mask.test(block)) {
|
|
||||||
block.setOrdinal(0);
|
block.setOrdinal(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Extent construct(Extent child) {
|
public Extent construct(Extent child) {
|
||||||
if (child == getExtent()) return this;
|
if (child == getExtent()) return this;
|
||||||
return new MaskingExtent(child, mask);
|
return new MaskingExtent(child, this.mask.copy(), this.threadIdToFilter);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Filter fork() {
|
||||||
|
return new MaskingExtent(getExtent(), this.mask.copy(), this.threadIdToFilter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,9 +19,20 @@
|
|||||||
|
|
||||||
package com.sk89q.worldedit.function.mask;
|
package com.sk89q.worldedit.function.mask;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A base class of {@link Mask} that all masks should inherit from.
|
* A base class of {@link Mask} that all masks should inherit from.
|
||||||
*/
|
*/
|
||||||
public abstract class AbstractMask implements Mask {
|
public abstract class AbstractMask implements Mask {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Nullable
|
||||||
|
public Mask copy() {
|
||||||
|
try {
|
||||||
|
return (Mask) super.clone();
|
||||||
|
} catch (CloneNotSupportedException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -102,4 +102,9 @@ public class BiomeMask extends AbstractMask {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mask copy() {
|
||||||
|
return new BiomeMask(extent, new HashSet<>(biomes));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -98,4 +98,9 @@ public class BiomeMask2D extends AbstractMask2D {
|
|||||||
return biomes.contains(biome);
|
return biomes.contains(biome);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mask2D copy2D() {
|
||||||
|
return new BiomeMask2D(extent, new HashSet<>(biomes));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -56,4 +56,9 @@ public class BlockCategoryMask extends AbstractExtentMask {
|
|||||||
public Mask2D toMask2D() {
|
public Mask2D toMask2D() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mask copy() {
|
||||||
|
return new BlockCategoryMask(getExtent(), category);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -328,4 +328,9 @@ public class BlockMask extends ABlockMask {
|
|||||||
}
|
}
|
||||||
return new BlockMask(getExtent(), cloned);
|
return new BlockMask(getExtent(), cloned);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mask copy() {
|
||||||
|
return new BlockMask(getExtent(), ordinals.clone());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,7 @@ import com.sk89q.worldedit.world.block.BlockState;
|
|||||||
import com.sk89q.worldedit.world.block.BlockType;
|
import com.sk89q.worldedit.world.block.BlockType;
|
||||||
import com.sk89q.worldedit.world.block.BlockTypes;
|
import com.sk89q.worldedit.world.block.BlockTypes;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
@ -82,4 +83,11 @@ public class BlockStateMask extends AbstractExtentMask {
|
|||||||
public Mask2D toMask2D() {
|
public Mask2D toMask2D() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mask copy() {
|
||||||
|
Map<String, String> statesClone = new HashMap<>();
|
||||||
|
states.forEach(statesClone::put);
|
||||||
|
return new BlockStateMask(getExtent(), statesClone, strict);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -70,6 +70,12 @@ public class BlockTypeMask extends AbstractExtentMask {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private BlockTypeMask(Extent extent, boolean[] types, boolean hasAir) {
|
||||||
|
super(extent);
|
||||||
|
this.types = types;
|
||||||
|
this.hasAir = hasAir;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add the given blocks to the list of criteria.
|
* Add the given blocks to the list of criteria.
|
||||||
*
|
*
|
||||||
@ -134,4 +140,9 @@ public class BlockTypeMask extends AbstractExtentMask {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mask copy() {
|
||||||
|
return new BlockTypeMask(getExtent(), types.clone(), hasAir);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -57,4 +57,10 @@ public class BoundedHeightMask extends AbstractMask {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mask copy() {
|
||||||
|
// The mask is not mutable. There is no need to clone it.
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -55,4 +55,10 @@ public class ExistingBlockMask extends AbstractExtentMask {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mask copy() {
|
||||||
|
// The mask is not mutable. There is no need to clone it.
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -89,4 +89,9 @@ public class ExpressionMask extends AbstractMask {
|
|||||||
return new ExpressionMask2D(expression, timeout);
|
return new ExpressionMask2D(expression, timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mask copy() {
|
||||||
|
return new ExpressionMask(expression.clone(), timeout);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -72,4 +72,9 @@ public class ExpressionMask2D extends AbstractMask2D {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mask2D copy2D() {
|
||||||
|
return new ExpressionMask2D(expression.clone(), timeout);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -31,4 +31,9 @@ public class InverseMask extends AbstractMask {
|
|||||||
public Mask inverse() {
|
public Mask inverse() {
|
||||||
return mask;
|
return mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mask copy() {
|
||||||
|
return new InverseMask(mask.copy());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,12 @@ public class InverseSingleBlockStateMask extends ABlockMask {
|
|||||||
this.ordinal = state.getOrdinalChar();
|
this.ordinal = state.getOrdinalChar();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private InverseSingleBlockStateMask(Extent extent, char ordinal, boolean isAir) {
|
||||||
|
super(extent);
|
||||||
|
this.ordinal = ordinal;
|
||||||
|
this.isAir = isAir;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean test(BlockVector3 vector) {
|
public boolean test(BlockVector3 vector) {
|
||||||
int test = getExtent().getBlock(vector).getOrdinal();
|
int test = getExtent().getBlock(vector).getOrdinal();
|
||||||
@ -40,4 +46,9 @@ public class InverseSingleBlockStateMask extends ABlockMask {
|
|||||||
public Mask inverse() {
|
public Mask inverse() {
|
||||||
return new SingleBlockStateMask(getExtent(), BlockState.getFromOrdinal(ordinal));
|
return new SingleBlockStateMask(getExtent(), BlockState.getFromOrdinal(ordinal));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mask copy() {
|
||||||
|
return new InverseSingleBlockStateMask(getExtent(), ordinal, isAir);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,4 +27,10 @@ public class InverseSingleBlockTypeMask extends ABlockMask {
|
|||||||
public BlockType getBlockType() {
|
public BlockType getBlockType() {
|
||||||
return BlockTypes.get(internalId);
|
return BlockTypes.get(internalId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mask copy() {
|
||||||
|
// The mask is not mutable. There is no need to clone it.
|
||||||
|
return this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -100,4 +100,10 @@ public interface Mask {
|
|||||||
default boolean replacesAir() {
|
default boolean replacesAir() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a copy of the mask. Usually for multi-threaded operation
|
||||||
|
* @return a clone of the mask
|
||||||
|
*/
|
||||||
|
Mask copy();
|
||||||
}
|
}
|
||||||
|
@ -34,4 +34,6 @@ public interface Mask2D {
|
|||||||
*/
|
*/
|
||||||
boolean test(BlockVector2 vector);
|
boolean test(BlockVector2 vector);
|
||||||
|
|
||||||
|
Mask2D copy2D();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -33,6 +33,7 @@ import java.util.Map;
|
|||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
@ -45,9 +46,9 @@ import static org.slf4j.LoggerFactory.getLogger;
|
|||||||
*/
|
*/
|
||||||
public class MaskIntersection extends AbstractMask {
|
public class MaskIntersection extends AbstractMask {
|
||||||
|
|
||||||
private final Set<Mask> masks;
|
protected final Set<Mask> masks;
|
||||||
private Mask[] masksArray;
|
protected Mask[] masksArray;
|
||||||
private boolean defaultReturn;
|
protected boolean defaultReturn;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new intersection.
|
* Create a new intersection.
|
||||||
@ -60,6 +61,12 @@ public class MaskIntersection extends AbstractMask {
|
|||||||
formArray();
|
formArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected MaskIntersection(Set<Mask> masks, Mask[] masksArray, boolean defaultReturn) {
|
||||||
|
this.masks = masks;
|
||||||
|
this.masksArray = masksArray;
|
||||||
|
this.defaultReturn = defaultReturn;
|
||||||
|
}
|
||||||
|
|
||||||
public static Mask of(Mask... masks) {
|
public static Mask of(Mask... masks) {
|
||||||
Set<Mask> set = new LinkedHashSet<>();
|
Set<Mask> set = new LinkedHashSet<>();
|
||||||
for (Mask mask : masks) {
|
for (Mask mask : masks) {
|
||||||
@ -250,4 +257,11 @@ public class MaskIntersection extends AbstractMask {
|
|||||||
return new MaskIntersection2D(mask2dList);
|
return new MaskIntersection2D(mask2dList);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mask copy(){
|
||||||
|
Set<Mask> masks = this.masks.stream().map(Mask::copy).collect(Collectors.toSet());
|
||||||
|
Mask[] maskArray = (Mask[]) Arrays.stream(this.masksArray).map(Mask::copy).toArray();
|
||||||
|
return new MaskIntersection(masks, maskArray, this.defaultReturn);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,7 @@ import java.util.Arrays;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
@ -33,7 +34,7 @@ import static com.google.common.base.Preconditions.checkNotNull;
|
|||||||
*/
|
*/
|
||||||
public class MaskIntersection2D implements Mask2D {
|
public class MaskIntersection2D implements Mask2D {
|
||||||
|
|
||||||
private final Set<Mask2D> masks = new HashSet<>();
|
protected final Set<Mask2D> masks = new HashSet<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new intersection.
|
* Create a new intersection.
|
||||||
@ -97,4 +98,10 @@ public class MaskIntersection2D implements Mask2D {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mask2D copy2D() {
|
||||||
|
Set<Mask2D> masksCopy = this.masks.stream().map(Mask2D::copy2D).collect(Collectors.toSet());
|
||||||
|
return new MaskIntersection2D(masksCopy);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -22,12 +22,14 @@ package com.sk89q.worldedit.function.mask;
|
|||||||
import com.sk89q.worldedit.math.BlockVector3;
|
import com.sk89q.worldedit.math.BlockVector3;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -55,6 +57,10 @@ public class MaskUnion extends MaskIntersection {
|
|||||||
super(mask);
|
super(mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private MaskUnion(Set<Mask> masks, Mask[] maskArray, boolean defaultReturn) {
|
||||||
|
super(masks, maskArray, defaultReturn);
|
||||||
|
}
|
||||||
|
|
||||||
public static Mask of(Mask... masks) {
|
public static Mask of(Mask... masks) {
|
||||||
Set<Mask> set = new LinkedHashSet<>();
|
Set<Mask> set = new LinkedHashSet<>();
|
||||||
for (Mask mask : masks) {
|
for (Mask mask : masks) {
|
||||||
@ -111,4 +117,11 @@ public class MaskUnion extends MaskIntersection {
|
|||||||
}
|
}
|
||||||
return new MaskUnion2D(mask2dList);
|
return new MaskUnion2D(mask2dList);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mask copy() {
|
||||||
|
Set<Mask> masksCopy = masks.stream().map(Mask::copy).collect(Collectors.toSet());
|
||||||
|
Mask[] maskArray = (Mask[]) Arrays.stream(masksArray).map(Mask::copy).toArray();
|
||||||
|
return new MaskUnion(masksCopy, maskArray, defaultReturn);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,8 @@ package com.sk89q.worldedit.function.mask;
|
|||||||
import com.sk89q.worldedit.math.BlockVector2;
|
import com.sk89q.worldedit.math.BlockVector2;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests true if any contained mask is true, even if it just one.
|
* Tests true if any contained mask is true, even if it just one.
|
||||||
@ -59,4 +61,10 @@ public class MaskUnion2D extends MaskIntersection2D {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mask2D copy2D() {
|
||||||
|
Set<Mask2D> masksCopy = masks.stream().map(Mask2D::copy2D).collect(Collectors.toSet());
|
||||||
|
return new MaskUnion2D(masksCopy);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -92,6 +92,11 @@ public final class Masks {
|
|||||||
public boolean test(BlockVector2 vector) {
|
public boolean test(BlockVector2 vector) {
|
||||||
return !mask.test(vector);
|
return !mask.test(vector);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mask2D copy2D() {
|
||||||
|
return Masks.negate(mask.copy2D());
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -113,6 +118,11 @@ public final class Masks {
|
|||||||
public Mask2D toMask2D() {
|
public Mask2D toMask2D() {
|
||||||
return mask;
|
return mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mask copy() {
|
||||||
|
return Masks.asMask(mask.copy2D());
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -143,6 +153,18 @@ public final class Masks {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// No need to properly clone an always true mask
|
||||||
|
@Override
|
||||||
|
public Mask copy() {
|
||||||
|
return new AlwaysTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
// No need to properly clone an always true mask
|
||||||
|
@Override
|
||||||
|
public Mask2D copy2D() {
|
||||||
|
return new AlwaysTrue();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static class AlwaysFalse implements Mask, Mask2D {
|
protected static class AlwaysFalse implements Mask, Mask2D {
|
||||||
@ -172,6 +194,18 @@ public final class Masks {
|
|||||||
return other;
|
return other;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// No need to properly clone an always false mask
|
||||||
|
@Override
|
||||||
|
public Mask copy() {
|
||||||
|
return new AlwaysFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
// No need to properly clone an always true mask
|
||||||
|
@Override
|
||||||
|
public Mask2D copy2D() {
|
||||||
|
return new AlwaysFalse();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -96,4 +96,9 @@ public class NoiseFilter extends AbstractMask {
|
|||||||
return new NoiseFilter2D(getNoiseGenerator(), getDensity());
|
return new NoiseFilter2D(getNoiseGenerator(), getDensity());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mask copy() {
|
||||||
|
return new NoiseFilter(noiseGenerator, density);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -87,4 +87,9 @@ public class NoiseFilter2D extends AbstractMask2D {
|
|||||||
return noiseGenerator.noise(pos.toVector2()) <= density;
|
return noiseGenerator.noise(pos.toVector2()) <= density;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mask2D copy2D() {
|
||||||
|
return new NoiseFilter2D(noiseGenerator, density);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -105,4 +105,9 @@ public class OffsetMask extends AbstractMask {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mask copy() {
|
||||||
|
return new OffsetMask(mask.copy(), offset.toImmutable());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -92,4 +92,9 @@ public class OffsetMask2D extends AbstractMask2D {
|
|||||||
return getMask().test(mutable);
|
return getMask().test(mutable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mask2D copy2D() {
|
||||||
|
return new OffsetMask2D(mask.copy2D(), BlockVector2.at(offset.getX(), offset.getZ()));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -72,4 +72,9 @@ public class RegionMask extends AbstractMask {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mask copy() {
|
||||||
|
return new RegionMask(region.clone());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,12 @@ public class SingleBlockStateMask extends ABlockMask {
|
|||||||
this.ordinal = state.getOrdinalChar();
|
this.ordinal = state.getOrdinalChar();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private SingleBlockStateMask(Extent extent, char ordinal, boolean isAir) {
|
||||||
|
super(extent);
|
||||||
|
this.ordinal = ordinal;
|
||||||
|
this.isAir = isAir;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean test(BlockVector3 vector) {
|
public boolean test(BlockVector3 vector) {
|
||||||
int test = getExtent().getBlock(vector).getOrdinal();
|
int test = getExtent().getBlock(vector).getOrdinal();
|
||||||
@ -50,4 +56,9 @@ public class SingleBlockStateMask extends ABlockMask {
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mask copy() {
|
||||||
|
return new SingleBlockStateMask(getExtent(), ordinal, isAir);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,12 @@ public class SingleBlockTypeMask extends ABlockMask {
|
|||||||
this.internalId = type.getInternalId();
|
this.internalId = type.getInternalId();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private SingleBlockTypeMask(Extent extent, int internalId, boolean isAir) {
|
||||||
|
super(extent);
|
||||||
|
this.internalId = internalId;
|
||||||
|
this.isAir = isAir;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final boolean test(BlockState state) {
|
public final boolean test(BlockState state) {
|
||||||
return state.getBlockType().getInternalId() == internalId;
|
return state.getBlockType().getInternalId() == internalId;
|
||||||
@ -35,4 +41,9 @@ public class SingleBlockTypeMask extends ABlockMask {
|
|||||||
public boolean replacesAir() {
|
public boolean replacesAir() {
|
||||||
return isAir;
|
return isAir;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mask copy() {
|
||||||
|
return new SingleBlockTypeMask(getExtent(), internalId, isAir);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,4 +35,9 @@ public class SolidBlockMask extends BlockMask {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mask copy() {
|
||||||
|
return new SolidBlockMask(getExtent());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,26 @@
|
|||||||
|
package com.sk89q.worldedit.function.mask;
|
||||||
|
|
||||||
|
import com.sk89q.worldedit.math.BlockVector3;
|
||||||
|
import com.sk89q.worldedit.regions.Region;
|
||||||
|
|
||||||
|
public class WallMakeMask implements Mask {
|
||||||
|
|
||||||
|
private final Region region;
|
||||||
|
|
||||||
|
public WallMakeMask(Region region) {
|
||||||
|
this.region = region.clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean test(BlockVector3 position) {
|
||||||
|
int x = position.getBlockX();
|
||||||
|
int z = position.getBlockZ();
|
||||||
|
return !region.contains(x, z + 1) || !region.contains(x, z - 1) || !region.contains(x + 1, z) || !region.contains(x - 1, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mask copy() {
|
||||||
|
return new WallMakeMask(region);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -31,8 +31,10 @@ import org.antlr.v4.runtime.misc.ParseCancellationException;
|
|||||||
import org.antlr.v4.runtime.tree.ParseTreeWalker;
|
import org.antlr.v4.runtime.tree.ParseTreeWalker;
|
||||||
|
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compiles and evaluates expressions.
|
* Compiles and evaluates expressions.
|
||||||
@ -60,19 +62,55 @@ import java.util.Objects;
|
|||||||
* pass values for all slots specified while compiling.
|
* pass values for all slots specified while compiling.
|
||||||
* To query slots after evaluation, you can use the {@linkplain #getSlots() slot table}.
|
* To query slots after evaluation, you can use the {@linkplain #getSlots() slot table}.
|
||||||
*/
|
*/
|
||||||
public class Expression {
|
public class Expression implements Cloneable {
|
||||||
|
|
||||||
private final SlotTable slots = new SlotTable();
|
private final SlotTable slots = new SlotTable();
|
||||||
private final List<String> providedSlots;
|
private final List<String> providedSlots;
|
||||||
private final ExpressionParser.AllStatementsContext root;
|
private final ExpressionParser.AllStatementsContext root;
|
||||||
private final Functions functions = Functions.create();
|
private final Functions functions = Functions.create();
|
||||||
private final CompiledExpression compiledExpression;
|
private final CompiledExpression compiledExpression;
|
||||||
|
private final String initialExpression;
|
||||||
|
|
||||||
public static Expression compile(String expression, String... variableNames) throws ExpressionException {
|
public static Expression compile(String expression, String... variableNames) throws ExpressionException {
|
||||||
return new Expression(expression, variableNames);
|
return new Expression(expression, variableNames);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Expression(String expression, String... variableNames) throws ExpressionException {
|
private Expression(String expression, String... variableNames) throws ExpressionException {
|
||||||
|
this.initialExpression = expression;
|
||||||
|
|
||||||
|
slots.putSlot("e", new LocalSlot.Constant(Math.E));
|
||||||
|
slots.putSlot("pi", new LocalSlot.Constant(Math.PI));
|
||||||
|
slots.putSlot("true", new LocalSlot.Constant(1));
|
||||||
|
slots.putSlot("false", new LocalSlot.Constant(0));
|
||||||
|
|
||||||
|
for (String variableName : variableNames) {
|
||||||
|
slots.initVariable(variableName)
|
||||||
|
.orElseThrow(() -> new ExpressionException(-1,
|
||||||
|
"Tried to overwrite identifier '" + variableName + "'"));
|
||||||
|
}
|
||||||
|
this.providedSlots = ImmutableList.copyOf(variableNames);
|
||||||
|
|
||||||
|
CharStream cs = CharStreams.fromString(expression, "<input>");
|
||||||
|
ExpressionLexer lexer = new ExpressionLexer(cs);
|
||||||
|
lexer.removeErrorListeners();
|
||||||
|
lexer.addErrorListener(new LexerErrorListener());
|
||||||
|
CommonTokenStream tokens = new CommonTokenStream(lexer);
|
||||||
|
ExpressionParser parser = new ExpressionParser(tokens);
|
||||||
|
parser.removeErrorListeners();
|
||||||
|
parser.addErrorListener(new ParserErrorListener());
|
||||||
|
try {
|
||||||
|
root = parser.allStatements();
|
||||||
|
Objects.requireNonNull(root, "Unable to parse root, but no exceptions?");
|
||||||
|
} catch (ParseCancellationException e) {
|
||||||
|
throw new ParserException(parser.getState(), e);
|
||||||
|
}
|
||||||
|
ParseTreeWalker.DEFAULT.walk(new ExpressionValidator(slots.keySet(), functions), root);
|
||||||
|
this.compiledExpression = new ExpressionCompiler().compileExpression(root, functions);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Expression(String expression, Set<String> variableNames) throws ExpressionException {
|
||||||
|
this.initialExpression = expression;
|
||||||
|
|
||||||
slots.putSlot("e", new LocalSlot.Constant(Math.E));
|
slots.putSlot("e", new LocalSlot.Constant(Math.E));
|
||||||
slots.putSlot("pi", new LocalSlot.Constant(Math.PI));
|
slots.putSlot("pi", new LocalSlot.Constant(Math.PI));
|
||||||
slots.putSlot("true", new LocalSlot.Constant(1));
|
slots.putSlot("true", new LocalSlot.Constant(1));
|
||||||
@ -147,4 +185,8 @@ public class Expression {
|
|||||||
functions.setEnvironment(environment);
|
functions.setEnvironment(environment);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Expression clone() {
|
||||||
|
return new Expression(initialExpression, new HashSet<>(providedSlots));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren