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