From fb76ff4ea0fc462de6e87717db905d2d998ddc5d Mon Sep 17 00:00:00 2001 From: Jesse Boyd Date: Sat, 11 Mar 2017 17:16:10 +1100 Subject: [PATCH] Heightmap rotation --- build.gradle | 2 +- .../fawe/object/brush/FlattenBrush.java | 6 +- .../boydti/fawe/object/brush/HeightBrush.java | 26 +++- .../fawe/object/brush/StencilBrush.java | 15 +- .../heightmap/AbstractDelegateHeightMap.java | 20 +++ .../object/brush/heightmap/HeightMap.java | 132 ++++++++++++++++++ .../brush/heightmap/RotatableHeightMap.java | 28 ++++ .../brush/heightmap/ScalableHeightMap.java | 126 +---------------- .../fawe/object/extent/OffsetExtent.java | 39 ++++++ .../java/com/sk89q/worldedit/EditSession.java | 60 +++++++- .../worldedit/command/BrushCommands.java | 39 ++++-- 11 files changed, 343 insertions(+), 150 deletions(-) create mode 100644 core/src/main/java/com/boydti/fawe/object/brush/heightmap/AbstractDelegateHeightMap.java create mode 100644 core/src/main/java/com/boydti/fawe/object/brush/heightmap/HeightMap.java create mode 100644 core/src/main/java/com/boydti/fawe/object/brush/heightmap/RotatableHeightMap.java create mode 100644 core/src/main/java/com/boydti/fawe/object/extent/OffsetExtent.java diff --git a/build.gradle b/build.gradle index 6e950f4f..f2709ac0 100644 --- a/build.gradle +++ b/build.gradle @@ -28,7 +28,7 @@ ext { date = git.head().date.format("yy.MM.dd") revision = "-${git.head().abbreviatedId}" parents = git.head().parentIds; - index = -87; // Offset to mach CI + index = -88; // Offset to mach CI int major, minor, patch; major = minor = patch = 0; for (;parents != null && !parents.isEmpty();index++) { diff --git a/core/src/main/java/com/boydti/fawe/object/brush/FlattenBrush.java b/core/src/main/java/com/boydti/fawe/object/brush/FlattenBrush.java index 79f1f185..d1af0a20 100644 --- a/core/src/main/java/com/boydti/fawe/object/brush/FlattenBrush.java +++ b/core/src/main/java/com/boydti/fawe/object/brush/FlattenBrush.java @@ -1,5 +1,6 @@ package com.boydti.fawe.object.brush; +import com.boydti.fawe.object.brush.heightmap.HeightMap; import com.boydti.fawe.object.brush.heightmap.ScalableHeightMap; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.MaxChangedBlocksException; @@ -23,7 +24,8 @@ public class FlattenBrush extends HeightBrush { if (mask == Masks.alwaysTrue() || mask == Masks.alwaysTrue2D()) { mask = null; } - heightMap.setSize(size); - heightMap.perform(editSession, mask, position, size, rotation, yscale, true, true); + HeightMap map = getHeightMap(); + map.setSize(size); + map.perform(editSession, mask, position, size, rotation, yscale, true, true); } } diff --git a/core/src/main/java/com/boydti/fawe/object/brush/HeightBrush.java b/core/src/main/java/com/boydti/fawe/object/brush/HeightBrush.java index 53263f14..aeb85e82 100644 --- a/core/src/main/java/com/boydti/fawe/object/brush/HeightBrush.java +++ b/core/src/main/java/com/boydti/fawe/object/brush/HeightBrush.java @@ -1,6 +1,9 @@ package com.boydti.fawe.object.brush; import com.boydti.fawe.config.BBC; +import com.boydti.fawe.object.PseudoRandom; +import com.boydti.fawe.object.brush.heightmap.HeightMap; +import com.boydti.fawe.object.brush.heightmap.RotatableHeightMap; import com.boydti.fawe.object.brush.heightmap.ScalableHeightMap; import com.boydti.fawe.object.exception.FaweException; import com.sk89q.worldedit.EditSession; @@ -16,7 +19,8 @@ import java.io.InputStream; public class HeightBrush implements Brush { - public final ScalableHeightMap heightMap; + private HeightMap heightMap; + private boolean randomRotate; public final int rotation; public final double yscale; @@ -40,6 +44,21 @@ public class HeightBrush implements Brush { } } + public HeightMap getHeightMap() { + if (randomRotate) { + if (!(heightMap instanceof RotatableHeightMap)) { + heightMap = new RotatableHeightMap(heightMap); + } + RotatableHeightMap rotatable = (RotatableHeightMap) heightMap; + rotatable.rotate(PseudoRandom.random.nextInt(360)); + } + return heightMap; + } + + public void setRandomRotate(boolean randomRotate) { + this.randomRotate = randomRotate; + } + @Override public void build(EditSession editSession, Vector position, Pattern pattern, double sizeDouble) throws MaxChangedBlocksException { int size = (int) sizeDouble; @@ -47,7 +66,8 @@ public class HeightBrush implements Brush { if (mask == Masks.alwaysTrue() || mask == Masks.alwaysTrue2D()) { mask = null; } - heightMap.setSize(size); - heightMap.perform(editSession, mask, position, size, rotation, yscale, true, false); + HeightMap map = getHeightMap(); + map.setSize(size); + map.perform(editSession, mask, position, size, rotation, yscale, true, false); } } 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 8d468a7e..939db357 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,6 +1,7 @@ package com.boydti.fawe.object.brush; import com.boydti.fawe.object.PseudoRandom; +import com.boydti.fawe.object.brush.heightmap.HeightMap; import com.boydti.fawe.object.mask.AdjacentAnyMask; import com.boydti.fawe.object.mask.RadiusMask; import com.sk89q.worldedit.EditSession; @@ -11,10 +12,12 @@ import com.sk89q.worldedit.blocks.BaseBlock; import com.sk89q.worldedit.extent.clipboard.Clipboard; import com.sk89q.worldedit.function.RegionFunction; import com.sk89q.worldedit.function.mask.Mask; +import com.sk89q.worldedit.function.mask.RegionMask; 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 com.sk89q.worldedit.regions.CuboidRegion; import java.io.InputStream; import java.util.Arrays; @@ -35,21 +38,23 @@ public class StencilBrush extends HeightBrush { int maxY = editSession.getMaxY(); double scale = (yscale / sizeDouble) * (maxY + 1); - heightMap.setSize(size); + final HeightMap map = getHeightMap(); + map.setSize(size); int cutoff = onlyWhite ? maxY : 0; final AdjacentAnyMask adjacent = new AdjacentAnyMask(editSession, Arrays.asList(new BaseBlock(0))); final SolidBlockMask solid = new SolidBlockMask(editSession); + RegionMask region = new RegionMask(new CuboidRegion(position.subtract(size, size, size), position.add(size, size, size))); 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)) { + int dx = vector.getBlockX() - cx; + int dy = vector.getBlockY() - cy; + int dz = vector.getBlockZ() - cz; 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; @@ -57,7 +62,7 @@ public class StencilBrush extends HeightBrush { dz += dir.getBlockZ() * dy; } } - double raise = heightMap.getHeight(dx, dz); + double raise = map.getHeight(dx, dz); int val = (int) Math.ceil(raise * scale); if (val <= cutoff) { return true; diff --git a/core/src/main/java/com/boydti/fawe/object/brush/heightmap/AbstractDelegateHeightMap.java b/core/src/main/java/com/boydti/fawe/object/brush/heightmap/AbstractDelegateHeightMap.java new file mode 100644 index 00000000..3cb4a62b --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/object/brush/heightmap/AbstractDelegateHeightMap.java @@ -0,0 +1,20 @@ +package com.boydti.fawe.object.brush.heightmap; + +public class AbstractDelegateHeightMap implements HeightMap { + + private final HeightMap parent; + + public AbstractDelegateHeightMap(HeightMap parent) { + this.parent = parent; + } + + @Override + public double getHeight(int x, int z) { + return parent.getHeight(x, z); + } + + @Override + public void setSize(int size) { + parent.setSize(size); + } +} diff --git a/core/src/main/java/com/boydti/fawe/object/brush/heightmap/HeightMap.java b/core/src/main/java/com/boydti/fawe/object/brush/heightmap/HeightMap.java new file mode 100644 index 00000000..f2bb476c --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/object/brush/heightmap/HeightMap.java @@ -0,0 +1,132 @@ +package com.boydti.fawe.object.brush.heightmap; + +import com.boydti.fawe.object.PseudoRandom; +import com.boydti.fawe.util.MainUtil; +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.MaxChangedBlocksException; +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.WorldVector; +import com.sk89q.worldedit.function.mask.Mask; +import com.sk89q.worldedit.internal.LocalWorldAdapter; +import com.sk89q.worldedit.math.convolution.GaussianKernel; +import com.sk89q.worldedit.math.convolution.HeightMapFilter; +import com.sk89q.worldedit.regions.CuboidRegion; +import com.sk89q.worldedit.regions.Region; + +public interface HeightMap { + public double getHeight(int x, int z); + + public void setSize(int size); + + + default void perform(EditSession session, Mask mask, Vector pos, int size, int rotationMode, double yscale, boolean smooth, boolean towards) throws MaxChangedBlocksException { + int[] data = generateHeightData(session, mask, pos, size, rotationMode, yscale, smooth, towards); + applyHeightMapData(data, session, mask, pos, size, rotationMode, yscale, smooth, towards); + } + + default void applyHeightMapData(int[] data, EditSession session, Mask mask, Vector pos, int size, int rotationMode, double yscale, boolean smooth, boolean towards) throws MaxChangedBlocksException { + Vector top = session.getMaximumPoint(); + int maxY = top.getBlockY(); + int diameter = 2 * size + 1; + int iterations = 1; + WorldVector min = new WorldVector(LocalWorldAdapter.adapt(session.getWorld()), pos.subtract(size, maxY, size)); + Vector max = pos.add(size, maxY, size); + Region region = new CuboidRegion(session.getWorld(), min, max); + com.sk89q.worldedit.math.convolution.HeightMap heightMap = new com.sk89q.worldedit.math.convolution.HeightMap(session, region, false); + if (smooth) { + try { + HeightMapFilter filter = (HeightMapFilter) HeightMapFilter.class.getConstructors()[0].newInstance(GaussianKernel.class.getConstructors()[0].newInstance(5, 1)); + data = filter.filter(data, diameter, diameter); + } catch (Throwable e) { + MainUtil.handleError(e); + } + } + heightMap.apply(data); + } + + default int[] generateHeightData(EditSession session, Mask mask, Vector pos, int size, int rotationMode, double yscale, boolean smooth, boolean towards) { + Vector top = session.getMaximumPoint(); + int maxY = top.getBlockY(); + int diameter = 2 * size + 1; + int centerX = pos.getBlockX(); + int centerZ = pos.getBlockZ(); + int centerY = pos.getBlockY(); + int endY = pos.getBlockY() + size; + int startY = pos.getBlockY() - size; + int[] newData = new int[diameter * diameter]; + Vector mutablePos = new Vector(0, 0, 0); + if (towards) { + double sizePow = Math.pow(size, yscale); + int targetY = pos.getBlockY(); + for (int x = -size; x <= size; x++) { + int xx = centerX + x; + mutablePos.mutX(xx); + for (int z = -size; z <= size; z++) { + int index = (z + size) * diameter + (x + size); + int zz = centerZ + z; + double raise; + switch (rotationMode) { + default: + raise = getHeight(x, z); + break; + case 1: + raise = getHeight(z, x); + break; + case 2: + raise = getHeight(-x, -z); + break; + case 3: + raise = getHeight(-z, -x); + break; + } + int height = session.getNearestSurfaceTerrainBlock(xx, zz, pos.getBlockY(), 0, 255); + if (height == 0) { + newData[index] = centerY; + continue; + } + double raisePow = Math.pow(raise, yscale); + int diff = targetY - height; + double raiseScaled = diff * (raisePow / sizePow); + double raiseScaledAbs = Math.abs(raiseScaled); + int random = PseudoRandom.random.random(256) < (int) ((Math.ceil(raiseScaledAbs) - Math.floor(raiseScaledAbs)) * 256) ? (diff > 0 ? 1 : -1) : 0; + int raiseScaledInt = (int) raiseScaled + random; + newData[index] = height + raiseScaledInt; + } + } + } else { + for (int x = -size; x <= size; x++) { + int xx = centerX + x; + mutablePos.mutX(xx); + for (int z = -size; z <= size; z++) { + int index = (z + size) * diameter + (x + size); + int zz = centerZ + z; + double raise; + switch (rotationMode) { + default: + raise = getHeight(x, z); + break; + case 1: + raise = getHeight(z, x); + break; + case 2: + raise = getHeight(-x, -z); + break; + case 3: + raise = getHeight(-z, -x); + break; + } + int height = session.getNearestSurfaceTerrainBlock(xx, zz, pos.getBlockY(), 0, maxY); + if (height == 0) { + newData[index] = centerY; + continue; + } + raise = (yscale * raise); + int random = PseudoRandom.random.random(maxY + 1) < (int) ((raise - (int) raise) * (maxY + 1)) ? 1 : 0; + int newHeight = height + (int) raise + random; + newData[index] = newHeight; + } + } + } + return newData; + } +} diff --git a/core/src/main/java/com/boydti/fawe/object/brush/heightmap/RotatableHeightMap.java b/core/src/main/java/com/boydti/fawe/object/brush/heightmap/RotatableHeightMap.java new file mode 100644 index 00000000..d6f1fca6 --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/object/brush/heightmap/RotatableHeightMap.java @@ -0,0 +1,28 @@ +package com.boydti.fawe.object.brush.heightmap; + +import com.sk89q.worldedit.MutableBlockVector; +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.math.transform.AffineTransform; + +public class RotatableHeightMap extends AbstractDelegateHeightMap { + private AffineTransform transform; + private MutableBlockVector mutable; + + public RotatableHeightMap(HeightMap parent) { + super(parent); + mutable = new MutableBlockVector(); + this.transform = new AffineTransform(); + } + + public void rotate(double angle) { + this.transform = transform.rotateY(angle); + } + + @Override + public double getHeight(int x, int z) { + mutable.mutX(x); + mutable.mutZ(z); + Vector pos = transform.apply(mutable.setComponents(x, 0, z)); + return super.getHeight(pos.getBlockX(), pos.getBlockZ()); + } +} \ No newline at end of file diff --git a/core/src/main/java/com/boydti/fawe/object/brush/heightmap/ScalableHeightMap.java b/core/src/main/java/com/boydti/fawe/object/brush/heightmap/ScalableHeightMap.java index 11e9450b..d5a087f7 100644 --- a/core/src/main/java/com/boydti/fawe/object/brush/heightmap/ScalableHeightMap.java +++ b/core/src/main/java/com/boydti/fawe/object/brush/heightmap/ScalableHeightMap.java @@ -1,22 +1,11 @@ package com.boydti.fawe.object.brush.heightmap; import com.boydti.fawe.object.IntegerPair; -import com.boydti.fawe.object.PseudoRandom; -import com.boydti.fawe.util.MainUtil; import com.boydti.fawe.util.MathMan; import com.sk89q.worldedit.EditSession; -import com.sk89q.worldedit.MaxChangedBlocksException; import com.sk89q.worldedit.Vector; -import com.sk89q.worldedit.WorldVector; import com.sk89q.worldedit.blocks.BaseBlock; import com.sk89q.worldedit.extent.clipboard.Clipboard; -import com.sk89q.worldedit.function.mask.Mask; -import com.sk89q.worldedit.internal.LocalWorldAdapter; -import com.sk89q.worldedit.math.convolution.GaussianKernel; -import com.sk89q.worldedit.math.convolution.HeightMap; -import com.sk89q.worldedit.math.convolution.HeightMapFilter; -import com.sk89q.worldedit.regions.CuboidRegion; -import com.sk89q.worldedit.regions.Region; import java.awt.image.BufferedImage; import java.awt.image.Raster; import java.io.IOException; @@ -24,7 +13,7 @@ import java.io.InputStream; import java.util.HashSet; import javax.imageio.ImageIO; -public class ScalableHeightMap { +public class ScalableHeightMap implements com.boydti.fawe.object.brush.heightmap.HeightMap{ public int size2; public int size; @@ -41,11 +30,13 @@ public class ScalableHeightMap { setSize(size); } + @Override public void setSize(int size) { this.size = size; this.size2 = size * size; } + @Override public double getHeight(int x, int z) { int dx = Math.abs(x); int dz = Math.abs(z); @@ -117,115 +108,4 @@ public class ScalableHeightMap { } return new ArrayHeightMap(array); } - - public void perform(EditSession session, Mask mask, Vector pos, int size, int rotationMode, double yscale, boolean smooth, boolean towards) throws MaxChangedBlocksException { - int[] data = generateHeightData(session, mask, pos, size, rotationMode, yscale, smooth, towards); - applyHeightMapData(data, session, mask, pos, size, rotationMode, yscale, smooth, towards); - } - - public void applyHeightMapData(int[] data, EditSession session, Mask mask, Vector pos, int size, int rotationMode, double yscale, boolean smooth, boolean towards) throws MaxChangedBlocksException { - Vector top = session.getMaximumPoint(); - int maxY = top.getBlockY(); - int diameter = 2 * size + 1; - int iterations = 1; - WorldVector min = new WorldVector(LocalWorldAdapter.adapt(session.getWorld()), pos.subtract(size, maxY, size)); - Vector max = pos.add(size, maxY, size); - Region region = new CuboidRegion(session.getWorld(), min, max); - HeightMap heightMap = new HeightMap(session, region, false); - if (smooth) { - try { - HeightMapFilter filter = (HeightMapFilter) HeightMapFilter.class.getConstructors()[0].newInstance(GaussianKernel.class.getConstructors()[0].newInstance(5, 1)); - data = filter.filter(data, diameter, diameter); - } catch (Throwable e) { - MainUtil.handleError(e); - } - } - heightMap.apply(data); - } - - public int[] generateHeightData(EditSession session, Mask mask, Vector pos, int size, int rotationMode, double yscale, boolean smooth, boolean towards) { - Vector top = session.getMaximumPoint(); - int maxY = top.getBlockY(); - int diameter = 2 * size + 1; - int centerX = pos.getBlockX(); - int centerZ = pos.getBlockZ(); - int centerY = pos.getBlockY(); - int endY = pos.getBlockY() + size; - int startY = pos.getBlockY() - size; - int[] newData = new int[diameter * diameter]; - Vector mutablePos = new Vector(0, 0, 0); - if (towards) { - double sizePow = Math.pow(size, yscale); - int targetY = pos.getBlockY(); - for (int x = -size; x <= size; x++) { - int xx = centerX + x; - mutablePos.mutX(xx); - for (int z = -size; z <= size; z++) { - int index = (z + size) * diameter + (x + size); - int zz = centerZ + z; - double raise; - switch (rotationMode) { - default: - raise = getHeight(x, z); - break; - case 1: - raise = getHeight(z, x); - break; - case 2: - raise = getHeight(-x, -z); - break; - case 3: - raise = getHeight(-z, -x); - break; - } - int height = session.getNearestSurfaceTerrainBlock(xx, zz, pos.getBlockY(), 0, 255); - if (height == 0) { - newData[index] = centerY; - continue; - } - double raisePow = Math.pow(raise, yscale); - int diff = targetY - height; - double raiseScaled = diff * (raisePow / sizePow); - double raiseScaledAbs = Math.abs(raiseScaled); - int random = PseudoRandom.random.random(256) < (int) ((Math.ceil(raiseScaledAbs) - Math.floor(raiseScaledAbs)) * 256) ? (diff > 0 ? 1 : -1) : 0; - int raiseScaledInt = (int) raiseScaled + random; - newData[index] = height + raiseScaledInt; - } - } - } else { - for (int x = -size; x <= size; x++) { - int xx = centerX + x; - mutablePos.mutX(xx); - for (int z = -size; z <= size; z++) { - int index = (z + size) * diameter + (x + size); - int zz = centerZ + z; - double raise; - switch (rotationMode) { - default: - raise = getHeight(x, z); - break; - case 1: - raise = getHeight(z, x); - break; - case 2: - raise = getHeight(-x, -z); - break; - case 3: - raise = getHeight(-z, -x); - break; - } - int height = session.getNearestSurfaceTerrainBlock(xx, zz, pos.getBlockY(), 0, maxY); - if (height == 0) { - newData[index] = centerY; - continue; - } - raise = (yscale * raise); - int random = PseudoRandom.random.random(maxY + 1) < (int) ((raise - (int) raise) * (maxY + 1)) ? 1 : 0; - int newHeight = height + (int) raise + random; - newData[index] = newHeight; - } - } - } - return newData; - } } diff --git a/core/src/main/java/com/boydti/fawe/object/extent/OffsetExtent.java b/core/src/main/java/com/boydti/fawe/object/extent/OffsetExtent.java new file mode 100644 index 00000000..305ef5ff --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/object/extent/OffsetExtent.java @@ -0,0 +1,39 @@ +package com.boydti.fawe.object.extent; + +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.Vector2D; +import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.blocks.BaseBlock; +import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.world.biome.BaseBiome; + +public class OffsetExtent extends ResettableExtent { + private final int dx, dy, dz; + + public OffsetExtent(Extent parent, int dx, int dy, int dz) { + super(parent); + this.dx = dx; + this.dy = dy; + this.dz = dz; + } + + @Override + public boolean setBiome(Vector2D position, BaseBiome biome) { + return super.setBiome(new Vector2D(position.getBlockX() + dx, position.getBlockZ() + dz), biome); + } + + @Override + public boolean setBlock(Vector location, BaseBlock block) throws WorldEditException { + return super.setBlock(location.getBlockX() + dx, location.getBlockY() + dy, location.getBlockZ() + dz, block); + } + + @Override + public boolean setBlock(int x, int y, int z, BaseBlock block) throws WorldEditException { + return super.setBlock(x + dx, y + dy, z + dz, block); + } + + @Override + public ResettableExtent setExtent(Extent extent) { + return super.setExtent(extent); + } +} diff --git a/core/src/main/java/com/sk89q/worldedit/EditSession.java b/core/src/main/java/com/sk89q/worldedit/EditSession.java index 3c6907e4..f0006da2 100644 --- a/core/src/main/java/com/sk89q/worldedit/EditSession.java +++ b/core/src/main/java/com/sk89q/worldedit/EditSession.java @@ -1453,7 +1453,65 @@ public class EditSession extends AbstractWorld implements HasFaweQueue, Lighting checkArgument(depth >= 1, "depth >= 1"); final MaskIntersection mask = new MaskIntersection(new RegionMask(new EllipsoidRegion(null, origin, new Vector(radius, radius, radius))), new BoundedHeightMask(Math.max( - (origin.getBlockY() - depth) + 1, 0), Math.min(EditSession.this.getMaximumPoint().getBlockY(), origin.getBlockY())), Masks.negate(new ExistingBlockMask(EditSession.this))); + (origin.getBlockY() - depth) + 1, 0), Math.min(EditSession.this.getMaximumPoint().getBlockY(), origin.getBlockY())), Masks.negate(new ExistingBlockMask(EditSession.this))); + + // Want to replace blocks + final BlockReplace replace = new BlockReplace(EditSession.this, Patterns.wrap(pattern)); + + // Pick how we're going to visit blocks + RecursiveVisitor visitor; + if (recursive) { + visitor = new RecursiveVisitor(mask, replace, (int) (radius * 2 + 1), this); + } else { + visitor = new DownwardVisitor(mask, replace, origin.getBlockY(), (int) (radius * 2 + 1), this); + } + + // Start at the origin + visitor.visit(origin); + + // Execute + Operations.completeBlindly(visitor); + return this.changes = visitor.getAffected(); + } + + public int fillDirection(final Vector origin, PlayerDirection direction, final Pattern pattern, final double radius, final int depth, final boolean recursive) throws MaxChangedBlocksException { + checkNotNull(origin); + checkNotNull(pattern); + checkArgument(radius >= 0, "radius >= 0"); + checkArgument(depth >= 1, "depth >= 1"); + + Vector dirVec = direction.vector(); + BlockVector min = origin.toBlockVector(); + BlockVector max = origin.toBlockVector(); + + CuboidRegion cuboid = new CuboidRegion(new Vector(), new Vector()); + switch (direction) { + case NORTH: + break; + case NORTH_EAST: + break; + case EAST: + break; + case SOUTH_EAST: + break; + case SOUTH: + break; + case SOUTH_WEST: + break; + case WEST: + break; + case NORTH_WEST: + break; + case UP: + break; + case DOWN: + break; + } + + + + final MaskIntersection mask = new MaskIntersection(new RegionMask(new EllipsoidRegion(null, origin, new Vector(radius, radius, radius))), new BoundedHeightMask(Math.max( + (origin.getBlockY() - depth) + 1, 0), Math.min(EditSession.this.getMaximumPoint().getBlockY(), origin.getBlockY())), Masks.negate(new ExistingBlockMask(EditSession.this))); // Want to replace blocks final BlockReplace replace = new BlockReplace(EditSession.this, Patterns.wrap(pattern)); 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 5e3865cf..7f20a892 100644 --- a/core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java +++ b/core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java @@ -542,18 +542,22 @@ public class BrushCommands { max = -1 ) @CommandPermissions("worldedit.brush.stencil") - 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 { + 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, @Switch('r') boolean randomRotate) throws WorldEditException { worldEdit.checkMaxBrushRadius(radius); BrushTool tool = session.getBrushTool(player); InputStream stream = getHeightmapStream(filename); tool.setFill(fill); tool.setSize(radius); + HeightBrush brush; try { - tool.setBrush(new StencilBrush(stream, rotation, yscale, onlyWhite, filename.equalsIgnoreCase("#clipboard") ? session.getClipboard().getClipboard() : null), "worldedit.brush.height", player); + brush = new StencilBrush(stream, rotation, yscale, onlyWhite, filename.equalsIgnoreCase("#clipboard") ? session.getClipboard().getClipboard() : null); } catch (EmptyClipboardException ignore) { - tool.setBrush(new StencilBrush(stream, rotation, yscale, onlyWhite, null), "worldedit.brush.height", player); + brush = new StencilBrush(stream, rotation, yscale, onlyWhite, null); + } + tool.setBrush(brush, "worldedit.brush.height", player); + if (randomRotate) { + brush.setRandomRotate(true); } - player.print(BBC.getPrefix() + BBC.BRUSH_STENCIL.f(radius)); } @@ -695,8 +699,8 @@ public class BrushCommands { max = 4 ) @CommandPermissions("worldedit.brush.height") - public void heightBrush(Player player, LocalSession session, @Optional("5") double radius, @Optional("") final String filename, @Optional("0") final int rotation, @Optional("1") final double yscale) throws WorldEditException { - terrainBrush(player, session, radius, filename, rotation, yscale, false, ScalableHeightMap.Shape.CONE); + public void heightBrush(Player player, LocalSession session, @Optional("5") double radius, @Optional("") final String filename, @Optional("0") final int rotation, @Optional("1") final double yscale, @Switch('r') boolean randomRotate) throws WorldEditException { + terrainBrush(player, session, radius, filename, rotation, yscale, false, randomRotate, ScalableHeightMap.Shape.CONE); } @Command( @@ -710,8 +714,8 @@ public class BrushCommands { max = 4 ) @CommandPermissions("worldedit.brush.height") - public void cliffBrush(Player player, LocalSession session, @Optional("5") double radius, @Optional("") final String filename, @Optional("0") final int rotation, @Optional("1") final double yscale) throws WorldEditException { - terrainBrush(player, session, radius, filename, rotation, yscale, true, ScalableHeightMap.Shape.CYLINDER); + public void cliffBrush(Player player, LocalSession session, @Optional("5") double radius, @Optional("") final String filename, @Optional("0") final int rotation, @Optional("1") final double yscale, @Switch('r') boolean randomRotate) throws WorldEditException { + terrainBrush(player, session, radius, filename, rotation, yscale, true, randomRotate, ScalableHeightMap.Shape.CYLINDER); } @Command( @@ -725,8 +729,8 @@ public class BrushCommands { max = 4 ) @CommandPermissions("worldedit.brush.height") - public void flattenBrush(Player player, LocalSession session, @Optional("5") double radius, @Optional("") final String filename, @Optional("0") final int rotation, @Optional("1") final double yscale) throws WorldEditException { - terrainBrush(player, session, radius, filename, rotation, yscale, true, ScalableHeightMap.Shape.CONE); + public void flattenBrush(Player player, LocalSession session, @Optional("5") double radius, @Optional("") final String filename, @Optional("0") final int rotation, @Optional("1") final double yscale, @Switch('r') boolean randomRotate) throws WorldEditException { + terrainBrush(player, session, radius, filename, rotation, yscale, true, randomRotate, ScalableHeightMap.Shape.CONE); } private InputStream getHeightmapStream(String filename) { @@ -760,24 +764,29 @@ public class BrushCommands { return null; } - private void terrainBrush(Player player, LocalSession session, double radius, String filename, int rotation, double yscale, boolean flat, ScalableHeightMap.Shape shape) throws WorldEditException { + private void terrainBrush(Player player, LocalSession session, double radius, String filename, int rotation, double yscale, boolean flat, boolean randomRotate, ScalableHeightMap.Shape shape) throws WorldEditException { worldEdit.checkMaxBrushRadius(radius); InputStream stream = getHeightmapStream(filename); BrushTool tool = session.getBrushTool(player); tool.setSize(radius); + HeightBrush brush; if (flat) { try { - tool.setBrush(new FlattenBrush(stream, rotation, yscale, filename.equalsIgnoreCase("#clipboard") ? session.getClipboard().getClipboard() : null, shape), "worldedit.brush.height", player); + brush = new FlattenBrush(stream, rotation, yscale, filename.equalsIgnoreCase("#clipboard") ? session.getClipboard().getClipboard() : null, shape); } catch (EmptyClipboardException ignore) { - tool.setBrush(new FlattenBrush(stream, rotation, yscale, null, shape), "worldedit.brush.height", player); + brush = new FlattenBrush(stream, rotation, yscale, null, shape); } } else { try { - tool.setBrush(new HeightBrush(stream, rotation, yscale, filename.equalsIgnoreCase("#clipboard") ? session.getClipboard().getClipboard() : null), "worldedit.brush.height", player); + brush = new HeightBrush(stream, rotation, yscale, filename.equalsIgnoreCase("#clipboard") ? session.getClipboard().getClipboard() : null); } catch (EmptyClipboardException ignore) { - tool.setBrush(new HeightBrush(stream, rotation, yscale, null), "worldedit.brush.height", player); + brush = new HeightBrush(stream, rotation, yscale, null); } } + tool.setBrush(brush, "worldedit.brush.height", player); + if (randomRotate) { + brush.setRandomRotate(true); + } player.print(BBC.getPrefix() + BBC.BRUSH_HEIGHT.f(radius)); }