From 5bbc381dd0da62df19ef9facace1b7a210404f6c Mon Sep 17 00:00:00 2001 From: Jesse Boyd Date: Sat, 11 Mar 2017 00:27:39 +1100 Subject: [PATCH] Various minor Stencil/Color brush can now be used on any surface, not just the top Optimize adjacency and radius mask --- .../fawe/command/FawePrimitiveBinding.java | 1 - .../fawe/object/brush/StencilBrush.java | 111 ++++++++++++++---- .../fawe/object/mask/AdjacentAnyMask.java | 62 ++++++++++ .../boydti/fawe/object/mask/RadiusMask.java | 13 +- .../worldedit/command/BrushCommands.java | 6 +- .../extension/factory/DefaultMaskParser.java | 5 +- .../sk89q/worldedit/function/mask/Mask.java | 4 +- .../function/visitor/BreadthFirstSearch.java | 33 +++++- 8 files changed, 194 insertions(+), 41 deletions(-) create mode 100644 core/src/main/java/com/boydti/fawe/object/mask/AdjacentAnyMask.java diff --git a/core/src/main/java/com/boydti/fawe/command/FawePrimitiveBinding.java b/core/src/main/java/com/boydti/fawe/command/FawePrimitiveBinding.java index e40b3e10..848b1773 100644 --- a/core/src/main/java/com/boydti/fawe/command/FawePrimitiveBinding.java +++ b/core/src/main/java/com/boydti/fawe/command/FawePrimitiveBinding.java @@ -119,7 +119,6 @@ public class FawePrimitiveBinding extends BindingHelper { try { return Double.parseDouble(input); } catch (NumberFormatException e1) { - System.out.println("NUMBER FORMAT EXCEPTION " + e1); try { Expression expression = Expression.compile(input); return expression.evaluate(); diff --git a/core/src/main/java/com/boydti/fawe/object/brush/StencilBrush.java b/core/src/main/java/com/boydti/fawe/object/brush/StencilBrush.java index 9f176de4..8d468a7e 100644 --- a/core/src/main/java/com/boydti/fawe/object/brush/StencilBrush.java +++ b/core/src/main/java/com/boydti/fawe/object/brush/StencilBrush.java @@ -1,56 +1,117 @@ package com.boydti.fawe.object.brush; import com.boydti.fawe.object.PseudoRandom; +import com.boydti.fawe.object.mask.AdjacentAnyMask; +import com.boydti.fawe.object.mask.RadiusMask; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.MaxChangedBlocksException; import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.blocks.BaseBlock; import com.sk89q.worldedit.extent.clipboard.Clipboard; -import com.sk89q.worldedit.function.mask.ExistingBlockMask; +import com.sk89q.worldedit.function.RegionFunction; import com.sk89q.worldedit.function.mask.Mask; +import com.sk89q.worldedit.function.mask.SolidBlockMask; +import com.sk89q.worldedit.function.operation.Operations; import com.sk89q.worldedit.function.pattern.Pattern; +import com.sk89q.worldedit.function.visitor.RecursiveVisitor; import java.io.InputStream; +import java.util.Arrays; public class StencilBrush extends HeightBrush { private final boolean onlyWhite; - private final int depth; - public StencilBrush(InputStream stream, int depth, int rotation, double yscale, boolean onlyWhite, Clipboard clipboard) { + public StencilBrush(InputStream stream, int rotation, double yscale, boolean onlyWhite, Clipboard clipboard) { super(stream, rotation, yscale, clipboard); this.onlyWhite = onlyWhite; - this.depth = depth; } @Override public void build(EditSession editSession, Vector position, 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; - Mask mask = new ExistingBlockMask(editSession); + int maxY = editSession.getMaxY(); double scale = (yscale / sizeDouble) * (maxY + 1); heightMap.setSize(size); int cutoff = onlyWhite ? maxY : 0; - for (int x = -size; x <= size; x++) { - int xx = position.getBlockX() + x; - for (int z = -size; z <= size; z++) { - double raise; - switch (rotation) { - default:raise = heightMap.getHeight(x, z); break; - case 1: raise = heightMap.getHeight(z, x); break; - case 2: raise = heightMap.getHeight(-x, -z); break; - case 3: raise = heightMap.getHeight(-z, -x);break; - } - int val = (int) Math.ceil(raise * scale); - if (val <= cutoff) { - continue; - } - if (val >= 255 || PseudoRandom.random.random(maxY) < val) { - int zz = position.getBlockZ() + z; - int y = editSession.getNearestSurfaceTerrainBlock(xx, zz, position.getBlockY(), 0, maxY); - for (int i = 0; i < depth; i++) { - editSession.setBlock(xx, y - i, zz, pattern); + final AdjacentAnyMask adjacent = new AdjacentAnyMask(editSession, Arrays.asList(new BaseBlock(0))); + final SolidBlockMask solid = new SolidBlockMask(editSession); + final RadiusMask radius = new RadiusMask(0, size); + RecursiveVisitor visitor = new RecursiveVisitor(new Mask() { + @Override + public boolean test(Vector vector) { + if (solid.test(vector) && radius.test(vector)) { + Vector dir = adjacent.direction(vector); + if (dir != null) { + int dx = vector.getBlockX() - cx; + int dy = vector.getBlockY() - cy; + int dz = vector.getBlockZ() - cz; + if (dy != 0) { + if (dir.getBlockX() != 0) { + dx += dir.getBlockX() * dy; + } else if (dir.getBlockZ() != 0) { + dz += dir.getBlockZ() * dy; + } + } + double raise = heightMap.getHeight(dx, dz); + int val = (int) Math.ceil(raise * scale); + if (val <= cutoff) { + return true; + } + if (val >= 255 || PseudoRandom.random.random(maxY) < val) { + editSession.setBlock(vector.getBlockX(), vector.getBlockY(), vector.getBlockZ(), pattern); + } + return true; } } + return false; } - } + }, new RegionFunction() { + @Override + public boolean apply(Vector vector) throws WorldEditException { + return true; + } + }, Integer.MAX_VALUE, editSession); + visitor.setDirections(Arrays.asList(visitor.DIAGONAL_DIRECTIONS)); + visitor.visit(position); + Operations.completeBlindly(visitor); + +// Mask mask = new ExistingBlockMask(editSession); +// int maxY = editSession.getMaxY(); +// double scale = (yscale / sizeDouble) * (maxY + 1); +// heightMap.setSize(size); +// int cutoff = onlyWhite ? maxY : 0; +// +// for (int x = -size; x <= size; x++) { +// int xx = position.getBlockX() + x; +// for (int z = -size; z <= size; z++) { +// double raise; +// switch (rotation) { +// default:raise = heightMap.getHeight(x, z); break; +// case 1: raise = heightMap.getHeight(z, x); break; +// case 2: raise = heightMap.getHeight(-x, -z); break; +// case 3: raise = heightMap.getHeight(-z, -x);break; +// } +// int val = (int) Math.ceil(raise * scale); +// if (val <= cutoff) { +// continue; +// } +// if (val >= 255 || PseudoRandom.random.random(maxY) < val) { +// int zz = position.getBlockZ() + z; +// int y = editSession.getNearestSurfaceTerrainBlock(xx, zz, position.getBlockY(), 0, maxY); +// for (int i = 0; i < depth; i++) { +// editSession.setBlock(xx, y - i, zz, pattern); +// } +// } +// } +// } + } + + private void apply(double val) { + } } \ No newline at end of file diff --git a/core/src/main/java/com/boydti/fawe/object/mask/AdjacentAnyMask.java b/core/src/main/java/com/boydti/fawe/object/mask/AdjacentAnyMask.java new file mode 100644 index 00000000..9c49e737 --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/object/mask/AdjacentAnyMask.java @@ -0,0 +1,62 @@ +package com.boydti.fawe.object.mask; + +import com.sk89q.worldedit.MutableBlockVector; +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.blocks.BaseBlock; +import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.function.mask.BlockMask; +import java.util.Collection; + +/** + * Just an optimized version of the Adjacent Mask for single adjacency + */ +public class AdjacentAnyMask extends BlockMask { + public AdjacentAnyMask(Extent extent, Collection blocks) { + super(extent, blocks); + } + + @Override + public boolean test(Vector v) { + int x = v.getBlockX(); + int y = v.getBlockY(); + int z = v.getBlockZ(); + v.mutY(y + 1); + if (super.test(v)) { v.mutY(y); return true; } + v.mutY(y - 1); + if (super.test(v)) { v.mutY(y); return true; } + v.mutY(y); + v.mutX(x + 1); + if (super.test(v)) { v.mutX(x); return true; } + v.mutX(x - 1); + if (super.test(v)) { v.mutX(x); return true; } + v.mutX(x); + v.mutZ(z + 1); + if (super.test(v)) { v.mutZ(z); return true; } + v.mutZ(z - 1); + if (super.test(v)) { v.mutZ(z); return true; } + v.mutZ(z); + return false; + } + + public Vector direction(Vector v) { + int x = v.getBlockX(); + int y = v.getBlockY(); + int z = v.getBlockZ(); + v.mutY(y + 1); + if (super.test(v)) { v.mutY(y); return MutableBlockVector.get(0, 1, 0); } + v.mutY(y - 1); + if (super.test(v)) { v.mutY(y); return MutableBlockVector.get(0, -1, 0); } + v.mutY(y); + v.mutX(x + 1); + if (super.test(v)) { v.mutX(x); return MutableBlockVector.get(1, 0, 0); } + v.mutX(x - 1); + if (super.test(v)) { v.mutX(x); return MutableBlockVector.get(-1, 0, 0); } + v.mutX(x); + v.mutZ(z + 1); + if (super.test(v)) { v.mutZ(z); return MutableBlockVector.get(0, 0, 1); } + v.mutZ(z - 1); + if (super.test(v)) { v.mutZ(z); return MutableBlockVector.get(0, 0, - 1); } + v.mutZ(z); + return null; + } +} \ No newline at end of file diff --git a/core/src/main/java/com/boydti/fawe/object/mask/RadiusMask.java b/core/src/main/java/com/boydti/fawe/object/mask/RadiusMask.java index 95d66e1f..db4d0a3f 100644 --- a/core/src/main/java/com/boydti/fawe/object/mask/RadiusMask.java +++ b/core/src/main/java/com/boydti/fawe/object/mask/RadiusMask.java @@ -1,5 +1,6 @@ package com.boydti.fawe.object.mask; +import com.sk89q.worldedit.MutableBlockVector; import com.sk89q.worldedit.Vector; import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.function.mask.Mask2D; @@ -24,19 +25,19 @@ public class RadiusMask implements Mask, ResettableMask{ @Override public boolean test(Vector to) { if (pos == null) { - pos = new Vector(to); + pos = new MutableBlockVector(to); } - int dx = Math.abs((int) (pos.getX() - to.getX())); - int dy = Math.abs((int) (pos.getY() - to.getY())); - int dz = Math.abs((int) (pos.getZ() - to.getZ())); + int dx = pos.getBlockX() - to.getBlockX(); int d = dx * dx; - if (d < minSqr || d > maxSqr) { + if (d > maxSqr) { return false; } + int dz = pos.getBlockZ() - to.getBlockZ(); d += dz * dz; - if (d < minSqr || d > maxSqr) { + if (d > maxSqr) { return false; } + int dy = pos.getBlockY() - to.getBlockY(); d += dy * dy; if (d < minSqr || d > maxSqr) { return false; diff --git a/core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java b/core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java index 2114bb10..5e3865cf 100644 --- a/core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java +++ b/core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java @@ -542,16 +542,16 @@ public class BrushCommands { max = -1 ) @CommandPermissions("worldedit.brush.stencil") - public void stencilBrush(Player player, LocalSession session, Pattern fill, @Optional("5") double radius, @Optional("1") double depth, @Optional("") final String filename, @Optional("0") final int rotation, @Optional("1") final double yscale, @Switch('w') boolean onlyWhite) throws WorldEditException { + public void stencilBrush(Player player, EditSession editSession, LocalSession session, Pattern fill, @Optional("5") double radius, @Optional("") final String filename, @Optional("0") final int rotation, @Optional("1") final double yscale, @Switch('w') boolean onlyWhite) throws WorldEditException { worldEdit.checkMaxBrushRadius(radius); BrushTool tool = session.getBrushTool(player); InputStream stream = getHeightmapStream(filename); tool.setFill(fill); tool.setSize(radius); try { - tool.setBrush(new StencilBrush(stream, (int) depth, rotation, yscale, onlyWhite, filename.equalsIgnoreCase("#clipboard") ? session.getClipboard().getClipboard() : null), "worldedit.brush.height", player); + tool.setBrush(new StencilBrush(stream, rotation, yscale, onlyWhite, filename.equalsIgnoreCase("#clipboard") ? session.getClipboard().getClipboard() : null), "worldedit.brush.height", player); } catch (EmptyClipboardException ignore) { - tool.setBrush(new StencilBrush(stream, (int) depth, rotation, yscale, onlyWhite, null), "worldedit.brush.height", player); + tool.setBrush(new StencilBrush(stream, rotation, yscale, onlyWhite, null), "worldedit.brush.height", player); } player.print(BBC.getPrefix() + BBC.BRUSH_STENCIL.f(radius)); diff --git a/core/src/main/java/com/sk89q/worldedit/extension/factory/DefaultMaskParser.java b/core/src/main/java/com/sk89q/worldedit/extension/factory/DefaultMaskParser.java index 5c7c3726..7d37edd7 100644 --- a/core/src/main/java/com/sk89q/worldedit/extension/factory/DefaultMaskParser.java +++ b/core/src/main/java/com/sk89q/worldedit/extension/factory/DefaultMaskParser.java @@ -241,7 +241,7 @@ public class DefaultMaskParser extends FaweParser { return new WallMask(extent, Arrays.asList(new BaseBlock(0)), 1, 8); case "#surface": masks.add(new ExistingBlockMask(extent)); - return new AdjacentMask(extent, Arrays.asList(new BaseBlock(0)), 1, 8); + return new AdjacentAnyMask(extent, Arrays.asList(new BaseBlock(0))); default: throw new SuggestInputParseException(input, HASHTAG_MASKS); } @@ -297,6 +297,9 @@ public class DefaultMaskParser extends FaweParser { } } if (firstChar == '~') { + if (requiredMax >= 8 && requiredMin == 1) { + return new AdjacentAnyMask(extent, worldEdit.getBlockFactory().parseFromListInput(split[0], tempContext)); + } return new AdjacentMask(extent, worldEdit.getBlockFactory().parseFromListInput(split[0], tempContext), requiredMin, requiredMax); } else { return new WallMask(extent, worldEdit.getBlockFactory().parseFromListInput(input.substring(1), tempContext), requiredMin, requiredMax); diff --git a/core/src/main/java/com/sk89q/worldedit/function/mask/Mask.java b/core/src/main/java/com/sk89q/worldedit/function/mask/Mask.java index 28fa7dba..e3bfae1d 100644 --- a/core/src/main/java/com/sk89q/worldedit/function/mask/Mask.java +++ b/core/src/main/java/com/sk89q/worldedit/function/mask/Mask.java @@ -45,7 +45,9 @@ public interface Mask extends com.sk89q.worldedit.masks.Mask { * @return a 2D mask version or {@code null} if this mask can't be 2D */ @Nullable - Mask2D toMask2D(); + default Mask2D toMask2D() { + return null; + } default void prepare(LocalSession session, LocalPlayer player, Vector target) {} diff --git a/core/src/main/java/com/sk89q/worldedit/function/visitor/BreadthFirstSearch.java b/core/src/main/java/com/sk89q/worldedit/function/visitor/BreadthFirstSearch.java index 1099debb..bf051ed0 100644 --- a/core/src/main/java/com/sk89q/worldedit/function/visitor/BreadthFirstSearch.java +++ b/core/src/main/java/com/sk89q/worldedit/function/visitor/BreadthFirstSearch.java @@ -16,11 +16,14 @@ import com.sk89q.worldedit.function.operation.RunContext; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; import java.util.List; public abstract class BreadthFirstSearch implements Operation { - public static Vector[] DEFAULT_DIRECTIONS = new Vector[6]; + public static final Vector[] DEFAULT_DIRECTIONS = new Vector[6]; + public static final Vector[] DIAGONAL_DIRECTIONS; static { DEFAULT_DIRECTIONS[0] = (new MutableBlockVector(0, -1, 0)); DEFAULT_DIRECTIONS[1] = (new MutableBlockVector(0, 1, 0)); @@ -28,10 +31,30 @@ public abstract class BreadthFirstSearch implements Operation { DEFAULT_DIRECTIONS[3] = (new MutableBlockVector(1, 0, 0)); DEFAULT_DIRECTIONS[4] = (new MutableBlockVector(0, 0, -1)); DEFAULT_DIRECTIONS[5] = (new MutableBlockVector(0, 0, 1)); + List list = new ArrayList<>(); + for (int x = -1; x <= 1; x++) { + for (int y = -1; y <= 1; y++) { + for (int z = -1; z <= 1; z++) { + if (x != 0 || y != 0 || z != 0) { + MutableBlockVector pos = new MutableBlockVector(x, y, z); + if (!list.contains(pos)) { + list.add(pos); + } + } + } + } + } + Collections.sort(list, new Comparator() { + @Override + public int compare(Vector o1, Vector o2) { + return (int) Math.signum(o1.lengthSq() - o2.lengthSq()); + } + }); + DIAGONAL_DIRECTIONS = list.toArray(new Vector[list.size()]); } private final RegionFunction function; - private final List directions = new ArrayList<>(); + private List directions = new ArrayList<>(); private BlockVectorSet visited; private final MappedFaweQueue mFaweQueue; private BlockVectorSet queue; @@ -62,6 +85,10 @@ public abstract class BreadthFirstSearch implements Operation { return this.directions; } + public void setDirections(List directions) { + this.directions = directions; + } + private IntegerTrio[] getIntDirections() { IntegerTrio[] array = new IntegerTrio[directions.size()]; for (int i = 0; i < array.length; i++) { @@ -91,8 +118,6 @@ public abstract class BreadthFirstSearch implements Operation { return visited.contains(pos); } - - @Override public Operation resume(RunContext run) throws WorldEditException { MutableBlockVector mutable = new MutableBlockVector();