From 60b839ed09ffbeb0615c07eaac41b8a9ccf23c1e Mon Sep 17 00:00:00 2001
From: sk89q
Date: Fri, 28 Mar 2014 16:13:27 -0700
Subject: [PATCH] Changed how ground finding operations were implemented.
---
.../java/com/sk89q/worldedit/EditSession.java | 36 ++--
.../worldedit/commands/RegionCommands.java | 42 ++--
.../operation/FlatRegionMaskingFilter.java | 62 ++++++
.../operation/GroundFindingFunction.java | 188 ------------------
.../worldedit/operation/GroundFunction.java | 64 ++++++
.../operation/GroundScatterFunction.java | 128 ------------
...skFilter.java => RegionMaskingFilter.java} | 29 +--
.../regions/search/AbstractGroundSearch.java | 76 +++++++
.../regions/search/GroundSearch.java | 47 +++++
.../regions/search/MaskingGroundSearch.java | 68 +++++++
10 files changed, 382 insertions(+), 358 deletions(-)
create mode 100644 src/main/java/com/sk89q/worldedit/operation/FlatRegionMaskingFilter.java
delete mode 100644 src/main/java/com/sk89q/worldedit/operation/GroundFindingFunction.java
create mode 100644 src/main/java/com/sk89q/worldedit/operation/GroundFunction.java
delete mode 100644 src/main/java/com/sk89q/worldedit/operation/GroundScatterFunction.java
rename src/main/java/com/sk89q/worldedit/operation/{RegionMaskFilter.java => RegionMaskingFilter.java} (66%)
create mode 100644 src/main/java/com/sk89q/worldedit/regions/search/AbstractGroundSearch.java
create mode 100644 src/main/java/com/sk89q/worldedit/regions/search/GroundSearch.java
create mode 100644 src/main/java/com/sk89q/worldedit/regions/search/MaskingGroundSearch.java
diff --git a/src/main/java/com/sk89q/worldedit/EditSession.java b/src/main/java/com/sk89q/worldedit/EditSession.java
index 6c877da98..b1faffb4a 100644
--- a/src/main/java/com/sk89q/worldedit/EditSession.java
+++ b/src/main/java/com/sk89q/worldedit/EditSession.java
@@ -40,11 +40,14 @@ import com.sk89q.worldedit.regions.CuboidRegion;
import com.sk89q.worldedit.regions.EllipsoidRegion;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.regions.RegionOperationException;
+import com.sk89q.worldedit.regions.search.GroundSearch;
+import com.sk89q.worldedit.regions.search.MaskingGroundSearch;
import com.sk89q.worldedit.shape.ArbitraryBiomeShape;
import com.sk89q.worldedit.shape.ArbitraryShape;
import com.sk89q.worldedit.shape.RegionShape;
import com.sk89q.worldedit.shape.WorldEditExpressionEnvironment;
import com.sk89q.worldedit.util.TreeGenerator;
+import com.sk89q.worldedit.util.noise.RandomNoise;
import com.sk89q.worldedit.visitor.DownwardVisitor;
import com.sk89q.worldedit.visitor.FlatRegionVisitor;
import com.sk89q.worldedit.visitor.RecursiveVisitor;
@@ -552,7 +555,7 @@ public class EditSession {
public int countBlocks(Region region, Set searchBlocks) {
FuzzyBlockMask mask = new FuzzyBlockMask(searchBlocks);
BlockCount count = new BlockCount();
- RegionMaskFilter filter = new RegionMaskFilter(this, mask, count);
+ RegionMaskingFilter filter = new RegionMaskingFilter(this, mask, count);
RegionVisitor visitor = new RegionVisitor(region, filter);
OperationHelper.completeBlindly(visitor); // We can't throw exceptions, nor do we expect any
return count.getCount();
@@ -977,7 +980,7 @@ public class EditSession {
checkNotNull(pattern);
BlockReplace replace = new BlockReplace(this, pattern);
- RegionMaskFilter filter = new RegionMaskFilter(this, mask, replace);
+ RegionMaskingFilter filter = new RegionMaskingFilter(this, mask, replace);
RegionVisitor visitor = new RegionVisitor(region, filter);
OperationHelper.completeLegacy(visitor);
return visitor.getAffected();
@@ -2045,13 +2048,20 @@ public class EditSession {
position.add(-apothem, -5, -apothem),
position.add(apothem, 10, apothem));
- // And we want to scatter them
- GroundScatterFunction scatter = new GroundScatterFunction(this, generator);
- scatter.setDensity(0.02);
- scatter.setRange(region);
+ int lowerY = region.getMinimumPoint().getBlockY();
+ int upperY = region.getMaximumPoint().getBlockY();
+ double density = 0.02;
+
+ // We want to find the ground
+ GroundSearch search = new MaskingGroundSearch(this, new ExistingBlockMask());
+ GroundFunction groundFunction = new GroundFunction(search, lowerY, upperY, generator);
+
+ // We don't want to place a patch in every column
+ Mask2D mask = new NoiseFilter2D(new RandomNoise(), density);
+ FlatRegionMaskingFilter filter = new FlatRegionMaskingFilter(this, mask, groundFunction);
// Generate those patches!
- FlatRegionVisitor operation = new FlatRegionVisitor(region, scatter);
+ FlatRegionVisitor operation = new FlatRegionVisitor(region, filter);
OperationHelper.completeLegacy(operation);
return operation.getAffected();
@@ -2122,16 +2132,14 @@ public class EditSession {
throws WorldEditException {
ForestGenerator generator = new ForestGenerator(this, treeGenerator);
-
- // And we want to scatter them
- GroundScatterFunction scatter = new GroundScatterFunction(this, generator);
- scatter.setDensity(density);
- scatter.setRange(lowerY, upperY);
+ GroundSearch search = new MaskingGroundSearch(this, new ExistingBlockMask());
+ GroundFunction groundFunction = new GroundFunction(search, lowerY, upperY, generator);
+ Mask2D mask = new NoiseFilter2D(new RandomNoise(), density);
+ FlatRegionMaskingFilter filter = new FlatRegionMaskingFilter(this, mask, groundFunction);
int affected = 0;
-
for (Vector2D pt : it) {
- if (scatter.apply(pt)) {
+ if (filter.apply(pt)) {
affected++;
}
}
diff --git a/src/main/java/com/sk89q/worldedit/commands/RegionCommands.java b/src/main/java/com/sk89q/worldedit/commands/RegionCommands.java
index b3d79cfb9..ae765a52f 100644
--- a/src/main/java/com/sk89q/worldedit/commands/RegionCommands.java
+++ b/src/main/java/com/sk89q/worldedit/commands/RegionCommands.java
@@ -31,8 +31,15 @@ import com.sk89q.worldedit.filtering.GaussianKernel;
import com.sk89q.worldedit.filtering.HeightMapFilter;
import com.sk89q.worldedit.generator.FloraGenerator;
import com.sk89q.worldedit.generator.ForestGenerator;
+import com.sk89q.worldedit.masks.ExistingBlockMask;
+import com.sk89q.worldedit.masks.Mask2D;
+import com.sk89q.worldedit.masks.NoiseFilter2D;
+import com.sk89q.worldedit.operation.FlatRegionMaskingFilter;
+import com.sk89q.worldedit.operation.GroundFunction;
+import com.sk89q.worldedit.regions.search.GroundSearch;
+import com.sk89q.worldedit.regions.search.MaskingGroundSearch;
+import com.sk89q.worldedit.util.noise.RandomNoise;
import com.sk89q.worldedit.visitor.FlatRegionVisitor;
-import com.sk89q.worldedit.operation.GroundScatterFunction;
import com.sk89q.worldedit.masks.Mask;
import com.sk89q.worldedit.operation.OperationHelper;
import com.sk89q.worldedit.patterns.Pattern;
@@ -543,16 +550,18 @@ public class RegionCommands {
Region region = session.getSelection(player.getWorld());
- // We want to generate trees
ForestGenerator generator = new ForestGenerator(editSession, new TreeGenerator(type));
- // And we want to scatter them
- GroundScatterFunction scatter = new GroundScatterFunction(editSession, generator);
- scatter.setDensity(density);
- scatter.setRange(region);
+ int lowerY = region.getMinimumPoint().getBlockY();
+ int upperY = region.getMaximumPoint().getBlockY();
- // Generate that forest
- FlatRegionVisitor operation = new FlatRegionVisitor(region, scatter);
+ GroundSearch search = new MaskingGroundSearch(editSession, new ExistingBlockMask());
+ GroundFunction groundFunction = new GroundFunction(search, lowerY, upperY, generator);
+ Mask2D mask = new NoiseFilter2D(new RandomNoise(), density);
+ FlatRegionMaskingFilter filter = new FlatRegionMaskingFilter(editSession, mask, groundFunction);
+
+ // Execute
+ FlatRegionVisitor operation = new FlatRegionVisitor(region, filter);
OperationHelper.complete(operation);
player.print(operation.getAffected() + " trees created.");
@@ -571,17 +580,18 @@ public class RegionCommands {
double density = args.argsLength() > 0 ? args.getDouble(0) / 100 : 0.1;
Region region = session.getSelection(player.getWorld());
-
- // We want to generate flora
FloraGenerator generator = new FloraGenerator(editSession);
- // And we want to scatter them
- GroundScatterFunction scatter = new GroundScatterFunction(editSession, generator);
- scatter.setDensity(density);
- scatter.setRange(region);
+ int lowerY = region.getMinimumPoint().getBlockY();
+ int upperY = region.getMaximumPoint().getBlockY();
- // Generate that flora
- FlatRegionVisitor operation = new FlatRegionVisitor(region, scatter);
+ GroundSearch search = new MaskingGroundSearch(editSession, new ExistingBlockMask());
+ GroundFunction groundFunction = new GroundFunction(search, lowerY, upperY, generator);
+ Mask2D mask = new NoiseFilter2D(new RandomNoise(), density);
+ FlatRegionMaskingFilter filter = new FlatRegionMaskingFilter(editSession, mask, groundFunction);
+
+ // Execute
+ FlatRegionVisitor operation = new FlatRegionVisitor(region, filter);
OperationHelper.complete(operation);
player.print(operation.getAffected() + " flora created.");
diff --git a/src/main/java/com/sk89q/worldedit/operation/FlatRegionMaskingFilter.java b/src/main/java/com/sk89q/worldedit/operation/FlatRegionMaskingFilter.java
new file mode 100644
index 000000000..7a6abb656
--- /dev/null
+++ b/src/main/java/com/sk89q/worldedit/operation/FlatRegionMaskingFilter.java
@@ -0,0 +1,62 @@
+/*
+ * WorldEdit, a Minecraft world manipulation toolkit
+ * Copyright (C) sk89q
+ * Copyright (C) WorldEdit team and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.sk89q.worldedit.operation;
+
+import com.sk89q.worldedit.EditSession;
+import com.sk89q.worldedit.Vector2D;
+import com.sk89q.worldedit.WorldEditException;
+import com.sk89q.worldedit.masks.Mask2D;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Passes calls to {@link #apply(com.sk89q.worldedit.Vector2D)} to the
+ * delegate {@link com.sk89q.worldedit.operation.FlatRegionFunction} if they
+ * match the given mask.
+ */
+public class FlatRegionMaskingFilter implements FlatRegionFunction {
+
+ private final EditSession editSession;
+ private final FlatRegionFunction function;
+ private Mask2D mask;
+
+ /**
+ * Create a new masking filter.
+ *
+ * @param editSession the edit session
+ * @param mask the mask
+ * @param function the delegate function to call
+ */
+ public FlatRegionMaskingFilter(EditSession editSession, Mask2D mask, FlatRegionFunction function) {
+ checkNotNull(function);
+ checkNotNull(editSession);
+ checkNotNull(mask);
+
+ this.editSession = editSession;
+ this.mask = mask;
+ this.function = function;
+ }
+
+ @Override
+ public boolean apply(Vector2D position) throws WorldEditException {
+ return mask.matches(editSession, position) && function.apply(position);
+ }
+
+}
diff --git a/src/main/java/com/sk89q/worldedit/operation/GroundFindingFunction.java b/src/main/java/com/sk89q/worldedit/operation/GroundFindingFunction.java
deleted file mode 100644
index 63f0f7715..000000000
--- a/src/main/java/com/sk89q/worldedit/operation/GroundFindingFunction.java
+++ /dev/null
@@ -1,188 +0,0 @@
-/*
- * WorldEdit, a Minecraft world manipulation toolkit
- * Copyright (C) sk89q
- * Copyright (C) WorldEdit team and contributors
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-package com.sk89q.worldedit.operation;
-
-import com.sk89q.worldedit.EditSession;
-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.blocks.BlockID;
-import com.sk89q.worldedit.regions.Region;
-
-/**
- * An abstract implementation of {@link com.sk89q.worldedit.operation.FlatRegionFunction}
- * that searches for the first "ground" block." A ground block is found when the
- * method {@link #shouldPassThrough(Vector, BaseBlock)} returns false, which, by default,
- * does so for all non-air blocks.
- *
- * This function starts from the provided upperY in each block column and traverses
- * down the column until it finds the first ground block, at which point
- * {@link #apply(Vector, BaseBlock)} is called with the position and the
- * {@link BaseBlock} for the found ground block. Implementations that want
- * to skip certain columns (and avoid the ground search) can override
- * {@link #shouldContinue(com.sk89q.worldedit.Vector2D)} and return true as necessary.
- */
-public abstract class GroundFindingFunction implements FlatRegionFunction {
-
- private final EditSession editSession;
- private int lowerY;
- private int upperY;
-
- /**
- * Create a new instance.
- *
- * @param editSession the edit session
- */
- protected GroundFindingFunction(EditSession editSession) {
- this.editSession = editSession;
- checkYBounds();
- }
-
- /**
- * Check whether upperY is >= lowerY.
- */
- private void checkYBounds() {
- if (upperY < lowerY) {
- throw new IllegalArgumentException("upperY must be greater than or equal to lowerY");
- }
- }
-
- /**
- * Get the upper Y coordinate to start the ground search from.
- *
- * @return the upper Y coordinate
- */
- public int getUpperY() {
- return upperY;
- }
-
- /**
- * Get the lower Y coordinate to end the ground search at.
- *
- * @return lowerY the lower Y coordinate
- */
- public int getLowerY() {
- return lowerY;
- }
-
- /**
- * Set the range of Y coordinates to perform a search for ground within.
- *
- * @param lowerY the lower Y coordinate
- * @param upperY the upper Y coordinate (upperY >= lowerY)
- */
- public void setRange(int lowerY, int upperY) {
- this.lowerY = lowerY;
- this.upperY = upperY;
- checkYBounds();
- }
-
- /**
- * Set the range of Y coordinates to perform a search for ground within from
- * the minimum and maximum Y of the given region.
- *
- * @param region the region
- */
- public void setRange(Region region) {
- setRange(region.getMinimumPoint().getBlockY(), region.getMaximumPoint().getBlockY());
- }
-
- /**
- * Increase the upper Y by the given amount.
- *
- * @param y the amount to increase the upper Y by
- */
- public void raiseCeiling(int y) {
- if (y <= 0) {
- throw new IllegalArgumentException("Can't raise by a negative");
- }
- upperY += y;
- }
-
- @Override
- public final boolean apply(Vector2D pt) throws WorldEditException {
- // Don't want to be in the ground
- if (!editSession.getBlock(pt.toVector(upperY + 1)).isAir()) {
- return false;
- }
-
- if (!shouldContinue(pt)) {
- return false;
- }
-
- for (int y = upperY + 1; y >= lowerY; --y) {
- Vector testPt = pt.toVector(y);
- BaseBlock block = editSession.getBlock(testPt);
-
- if (!shouldPassThrough(testPt, block)) {
- return apply(testPt, block);
- }
- }
-
- return false;
- }
-
- /**
- * Returns whether a search for the ground should be performed for the given
- * column. Return false if the column is to be skipped.
- *
- * @param pt the point
- * @return true if the search should begin
- */
- protected boolean shouldContinue(Vector2D pt) {
- return true;
- }
-
- /**
- * Returns whether the given block should be "passed through" when
- * conducting the ground search.
- *
- * Examples of blocks where this method could return true include snow, tall
- * grass, shrubs, and flowers. Note that this method will also receive
- * calls on each air block so that condition must be handled. Be aware
- * that blocks passed through are not automatically removed
- * from the world, so implementations may need to remove the block
- * immediately above the ground.
- *
- * The default implementation only returns true for air blocks.
- *
- * @param position the position
- * @param block the block
- * @return true if the block should be passed through during the ground search
- */
- protected boolean shouldPassThrough(Vector position, BaseBlock block) {
- return block.getType() == BlockID.AIR;
- }
-
- /**
- * Apply the function to the given ground block.
- *
- * Naive implementations may provide flowers, tall grass, and other
- * blocks not often considered to be the ground as the ground block.
- *
- * @param position the position
- * @param block the block
- * @return true if something was changed
- * @throws WorldEditException thrown on an error
- */
- protected abstract boolean apply(Vector position, BaseBlock block) throws WorldEditException;
-
-}
diff --git a/src/main/java/com/sk89q/worldedit/operation/GroundFunction.java b/src/main/java/com/sk89q/worldedit/operation/GroundFunction.java
new file mode 100644
index 000000000..78e90e209
--- /dev/null
+++ b/src/main/java/com/sk89q/worldedit/operation/GroundFunction.java
@@ -0,0 +1,64 @@
+/*
+ * WorldEdit, a Minecraft world manipulation toolkit
+ * Copyright (C) sk89q
+ * Copyright (C) WorldEdit team and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.sk89q.worldedit.operation;
+
+import com.sk89q.worldedit.Vector;
+import com.sk89q.worldedit.Vector2D;
+import com.sk89q.worldedit.WorldEditException;
+import com.sk89q.worldedit.regions.search.GroundSearch;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Accepts 2D coordinates to columns, finds the first ground block in each
+ * column, and applies the given {@link RegionFunction} onto the ground blocks.
+ */
+public class GroundFunction implements FlatRegionFunction {
+
+ private final RegionFunction function;
+ private GroundSearch groundSearch;
+ private int minY;
+ private int maxY;
+
+ /**
+ * Create a new instance.
+ *
+ * @param groundSearch the ground search implementation
+ * @param minY the minimum Y (inclusive)
+ * @param maxY the maximum Y (inclusive)
+ * @param function the function to apply on ground blocks
+ */
+ public GroundFunction(GroundSearch groundSearch, int minY, int maxY, RegionFunction function) {
+ checkNotNull(function);
+ checkNotNull(groundSearch);
+ checkArgument(minY <= maxY, "minY <= maxY required");
+ this.function = function;
+ this.groundSearch = groundSearch;
+ this.minY = minY;
+ this.maxY = maxY;
+ }
+
+ @Override
+ public boolean apply(Vector2D pt) throws WorldEditException {
+ Vector ground = groundSearch.findGround(pt.toVector(maxY), minY);
+ return ground != null && function.apply(ground);
+ }
+}
diff --git a/src/main/java/com/sk89q/worldedit/operation/GroundScatterFunction.java b/src/main/java/com/sk89q/worldedit/operation/GroundScatterFunction.java
deleted file mode 100644
index 474d70214..000000000
--- a/src/main/java/com/sk89q/worldedit/operation/GroundScatterFunction.java
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * WorldEdit, a Minecraft world manipulation toolkit
- * Copyright (C) sk89q
- * Copyright (C) WorldEdit team and contributors
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-package com.sk89q.worldedit.operation;
-
-import com.sk89q.worldedit.EditSession;
-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.noise.NoiseGenerator;
-import com.sk89q.worldedit.noise.RandomNoise;
-
-/**
- * Randomly applies the given {@link RegionFunction} onto random ground blocks.
- *
- * This class can be used to generate a structure randomly over an area.
- */
-public class GroundScatterFunction extends GroundFindingFunction {
-
- private NoiseGenerator noiseGenerator;
- private RegionFunction function;
- private double density;
-
- /**
- * Create a new instance using a {@link RandomNoise} as the noise generator.
- *
- * @param editSession the edit session
- * @param function the function
- */
- public GroundScatterFunction(EditSession editSession, RegionFunction function) {
- this(editSession, function, new RandomNoise());
- }
-
- /**
- * Create a new instance.
- *
- * @param editSession the edit session
- * @param function the function
- */
- public GroundScatterFunction(EditSession editSession, RegionFunction function, NoiseGenerator noiseGenerator) {
- super(editSession);
- this.function = function;
- this.noiseGenerator = noiseGenerator;
- }
-
- /**
- * Get the density (0 <= density <= 1) which indicates the threshold that
- * must be met for the function to be applied to a column.
- *
- * @return the density
- */
- public double getDensity() {
- return density;
- }
-
- /**
- * Set the density (0 <= density <= 1) which indicates the threshold that
- * must be met for the function to be applied to a column.
- *
- * @param density the density
- */
- public void setDensity(double density) {
- this.density = density;
- }
-
- /**
- * Get the noise generator.
- *
- * @return the noise generator
- */
- public NoiseGenerator getNoiseGenerator() {
- return noiseGenerator;
- }
-
- /**
- * Set the noise generator.
- *
- * @param noiseGenerator the noise generator
- */
- public void setNoiseGenerator(NoiseGenerator noiseGenerator) {
- this.noiseGenerator = noiseGenerator;
- }
-
- /**
- * Get the function to apply.
- *
- * @return the region function
- */
- public RegionFunction getFunction() {
- return function;
- }
-
- /**
- * Set the function to apply.
- *
- * @param function the region function
- */
- public void setFunction(RegionFunction function) {
- this.function = function;
- }
-
- @Override
- protected boolean shouldContinue(Vector2D pt) {
- return noiseGenerator.noise(pt) <= density;
- }
-
- @Override
- protected boolean apply(Vector position, BaseBlock block) throws WorldEditException {
- return function.apply(position);
- }
-}
diff --git a/src/main/java/com/sk89q/worldedit/operation/RegionMaskFilter.java b/src/main/java/com/sk89q/worldedit/operation/RegionMaskingFilter.java
similarity index 66%
rename from src/main/java/com/sk89q/worldedit/operation/RegionMaskFilter.java
rename to src/main/java/com/sk89q/worldedit/operation/RegionMaskingFilter.java
index 6a8640401..072f46be7 100644
--- a/src/main/java/com/sk89q/worldedit/operation/RegionMaskFilter.java
+++ b/src/main/java/com/sk89q/worldedit/operation/RegionMaskingFilter.java
@@ -27,18 +27,27 @@ import com.sk89q.worldedit.masks.Mask;
import static com.google.common.base.Preconditions.checkNotNull;
/**
- * Passes positions that match the given mask onto the provided function.
+ * Passes calls to {@link #apply(com.sk89q.worldedit.Vector)} to the
+ * delegate {@link com.sk89q.worldedit.operation.RegionFunction} if they
+ * match the given mask.
*/
-public class RegionMaskFilter implements RegionFunction {
+public class RegionMaskingFilter implements RegionFunction {
private final EditSession editSession;
- private final Mask mask;
private final RegionFunction function;
+ private Mask mask;
- public RegionMaskFilter(EditSession editSession, Mask mask, RegionFunction function) {
- checkNotNull(function, "function must not be null");
- checkNotNull(editSession, "editSession must not be null");
- checkNotNull(mask, "mask must not be null");
+ /**
+ * Create a new masking filter.
+ *
+ * @param editSession the edit session
+ * @param mask the mask
+ * @param function the function
+ */
+ public RegionMaskingFilter(EditSession editSession, Mask mask, RegionFunction function) {
+ checkNotNull(function);
+ checkNotNull(editSession);
+ checkNotNull(mask);
this.editSession = editSession;
this.mask = mask;
@@ -47,11 +56,7 @@ public class RegionMaskFilter implements RegionFunction {
@Override
public boolean apply(Vector position) throws WorldEditException {
- if (mask.matches(editSession, position)) {
- return function.apply(position);
- } else {
- return false;
- }
+ return mask.matches(editSession, position) && function.apply(position);
}
}
diff --git a/src/main/java/com/sk89q/worldedit/regions/search/AbstractGroundSearch.java b/src/main/java/com/sk89q/worldedit/regions/search/AbstractGroundSearch.java
new file mode 100644
index 000000000..94ca881b9
--- /dev/null
+++ b/src/main/java/com/sk89q/worldedit/regions/search/AbstractGroundSearch.java
@@ -0,0 +1,76 @@
+/*
+ * WorldEdit, a Minecraft world manipulation toolkit
+ * Copyright (C) sk89q
+ * Copyright (C) WorldEdit team and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.sk89q.worldedit.regions.search;
+
+import com.sk89q.worldedit.Vector;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static net.minecraft.util.com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Utility class for finding the first ground block starting from a certain
+ * position and traversing down.
+ */
+public abstract class AbstractGroundSearch implements GroundSearch {
+
+ /**
+ * Returns whether the given block should be "passed through" when
+ * conducting the ground search.
+ *
+ * @param position return whether the given block is the ground
+ * @return true if the search should stop
+ */
+ protected abstract boolean isGround(Vector position);
+
+ /**
+ * Find the ground block starting from the given position and traversing
+ * downward until reaching minY (inclusive).
+ *
+ * The highest ground block that may be returned is at the location of
+ * the origin location. The lowest ground block that may be returned is
+ * in the same column as the origin but with y = minY.
+ *
+ * It is possible for no ground block to be found if the given origin
+ * block is underground to begin with.
+ *
+ * @param origin the origin
+ * @param minY the minimum Y to end the search at
+ * @return the location of a ground block, or null if none was found
+ */
+ public Vector findGround(Vector origin, int minY) {
+ checkNotNull(origin);
+ checkArgument(minY <= origin.getBlockY(), "minY <= origin Y");
+
+ // Don't want to be in the ground
+ if (isGround(origin.add(0, 1, 0))) {
+ return null;
+ }
+
+ for (int y = origin.getBlockY() + 1; y >= minY; --y) {
+ Vector test = origin.setY(y);
+ if (isGround(test)) {
+ return test;
+ }
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/main/java/com/sk89q/worldedit/regions/search/GroundSearch.java b/src/main/java/com/sk89q/worldedit/regions/search/GroundSearch.java
new file mode 100644
index 000000000..0f0aa3ada
--- /dev/null
+++ b/src/main/java/com/sk89q/worldedit/regions/search/GroundSearch.java
@@ -0,0 +1,47 @@
+/*
+ * WorldEdit, a Minecraft world manipulation toolkit
+ * Copyright (C) sk89q
+ * Copyright (C) WorldEdit team and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.sk89q.worldedit.regions.search;
+
+import com.sk89q.worldedit.Vector;
+
+/**
+ * Given a column of blocks in the world, finds the first ground block
+ * starting from a given Y.
+ */
+public interface GroundSearch {
+
+ /**
+ * Find the ground block starting from the given position and traversing
+ * downward until reaching minY (inclusive).
+ *
+ * The highest ground block that may be returned is at the location of
+ * the origin location. The lowest ground block that may be returned is
+ * in the same column as the origin but with y = minY.
+ *
+ * It is possible for no ground block to be found if the given origin
+ * block is underground.
+ *
+ * @param origin the origin
+ * @param minY the minimum Y to end the search at
+ * @return the location of a ground block, or null if none was found
+ */
+ Vector findGround(Vector origin, int minY);
+
+}
diff --git a/src/main/java/com/sk89q/worldedit/regions/search/MaskingGroundSearch.java b/src/main/java/com/sk89q/worldedit/regions/search/MaskingGroundSearch.java
new file mode 100644
index 000000000..27c196c22
--- /dev/null
+++ b/src/main/java/com/sk89q/worldedit/regions/search/MaskingGroundSearch.java
@@ -0,0 +1,68 @@
+/*
+ * WorldEdit, a Minecraft world manipulation toolkit
+ * Copyright (C) sk89q
+ * Copyright (C) WorldEdit team and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.sk89q.worldedit.regions.search;
+
+import com.sk89q.worldedit.EditSession;
+import com.sk89q.worldedit.Vector;
+import com.sk89q.worldedit.masks.Mask;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * A ground finder that uses a {@link com.sk89q.worldedit.masks.Mask} to determine
+ * ground blocks.
+ */
+public class MaskingGroundSearch extends AbstractGroundSearch {
+
+ private final EditSession editSession;
+ private final Mask mask;
+
+ /**
+ * Create a new instance.
+ *
+ * If a mask that matches non-ground blocks is available, it can be inverted with
+ * {@link com.sk89q.worldedit.masks.InvertedMask}.
+ *
+ * @param editSession an edit session
+ * @param mask a mask that matches ground blocks
+ */
+ public MaskingGroundSearch(EditSession editSession, Mask mask) {
+ checkNotNull(editSession);
+ checkNotNull(mask);
+
+ this.editSession = editSession;
+ this.mask = mask;
+ }
+
+ /**
+ * Get the mask that matches ground blocks.
+ *
+ * @return the mask
+ */
+ public Mask getMask() {
+ return mask;
+ }
+
+ @Override
+ protected boolean isGround(Vector position) {
+ return mask.matches(editSession, position);
+ }
+
+}