From 46634b1b26efc13a47d874f1dc4b50711ba6daa0 Mon Sep 17 00:00:00 2001 From: Jesse Boyd Date: Tue, 6 Feb 2018 18:02:01 +1100 Subject: [PATCH] Fix CylinderRegion minY/maxY excedding world --- core/src/main/java/com/boydti/fawe/Fawe.java | 4 + .../main/java/com/boydti/fawe/config/BBC.java | 2 +- .../boydti/fawe/object/mask/CachedMask.java | 1 + .../java/com/sk89q/worldedit/EditSession.java | 2 +- .../worldedit/regions/CylinderRegion.java | 414 ++++++++++++++++++ 5 files changed, 421 insertions(+), 2 deletions(-) create mode 100644 core/src/main/java/com/sk89q/worldedit/regions/CylinderRegion.java diff --git a/core/src/main/java/com/boydti/fawe/Fawe.java b/core/src/main/java/com/boydti/fawe/Fawe.java index 93bfe4ae..646cdcf9 100644 --- a/core/src/main/java/com/boydti/fawe/Fawe.java +++ b/core/src/main/java/com/boydti/fawe/Fawe.java @@ -128,6 +128,8 @@ import com.sk89q.worldedit.math.convolution.HeightMap; import com.sk89q.worldedit.math.interpolation.KochanekBartelsInterpolation; import com.sk89q.worldedit.math.transform.AffineTransform; import com.sk89q.worldedit.regions.CuboidRegion; +import com.sk89q.worldedit.regions.CylinderRegion; +import com.sk89q.worldedit.regions.EllipsoidRegion; import com.sk89q.worldedit.regions.selector.ConvexPolyhedralRegionSelector; import com.sk89q.worldedit.regions.selector.CuboidRegionSelector; import com.sk89q.worldedit.regions.selector.CylinderRegionSelector; @@ -599,6 +601,8 @@ public class Fawe { ClipboardHolder.inject(); // Closeable // Regions CuboidRegion.inject(); // Optimizations + CylinderRegion.inject(); // Optimizations + EllipsoidRegion.inject(); // Optimizations // Extents MaskingExtent.inject(); // Features BlockTransformExtent.inject(); // Fix for cache not being mutable diff --git a/core/src/main/java/com/boydti/fawe/config/BBC.java b/core/src/main/java/com/boydti/fawe/config/BBC.java index 13b1b61a..3ef2e24c 100644 --- a/core/src/main/java/com/boydti/fawe/config/BBC.java +++ b/core/src/main/java/com/boydti/fawe/config/BBC.java @@ -257,7 +257,7 @@ public enum BBC { WORLDEDIT_CANCEL_REASON_MAX_TILES("Too many blockstates", "Cancel"), WORLDEDIT_CANCEL_REASON_MAX_ENTITIES("Too many entities", "Cancel"), WORLDEDIT_CANCEL_REASON_MAX_ITERATIONS("Max iterations", "Cancel"), - WORLDEDIT_CANCEL_REASON_OUTSIDE_WORLD("Cancel", "Outside world"), + WORLDEDIT_CANCEL_REASON_OUTSIDE_LEVEL("Outside world", "Cancel"), WORLDEDIT_CANCEL_REASON_OUTSIDE_REGION("Outside allowed region (bypass with /wea, or disable `region-restrictions` in config.yml)", "Cancel"), WORLDEDIT_CANCEL_REASON_NO_REGION("No allowed region (bypass with /wea, or disable `region-restrictions` in config.yml)", "Cancel"), WORLDEDIT_FAILED_LOAD_CHUNK("&cSkipped loading chunk: &7%s0;%s1&c. Try increasing chunk-wait.", "Cancel"), diff --git a/core/src/main/java/com/boydti/fawe/object/mask/CachedMask.java b/core/src/main/java/com/boydti/fawe/object/mask/CachedMask.java index f9c08fc1..37408201 100644 --- a/core/src/main/java/com/boydti/fawe/object/mask/CachedMask.java +++ b/core/src/main/java/com/boydti/fawe/object/mask/CachedMask.java @@ -46,6 +46,7 @@ public class CachedMask extends AbstractDelegateMask implements ResettableMask { } public boolean test(int x, int y, int z) { + if (y < 0 || y > 255) return getMask().test(mutable.setComponents(x, y, z)); if (cache_checked.contains(x, y, z)) { return cache_results.contains(x, y, z); } diff --git a/core/src/main/java/com/sk89q/worldedit/EditSession.java b/core/src/main/java/com/sk89q/worldedit/EditSession.java index f9d035d9..07986fe8 100644 --- a/core/src/main/java/com/sk89q/worldedit/EditSession.java +++ b/core/src/main/java/com/sk89q/worldedit/EditSession.java @@ -1379,7 +1379,7 @@ public class EditSession extends AbstractDelegateExtent implements HasFaweQueue, } else if (new ExtentTraverser(this).findAndGet(FaweRegionExtent.class) != null){ BBC.WORLDEDIT_CANCEL_REASON_OUTSIDE_REGION.send(player); } else { - BBC.WORLDEDIT_CANCEL_REASON_OUTSIDE_WORLD.send(player); + BBC.WORLDEDIT_CANCEL_REASON_OUTSIDE_LEVEL.send(player); } } // Reset limit diff --git a/core/src/main/java/com/sk89q/worldedit/regions/CylinderRegion.java b/core/src/main/java/com/sk89q/worldedit/regions/CylinderRegion.java new file mode 100644 index 00000000..d8d6548f --- /dev/null +++ b/core/src/main/java/com/sk89q/worldedit/regions/CylinderRegion.java @@ -0,0 +1,414 @@ +/* + * 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 Lesser 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 Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.regions; + +import com.sk89q.worldedit.BlockVector; +import com.sk89q.worldedit.BlockVector2D; +import com.sk89q.worldedit.LocalWorld; +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.Vector2D; +import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.math.geom.Polygons; +import com.sk89q.worldedit.regions.iterator.FlatRegion3DIterator; +import com.sk89q.worldedit.regions.iterator.FlatRegionIterator; +import com.sk89q.worldedit.world.World; + +import java.util.Iterator; +import java.util.List; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Represents a cylindrical region. + */ +public class CylinderRegion extends AbstractRegion implements FlatRegion { + + private BlockVector2D center; + private BlockVector2D radius; + private Vector2D radiusInverse; + private int minY; + private int maxY; + private boolean hasY = false; + + /** + * Construct the region + */ + public CylinderRegion() { + this((World) null); + } + + /** + * @deprecated cast {@code world} to {@link World} + */ + @Deprecated + public CylinderRegion(LocalWorld world) { + this((World) world); + } + /** + * Construct the region. + * + * @param world the world + */ + public CylinderRegion(World world) { + this(world, new Vector(), new Vector2D(), 0, 0); + hasY = false; + } + + @Deprecated + public CylinderRegion(LocalWorld world, Vector center, Vector2D radius, int minY, int maxY) { + this((World) world, center, radius, minY, maxY); + } + + /** + * Construct the region. + * + * @param world the world + * @param center the center position + * @param radius the radius along the X and Z axes + * @param minY the minimum Y, inclusive + * @param maxY the maximum Y, inclusive + */ + public CylinderRegion(World world, Vector center, Vector2D radius, int minY, int maxY) { + super(world); + setCenter(center.toVector2D()); + setRadius(radius); + this.minY = minY; + this.maxY = maxY; + hasY = true; + } + + /** + * Construct the region. + * + * @param center the center position + * @param radius the radius along the X and Z axes + * @param minY the minimum Y, inclusive + * @param maxY the maximum Y, inclusive + */ + public CylinderRegion(Vector center, Vector2D radius, int minY, int maxY) { + super(null); + setCenter(center.toVector2D()); + setRadius(radius); + this.minY = minY; + this.maxY = maxY; + hasY = true; + } + + public CylinderRegion(CylinderRegion region) { + this(region.world, region.getCenter(), region.getRadius(), region.minY, region.maxY); + hasY = region.hasY; + } + + @Override + public Vector getCenter() { + return center.toVector((getMaximumY() + getMinimumY()) / 2); + } + + /** + * Sets the main center point of the region + * + * @deprecated replaced by {@link #setCenter(Vector2D)} + */ + @Deprecated + public void setCenter(Vector center) { + setCenter(center.toVector2D()); + } + + /** + * Sets the main center point of the region + * + * @param center the center point + */ + public void setCenter(Vector2D center) { + this.center = new BlockVector2D(center); + } + + /** + * Returns the radius of the cylinder + * + * @return the radius along the X and Z axes + */ + public Vector2D getRadius() { + return radius.subtract(0.5, 0.5); + } + + /** + * Sets the radius of the cylinder + * + * @param radius the radius along the X and Z axes + */ + public void setRadius(Vector2D radius) { + this.radius = radius.add(0.5, 0.5).toBlockVector2D(); + this.radiusInverse = Vector2D.ONE.divide(radius); + } + + /** + * Extends the radius to be at least the given radius + * + * @param minRadius the minimum radius + */ + public void extendRadius(Vector2D minRadius) { + setRadius(Vector2D.getMaximum(minRadius, getRadius())); + } + + /** + * Set the minimum Y. + * + * @param y the y + */ + public void setMinimumY(int y) { + hasY = true; + minY = y; + } + + /** + * Se the maximum Y. + * + * @param y the y + */ + public void setMaximumY(int y) { + hasY = true; + maxY = y; + } + + @Override + public Vector getMinimumPoint() { + return center.subtract(getRadius()).toVector(getMinimumY()); + } + + @Override + public Vector getMaximumPoint() { + return center.add(getRadius()).toVector(getMaximumY()); + } + + @Override + public int getMaximumY() { + int worldMax = world.getMaxY() - 1; + if (maxY > worldMax) { + return maxY = worldMax; + } + return maxY; + } + + @Override + public int getMinimumY() { + if (minY < 0) { + return minY = 0; + } + return minY; + } + + @Override + public int getArea() { + return (int) Math.floor(radius.getX() * radius.getZ() * Math.PI * getHeight()); + } + + @Override + public int getWidth() { + return (int) (2 * radius.getX()); + } + + @Override + public int getHeight() { + return getMaximumY() - getMinimumY() + 1; + } + + @Override + public int getLength() { + return (int) (2 * radius.getZ()); + } + + private Vector2D calculateDiff2D(Vector... changes) throws RegionOperationException { + Vector2D diff = new Vector2D(); + for (Vector change : changes) { + diff = diff.add(change.toVector2D()); + } + + if ((diff.getBlockX() & 1) + (diff.getBlockZ() & 1) != 0) { + throw new RegionOperationException("Cylinders changes must be even for each horizontal dimensions."); + } + + return diff.divide(2).floor(); + } + + private Vector2D calculateChanges2D(Vector... changes) { + Vector2D total = new Vector2D(); + for (Vector change : changes) { + total = total.add(change.toVector2D().positive()); + } + + return total.divide(2).floor(); + } + + /** + * Expand the region. + * Expand the region. + * + * @param changes array/arguments with multiple related changes + * @throws RegionOperationException + */ + @Override + public void expand(Vector... changes) throws RegionOperationException { + center = center.add(calculateDiff2D(changes)).toBlockVector2D(); + radius = radius.add(calculateChanges2D(changes)).toBlockVector2D(); + this.radiusInverse = Vector2D.ONE.divide(radius); + for (Vector change : changes) { + int changeY = change.getBlockY(); + if (changeY > 0) { + maxY += changeY; + } else { + minY += changeY; + } + } + } + + /** + * Contract the region. + * + * @param changes array/arguments with multiple related changes + * @throws RegionOperationException + */ + @Override + public void contract(Vector... changes) throws RegionOperationException { + center = center.subtract(calculateDiff2D(changes)).toBlockVector2D(); + Vector2D newRadius = radius.subtract(calculateChanges2D(changes)); + radius = Vector2D.getMaximum(new Vector2D(1.5, 1.5), newRadius).toBlockVector2D(); + this.radiusInverse = Vector2D.ONE.divide(radius); + for (Vector change : changes) { + int height = maxY - minY; + int changeY = change.getBlockY(); + if (changeY > 0) { + minY += Math.min(height, changeY); + } else { + maxY += Math.max(-height, changeY); + } + } + } + + @Override + public void shift(Vector change) throws RegionOperationException { + center = center.add(change.toVector2D()).toBlockVector2D(); + + int changeY = change.getBlockY(); + maxY += changeY; + minY += changeY; + } + + /** + * Checks to see if a point is inside this region. + */ + @Override + public boolean contains(Vector position) { + final int pt = position.getBlockY(); + if (pt < getMinimumY() || pt > getMaximumY()) { + return false; + } + int px = position.getBlockX(); + int pz = position.getBlockZ(); + + double dx = Math.abs(px - center.getBlockX()) * radiusInverse.getX(); + double dz = Math.abs(pz - center.getBlockZ()) * radiusInverse.getZ(); + + return dx * dx + dz * dz <= 1; + } + + + /** + * Sets the height of the cylinder to fit the specified Y. + * + * @param y the y value + * @return true if the area was expanded + */ + public boolean setY(int y) { + if (!hasY) { + minY = y; + maxY = y; + hasY = true; + return true; + } else if (y < minY) { + minY = y; + return true; + } else if (y > maxY) { + maxY = y; + return true; + } + + return false; + } + + @Override + public Iterator iterator() { + return new FlatRegion3DIterator(this); + } + + @Override + public Iterable asFlatRegion() { + return new Iterable() { + @Override + public Iterator iterator() { + return new FlatRegionIterator(CylinderRegion.this); + } + }; + } + + /** + * Returns string representation in the format + * "(centerX, centerZ) - (radiusX, radiusZ) - (minY, maxY)" + * + * @return string + */ + @Override + public String toString() { + return center + " - " + radius + "(" + minY + ", " + maxY + ")"; + } + + @Override + public CylinderRegion clone() { + return (CylinderRegion) super.clone(); + } + + @Override + public List polygonize(int maxPoints) { + return Polygons.polygonizeCylinder(center, radius, maxPoints); + } + + /** + * Return a new instance with the given center and radius in the X and Z + * axes with a Y that extends from the bottom of the extent to the top + * of the extent. + * + * @param extent the extent + * @param center the center position + * @param radius the radius in the X and Z axes + * @return a region + */ + public static CylinderRegion createRadius(Extent extent, Vector center, double radius) { + checkNotNull(extent); + checkNotNull(center); + Vector2D radiusVec = new Vector2D(radius, radius); + int minY = extent.getMinimumPoint().getBlockY(); + int maxY = extent.getMaximumPoint().getBlockY(); + return new CylinderRegion(center, radiusVec, minY, maxY); + } + + public static Class inject() { + return CylinderRegion.class; + } +}