Changed ground seeking operations to LayerVisitor.

This commit is contained in:
sk89q 2014-03-29 18:51:33 -07:00
parent c804aeb03e
commit 3c5c257a41
7 changed files with 102 additions and 315 deletions

View File

@ -45,8 +45,6 @@ import com.sk89q.worldedit.masks.*;
import com.sk89q.worldedit.patterns.Pattern; import com.sk89q.worldedit.patterns.Pattern;
import com.sk89q.worldedit.patterns.SingleBlockPattern; import com.sk89q.worldedit.patterns.SingleBlockPattern;
import com.sk89q.worldedit.regions.*; import com.sk89q.worldedit.regions.*;
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.ArbitraryBiomeShape;
import com.sk89q.worldedit.shape.ArbitraryShape; import com.sk89q.worldedit.shape.ArbitraryShape;
import com.sk89q.worldedit.shape.RegionShape; import com.sk89q.worldedit.shape.RegionShape;
@ -58,6 +56,7 @@ import java.util.*;
import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
import static com.sk89q.worldedit.regions.Regions.asFlatRegion;
import static com.sk89q.worldedit.regions.Regions.maximumBlockY; import static com.sk89q.worldedit.regions.Regions.maximumBlockY;
import static com.sk89q.worldedit.regions.Regions.minimumBlockY; import static com.sk89q.worldedit.regions.Regions.minimumBlockY;
@ -1155,17 +1154,13 @@ public class EditSession {
checkNotNull(region); checkNotNull(region);
checkNotNull(pattern); checkNotNull(pattern);
int lowerY = region.getMinimumPoint().getBlockY();
int upperY = region.getMaximumPoint().getBlockY();
BlockReplace replace = new BlockReplace(this, pattern); BlockReplace replace = new BlockReplace(this, pattern);
RegionOffset offset = new RegionOffset(new Vector(0, 1, 0), replace); RegionOffset offset = new RegionOffset(new Vector(0, 1, 0), replace);
GroundSearch search = new MaskingGroundSearch(this, new ExistingBlockMask()); GroundFunction ground = new GroundFunction(this, offset);
GroundFunction groundFunction = new GroundFunction(search, lowerY, upperY, offset); LayerVisitor visitor = new LayerVisitor(
FlatRegionVisitor operation = new FlatRegionVisitor(Regions.asFlatRegion(region), groundFunction); this, asFlatRegion(region), minimumBlockY(region), maximumBlockY(region), ground);
OperationHelper.completeLegacy(operation); OperationHelper.completeLegacy(visitor);
return ground.getAffected();
return operation.getAffected();
} }
/** /**
@ -1181,7 +1176,8 @@ public class EditSession {
Naturalizer naturalizer = new Naturalizer(this); Naturalizer naturalizer = new Naturalizer(this);
FlatRegion flatRegion = Regions.asFlatRegion(region); FlatRegion flatRegion = Regions.asFlatRegion(region);
LayerVisitor visitor = new LayerVisitor(flatRegion, minimumBlockY(region), maximumBlockY(region), naturalizer); LayerVisitor visitor = new LayerVisitor(
this, flatRegion, minimumBlockY(region), maximumBlockY(region), naturalizer);
OperationHelper.completeLegacy(visitor); OperationHelper.completeLegacy(visitor);
return naturalizer.getAffected(); return naturalizer.getAffected();
} }
@ -1948,28 +1944,18 @@ public class EditSession {
generator.setPlant(GardenPatchGenerator.getPumpkinPattern()); generator.setPlant(GardenPatchGenerator.getPumpkinPattern());
// In a region of the given radius // In a region of the given radius
Region region = new CuboidRegion( FlatRegion region = new CuboidRegion(
getWorld(), // Causes clamping of Y range getWorld(), // Causes clamping of Y range
position.add(-apothem, -5, -apothem), position.add(-apothem, -5, -apothem),
position.add(apothem, 10, apothem)); position.add(apothem, 10, apothem));
int lowerY = region.getMinimumPoint().getBlockY();
int upperY = region.getMaximumPoint().getBlockY();
double density = 0.02; double density = 0.02;
// We want to find the ground GroundFunction ground = new GroundFunction(this, generator);
GroundSearch search = new MaskingGroundSearch(this, new ExistingBlockMask()); LayerVisitor visitor = new LayerVisitor(
GroundFunction groundFunction = new GroundFunction(search, lowerY, upperY, generator); this, region, minimumBlockY(region), maximumBlockY(region), ground);
visitor.setMask(new NoiseFilter2D(new RandomNoise(), density));
// We don't want to place a patch in every column OperationHelper.completeLegacy(visitor);
Mask2D mask = new NoiseFilter2D(new RandomNoise(), density); return ground.getAffected();
FlatRegionMaskingFilter filter = new FlatRegionMaskingFilter(this, mask, groundFunction);
// Generate those patches!
FlatRegionVisitor operation = new FlatRegionVisitor(Regions.asFlatRegion(region), filter);
OperationHelper.completeLegacy(operation);
return operation.getAffected();
} }
/** /**
@ -2018,40 +2004,6 @@ public class EditSession {
return affected; return affected;
} }
/**
* Makes a forest.
*
* @param it an iterator over the points within the region
* @param upperY the Y to start from (upperY >= lowerY), inclusive
* @param lowerY the Y to end at (upperY >= lowerY), inclusive
* @param density density of the forest
* @param treeGenerator the tree generator
* @return number of trees created
* @throws MaxChangedBlocksException
* @deprecated Use {@link com.sk89q.worldedit.function.generator.ForestGenerator} with a
* {@link com.sk89q.worldedit.function.visitor.FlatRegionVisitor}
*/
@Deprecated
public int makeForest(Iterable<Vector2D> it, int upperY, int lowerY,
double density, TreeGenerator treeGenerator)
throws WorldEditException {
ForestGenerator generator = new ForestGenerator(this, treeGenerator);
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 (filter.apply(pt)) {
affected++;
}
}
return affected;
}
/** /**
* Get the block distribution inside a region. * Get the block distribution inside a region.
* *

View File

@ -29,29 +29,28 @@ import com.sk89q.worldedit.blocks.BlockID;
import com.sk89q.worldedit.expression.ExpressionException; import com.sk89q.worldedit.expression.ExpressionException;
import com.sk89q.worldedit.filtering.GaussianKernel; import com.sk89q.worldedit.filtering.GaussianKernel;
import com.sk89q.worldedit.filtering.HeightMapFilter; import com.sk89q.worldedit.filtering.HeightMapFilter;
import com.sk89q.worldedit.function.GroundFunction;
import com.sk89q.worldedit.function.generator.FloraGenerator; import com.sk89q.worldedit.function.generator.FloraGenerator;
import com.sk89q.worldedit.function.generator.ForestGenerator; import com.sk89q.worldedit.function.generator.ForestGenerator;
import com.sk89q.worldedit.masks.ExistingBlockMask;
import com.sk89q.worldedit.masks.Mask2D;
import com.sk89q.worldedit.masks.NoiseFilter2D;
import com.sk89q.worldedit.function.FlatRegionMaskingFilter;
import com.sk89q.worldedit.function.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.function.visitor.FlatRegionVisitor;
import com.sk89q.worldedit.masks.Mask;
import com.sk89q.worldedit.function.operation.OperationHelper; import com.sk89q.worldedit.function.operation.OperationHelper;
import com.sk89q.worldedit.function.visitor.LayerVisitor;
import com.sk89q.worldedit.masks.Mask;
import com.sk89q.worldedit.masks.NoiseFilter2D;
import com.sk89q.worldedit.patterns.Pattern; import com.sk89q.worldedit.patterns.Pattern;
import com.sk89q.worldedit.patterns.SingleBlockPattern; import com.sk89q.worldedit.patterns.SingleBlockPattern;
import com.sk89q.worldedit.regions.*; import com.sk89q.worldedit.regions.ConvexPolyhedralRegion;
import com.sk89q.worldedit.regions.CuboidRegion;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.regions.RegionOperationException;
import com.sk89q.worldedit.util.TreeGenerator; import com.sk89q.worldedit.util.TreeGenerator;
import com.sk89q.worldedit.util.noise.RandomNoise;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import static com.sk89q.minecraft.util.commands.Logging.LogMode.*; import static com.sk89q.minecraft.util.commands.Logging.LogMode.*;
import static com.sk89q.worldedit.regions.Regions.*;
/** /**
* Region related commands. * Region related commands.
@ -551,20 +550,13 @@ public class RegionCommands {
Region region = session.getSelection(player.getWorld()); Region region = session.getSelection(player.getWorld());
ForestGenerator generator = new ForestGenerator(editSession, new TreeGenerator(type)); ForestGenerator generator = new ForestGenerator(editSession, new TreeGenerator(type));
GroundFunction ground = new GroundFunction(editSession, generator);
LayerVisitor visitor = new LayerVisitor(
editSession, asFlatRegion(region), minimumBlockY(region), maximumBlockY(region), ground);
visitor.setMask(new NoiseFilter2D(new RandomNoise(), density));
OperationHelper.completeLegacy(visitor);
int lowerY = region.getMinimumPoint().getBlockY(); player.print(ground.getAffected() + " trees created.");
int upperY = region.getMaximumPoint().getBlockY();
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(Regions.asFlatRegion(region), filter);
OperationHelper.complete(operation);
player.print(operation.getAffected() + " trees created.");
} }
@Command( @Command(
@ -581,20 +573,13 @@ public class RegionCommands {
Region region = session.getSelection(player.getWorld()); Region region = session.getSelection(player.getWorld());
FloraGenerator generator = new FloraGenerator(editSession); FloraGenerator generator = new FloraGenerator(editSession);
GroundFunction ground = new GroundFunction(editSession, generator);
LayerVisitor visitor = new LayerVisitor(
editSession, asFlatRegion(region), minimumBlockY(region), maximumBlockY(region), ground);
visitor.setMask(new NoiseFilter2D(new RandomNoise(), density));
OperationHelper.completeLegacy(visitor);
int lowerY = region.getMinimumPoint().getBlockY(); player.print(ground.getAffected() + " flora created.");
int upperY = region.getMaximumPoint().getBlockY();
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(Regions.asFlatRegion(region), filter);
OperationHelper.complete(operation);
player.print(operation.getAffected() + " flora created.");
} }
} }

View File

@ -19,46 +19,79 @@
package com.sk89q.worldedit.function; package com.sk89q.worldedit.function;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.Vector; import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.Vector2D;
import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.regions.search.GroundSearch; import com.sk89q.worldedit.masks.ExistingBlockMask;
import com.sk89q.worldedit.masks.Mask;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
/** /**
* Accepts 2D coordinates to columns, finds the first ground block in each * Applies a {@link RegionFunction} to the first ground block.
* column, and applies the given {@link RegionFunction} onto the ground blocks.
*/ */
public class GroundFunction implements FlatRegionFunction { public class GroundFunction implements LayerFunction {
private final EditSession editSession;
private final RegionFunction function; private final RegionFunction function;
private GroundSearch groundSearch; private Mask mask = new ExistingBlockMask();
private int minY; private int affected;
private int maxY;
/** /**
* Create a new instance. * Create a new ground function.
* *
* @param groundSearch the ground search implementation * @param editSession an edit session
* @param minY the minimum Y (inclusive) * @param function the function to apply
* @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) { public GroundFunction(EditSession editSession, RegionFunction function) {
checkNotNull(editSession);
checkNotNull(function); checkNotNull(function);
checkNotNull(groundSearch); this.editSession = editSession;
checkArgument(minY <= maxY, "minY <= maxY required");
this.function = function; this.function = function;
this.groundSearch = groundSearch; }
this.minY = minY;
this.maxY = maxY; /**
* Get the mask that determines what the ground consists of.
*
* @return a mask
*/
public Mask getMask() {
return mask;
}
/**
* Set the mask that determines what the ground consists of.
*
* @param mask a mask
*/
public void setMask(Mask mask) {
checkNotNull(mask);
this.mask = mask;
}
/**
* Get the number of affected objects.
*
* @return the number of affected
*/
public int getAffected() {
return affected;
} }
@Override @Override
public boolean apply(Vector2D pt) throws WorldEditException { public boolean isGround(Vector position) {
Vector ground = groundSearch.findGround(pt.toVector(maxY), minY); return mask.matches(editSession, position);
return ground != null && function.apply(ground);
} }
@Override
public boolean apply(Vector position, int depth) throws WorldEditException {
if (depth == 0) {
if (function.apply(position)) {
affected++;
}
}
return false;
}
} }

View File

@ -19,6 +19,7 @@
package com.sk89q.worldedit.function.visitor; package com.sk89q.worldedit.function.visitor;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.Vector; import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.Vector2D; import com.sk89q.worldedit.Vector2D;
import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.WorldEditException;
@ -41,6 +42,7 @@ import static com.google.common.base.Preconditions.checkNotNull;
*/ */
public class LayerVisitor implements Operation { public class LayerVisitor implements Operation {
private final EditSession editSession;
private final FlatRegion flatRegion; private final FlatRegion flatRegion;
private final LayerFunction function; private final LayerFunction function;
private Mask2D mask = new DummyMask2D(); private Mask2D mask = new DummyMask2D();
@ -55,11 +57,13 @@ public class LayerVisitor implements Operation {
* @param maxY the maximum Y to begin the search at * @param maxY the maximum Y to begin the search at
* @param function the layer function to apply t blocks * @param function the layer function to apply t blocks
*/ */
public LayerVisitor(FlatRegion flatRegion, int minY, int maxY, LayerFunction function) { public LayerVisitor(EditSession editSession, FlatRegion flatRegion, int minY, int maxY, LayerFunction function) {
checkNotNull(editSession);
checkNotNull(flatRegion); checkNotNull(flatRegion);
checkArgument(minY <= maxY, "minY <= maxY required"); checkArgument(minY <= maxY, "minY <= maxY required");
checkNotNull(function); checkNotNull(function);
this.editSession = editSession;
this.flatRegion = flatRegion; this.flatRegion = flatRegion;
this.minY = minY; this.minY = minY;
this.maxY = maxY; this.maxY = maxY;
@ -90,6 +94,10 @@ public class LayerVisitor implements Operation {
@Override @Override
public Operation resume() throws WorldEditException { public Operation resume() throws WorldEditException {
for (Vector2D column : flatRegion.asFlatRegion()) { for (Vector2D column : flatRegion.asFlatRegion()) {
if (!mask.matches(editSession, column)) {
continue;
}
// Abort if we are underground // Abort if we are underground
if (function.isGround(column.toVector(maxY + 1))) { if (function.isGround(column.toVector(maxY + 1))) {
return null; return null;

View File

@ -1,76 +0,0 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* 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 <http://www.gnu.org/licenses/>.
*/
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).
* </p>
* 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.
* </p>
* 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;
}
}

View File

@ -1,47 +0,0 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* 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 <http://www.gnu.org/licenses/>.
*/
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).
* </p>
* 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.
* </p>
* 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);
}

View File

@ -1,68 +0,0 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* 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 <http://www.gnu.org/licenses/>.
*/
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.
* </p>
* 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);
}
}