From 616f9a2360ffc9d60e8c7c9d354506b274a48ba0 Mon Sep 17 00:00:00 2001 From: TomyLobo Date: Tue, 27 Dec 2011 23:24:57 +0100 Subject: [PATCH] Added support for ellipsoid and sphere selections. //sel ellipsoid, //sel sphere. --- .../worldedit/commands/SelectionCommands.java | 13 +- .../cui/SelectionEllipsoidPointEvent.java | 45 +++ .../regions/CuboidRegionSelector.java | 2 +- .../worldedit/regions/EllipsoidRegion.java | 293 ++++++++++++++++++ .../regions/EllipsoidRegionSelector.java | 195 ++++++++++++ .../regions/SphereRegionSelector.java | 76 +++++ 6 files changed, 621 insertions(+), 3 deletions(-) create mode 100644 src/main/java/com/sk89q/worldedit/cui/SelectionEllipsoidPointEvent.java create mode 100644 src/main/java/com/sk89q/worldedit/regions/EllipsoidRegion.java create mode 100644 src/main/java/com/sk89q/worldedit/regions/EllipsoidRegionSelector.java create mode 100644 src/main/java/com/sk89q/worldedit/regions/SphereRegionSelector.java diff --git a/src/main/java/com/sk89q/worldedit/commands/SelectionCommands.java b/src/main/java/com/sk89q/worldedit/commands/SelectionCommands.java index 19dd4b35b..97bfe5f96 100644 --- a/src/main/java/com/sk89q/worldedit/commands/SelectionCommands.java +++ b/src/main/java/com/sk89q/worldedit/commands/SelectionCommands.java @@ -29,11 +29,13 @@ import com.sk89q.minecraft.util.commands.CommandPermissions; import com.sk89q.worldedit.*; import com.sk89q.worldedit.data.ChunkStore; import com.sk89q.worldedit.regions.CuboidRegionSelector; +import com.sk89q.worldedit.regions.EllipsoidRegionSelector; import com.sk89q.worldedit.regions.ExtendingCuboidRegionSelector; import com.sk89q.worldedit.regions.Polygonal2DRegionSelector; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.regions.RegionOperationException; import com.sk89q.worldedit.regions.RegionSelector; +import com.sk89q.worldedit.regions.SphereRegionSelector; import com.sk89q.worldedit.blocks.*; /** @@ -592,7 +594,7 @@ public class SelectionCommands { @Command( aliases = { "/sel", ";" }, - usage = "[cuboid|extend|poly]", + usage = "[cuboid|extend|poly|ellipsoid|sphere]", desc = "Choose a region selector", min = 0, max = 1 @@ -621,10 +623,17 @@ public class SelectionCommands { } else if (typeName.equalsIgnoreCase("poly")) { selector = new Polygonal2DRegionSelector(oldSelector); player.print("2D polygon selector: Left/right click to add a point."); + } else if (typeName.equalsIgnoreCase("ellipsoid")) { + selector = new EllipsoidRegionSelector(oldSelector); + player.print("Ellipsoid selector: left click=center, right click to extend"); + } else if (typeName.equalsIgnoreCase("sphere")) { + selector = new SphereRegionSelector(oldSelector); + player.print("Sphere selector: left click=center, right click to extend"); } else { - player.printError("Only 'cuboid', 'extend' and 'poly' are accepted."); + player.printError("Only cuboid|extend|poly|ellipsoid|sphere are accepted."); return; } + session.setRegionSelector(world, selector); session.dispatchCUISelection(player); } diff --git a/src/main/java/com/sk89q/worldedit/cui/SelectionEllipsoidPointEvent.java b/src/main/java/com/sk89q/worldedit/cui/SelectionEllipsoidPointEvent.java new file mode 100644 index 000000000..eea88af77 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/cui/SelectionEllipsoidPointEvent.java @@ -0,0 +1,45 @@ +// $Id$ +/* + * WorldEdit + * Copyright (C) 2010, 2011 sk89q 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.cui; + +import com.sk89q.worldedit.Vector; + +public class SelectionEllipsoidPointEvent implements CUIEvent { + protected int id; + protected Vector pos; + + public SelectionEllipsoidPointEvent(int id, Vector pos) { + this.id = id; + this.pos = pos; + } + + public String getTypeId() { + return "e"; + } + + public String[] getParameters() { + return new String[] { + String.valueOf(id), + String.valueOf(pos.getBlockX()), + String.valueOf(pos.getBlockY()), + String.valueOf(pos.getBlockZ()) + }; + } +} diff --git a/src/main/java/com/sk89q/worldedit/regions/CuboidRegionSelector.java b/src/main/java/com/sk89q/worldedit/regions/CuboidRegionSelector.java index b42149070..03c2dfbcd 100644 --- a/src/main/java/com/sk89q/worldedit/regions/CuboidRegionSelector.java +++ b/src/main/java/com/sk89q/worldedit/regions/CuboidRegionSelector.java @@ -41,7 +41,7 @@ public class CuboidRegionSelector implements RegionSelector, CUIPointBasedRegion protected CuboidRegion region; public CuboidRegionSelector(LocalWorld world) { - region = new CuboidRegion(world, new Vector(), new Vector()); + region = new CuboidRegion(world, new Vector(), new Vector()); } public CuboidRegionSelector() { diff --git a/src/main/java/com/sk89q/worldedit/regions/EllipsoidRegion.java b/src/main/java/com/sk89q/worldedit/regions/EllipsoidRegion.java new file mode 100644 index 000000000..0771e19f3 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/regions/EllipsoidRegion.java @@ -0,0 +1,293 @@ +// $Id$ +/* + * WorldEdit + * Copyright (C) 2010 sk89q 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; + +import com.sk89q.worldedit.BlockVector; +import com.sk89q.worldedit.LocalWorld; +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.Vector2D; +import com.sk89q.worldedit.data.ChunkStore; +import java.util.Iterator; +import java.util.Set; +import java.util.HashSet; + +/** + * + * @author TomyLobo + */ +public class EllipsoidRegion implements Region { + /** + * Stores the center. + */ + private Vector center; + /** + * Stores the radiuses plus 0.5 on each axis. + */ + private Vector radius; + /** + * Stores the world. + */ + private LocalWorld world; + + /** + * Construct a new instance of this ellipsoid region. + * + * @param pos1 + * @param pos2 + */ + public EllipsoidRegion(Vector pos1, Vector pos2) { + this(null, pos1, pos2); + } + + /** + * Construct a new instance of this ellipsoid region. + * + * @param world + * @param center + * @param radius + */ + public EllipsoidRegion(LocalWorld world, Vector center, Vector radius) { + this.world = world; + this.center = center; + this.radius = radius; + } + + public EllipsoidRegion(EllipsoidRegion ellipsoidRegion) { + this(ellipsoidRegion.world, ellipsoidRegion.center, ellipsoidRegion.radius); + } + + /** + * Get the lower point of the ellipsoid. + * + * @return min point + */ + public Vector getMinimumPoint() { + return center.subtract(getRadius()); + } + + /** + * Get the upper point of the ellipsoid. + * + * @return max point + */ + public Vector getMaximumPoint() { + return center.add(getRadius()); + } + + /** + * Get the number of blocks in the region. + * + * @return number of blocks + */ + public int getArea() { + return (int) Math.floor((4.0 / 3.0) * Math.PI * radius.getX() * radius.getY() * radius.getZ()); + } + + /** + * Get X-size. + * + * @return width + */ + public int getWidth() { + return (int) (2 * radius.getX()); + } + + /** + * Get Y-size. + * + * @return height + */ + public int getHeight() { + return (int) (2 * radius.getY()); + } + + /** + * Get Z-size. + * + * @return length + */ + public int getLength() { + return (int) (2 * radius.getZ()); + } + + /** + * Expands the ellipsoid in a direction. + * + * @param change + */ + public void expand(Vector change) { + } + + /** + * Contracts the ellipsoid in a direction. + * + * @param change + */ + public void contract(Vector change) { + } + + /** + * Get the center. + * + * @return center + */ + public Vector getCenter() { + return center; + } + + /** + * Set the center. + * + * @param center + */ + public void setCenter(Vector center) { + this.center = center; + } + + /** + * Get the radiuses. + * + * @return radiuses + */ + public Vector getRadius() { + return radius.subtract(0.5, 0.5, 0.5); + } + + /** + * Set radiuses. + * + * @param radiuses + */ + public void setRadius(Vector radius) { + this.radius = radius.add(0.5, 0.5, 0.5); + } + + /** + * Get a list of chunks that this region is within. + * + * @return + */ + public Set getChunks() { + Set chunks = new HashSet(); + + Vector min = getMinimumPoint(); + Vector max = getMaximumPoint(); + + for (int x = min.getBlockX(); x <= max.getBlockX(); ++x) { + for (int y = min.getBlockY(); y <= max.getBlockY(); ++y) { + for (int z = min.getBlockZ(); z <= max.getBlockZ(); ++z) { + Vector pt = new Vector(x, y, z); + chunks.add(ChunkStore.toChunk(pt)); + } + } + } + + return chunks; + } + + /** + * Returns true based on whether the region contains the point, + * + * @param pt + */ + public boolean contains(Vector pt) { + return pt.subtract(center).divide(radius).lengthSq() <= 1; + } + + /** + * Get the iterator. + * + * @return iterator of points inside the region + */ + public Iterator iterator() { + return new Iterator() { + private Vector min = getMinimumPoint(); + private Vector max = getMaximumPoint(); + private int nextX = min.getBlockX(); + private int nextY = min.getBlockY(); + private int nextZ = min.getBlockZ(); + { + forward(); + } + + public boolean hasNext() { + return (nextX != Integer.MIN_VALUE); + } + + private void forward() { + while (hasNext() && !contains(new BlockVector(nextX, nextY, nextZ))) { + forwardOne(); + } + } + + public BlockVector next() { + if (!hasNext()) throw new java.util.NoSuchElementException(); + BlockVector answer = new BlockVector(nextX, nextY, nextZ); + forwardOne(); + forward(); + return answer; + } + + private void forwardOne() { + if (++nextX <= max.getBlockX()) { + return; + } + nextX = min.getBlockX(); + + if (++nextY <= max.getBlockY()) { + return; + } + nextY = min.getBlockY(); + + if (++nextZ <= max.getBlockZ()) { + return; + } + nextX = Integer.MIN_VALUE; + } + + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + + /** + * Returns string representation in the format + * "(centerX, centerY, centerZ) - (radiusX, radiusY, radiusZ)". + * + * @return string + */ + @Override + public String toString() { + return center + " - " + getRadius(); + } + + public LocalWorld getWorld() { + return world; + } + + public void setWorld(LocalWorld world) { + this.world = world; + } + + public void extendRadius(Vector minRadius) { + setRadius(Vector.getMaximum(minRadius, getRadius())); + } +} diff --git a/src/main/java/com/sk89q/worldedit/regions/EllipsoidRegionSelector.java b/src/main/java/com/sk89q/worldedit/regions/EllipsoidRegionSelector.java new file mode 100644 index 000000000..a818401b4 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/regions/EllipsoidRegionSelector.java @@ -0,0 +1,195 @@ +// $Id$ +/* + * WorldEdit + * Copyright (C) 2010, 2011 sk89q 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; + +import java.util.ArrayList; +import java.util.List; +import com.sk89q.worldedit.BlockVector; +import com.sk89q.worldedit.IncompleteRegionException; +import com.sk89q.worldedit.LocalPlayer; +import com.sk89q.worldedit.LocalSession; +import com.sk89q.worldedit.LocalWorld; +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.cui.CUIPointBasedRegion; +import com.sk89q.worldedit.cui.SelectionEllipsoidPointEvent; +import com.sk89q.worldedit.cui.SelectionPointEvent; + +/** + * Selector for ellipsoids. + * + * @author TomyLobo + */ +public class EllipsoidRegionSelector implements RegionSelector, CUIPointBasedRegion { + protected EllipsoidRegion region; + + public EllipsoidRegionSelector(LocalWorld world) { + region = new EllipsoidRegion(world, new Vector(), new Vector()); + } + + public EllipsoidRegionSelector() { + this((LocalWorld) null); + } + + public EllipsoidRegionSelector(RegionSelector oldSelector) { + this(oldSelector.getIncompleteRegion().getWorld()); + if (oldSelector instanceof EllipsoidRegionSelector) { + final EllipsoidRegionSelector ellipsoidRegionSelector = (EllipsoidRegionSelector) oldSelector; + + region = new EllipsoidRegion(ellipsoidRegionSelector.getIncompleteRegion()); + }/* else { + final Region oldRegion; + try { + oldRegion = oldSelector.getRegion(); + } catch (IncompleteRegionException e) { + return; + } + + pos1 = oldRegion.getMinimumPoint().toBlockVector(); + pos2 = oldRegion.getMaximumPoint().toBlockVector(); + } + + region.setPos1(pos1); + region.setPos2(pos2);*/ + } + + public boolean selectPrimary(Vector pos) { + if (pos.equals(region.getCenter()) && region.getRadius().lengthSq() == 0) { + return false; + } + + region.setCenter(pos.toBlockVector()); + region.setRadius(new Vector()); + return true; + } + + public boolean selectSecondary(Vector pos) { + final Vector diff = pos.subtract(region.getCenter()); + final Vector minRadius = Vector.getMaximum(diff, diff.multiply(-1.0)); + region.extendRadius(minRadius); + return true; + } + + public void explainPrimarySelection(LocalPlayer player, LocalSession session, Vector pos) { + if (isDefined()) { + player.print("Center position set to " + region.getCenter() + " (" + region.getArea() + ")."); + } else { + player.print("Center position set to " + region.getCenter() + "."); + } + + session.dispatchCUIEvent(player, new SelectionEllipsoidPointEvent(0, region.getCenter())); + session.dispatchCUIEvent(player, new SelectionEllipsoidPointEvent(1, region.getRadius())); + legacyDescribeCUI(player, session); + } + + public void explainSecondarySelection(LocalPlayer player, LocalSession session, Vector pos) { + if (isDefined()) { + player.print("Radius set to " + region.getRadius() + " (" + region.getArea() + ")."); + } else { + player.print("Radius set to " + region.getRadius() + "."); + } + + session.dispatchCUIEvent(player, new SelectionEllipsoidPointEvent(1, region.getRadius())); + legacyDescribeCUI(player, session); + } + + public void explainRegionAdjust(LocalPlayer player, LocalSession session) { + legacyDescribeCUI(player, session); + } + + public boolean isDefined() { + return region.getRadius().lengthSq() > 0; + } + + public EllipsoidRegion getRegion() throws IncompleteRegionException { + if (!isDefined()) { + throw new IncompleteRegionException(); + } + + return region; + } + + public EllipsoidRegion getIncompleteRegion() { + return region; + } + + public void learnChanges() { + } + + public void clear() { + region.setCenter(new Vector()); + region.setRadius(new Vector()); + } + + public String getTypeName() { + return "ellipsoid"; + } + + public List getInformationLines() { + final List lines = new ArrayList(); + + final Vector center = region.getCenter(); + if (center.lengthSq() > 0) { + lines.add("Center: " + center); + } + + final Vector radius = region.getRadius(); + if (radius.lengthSq() > 0) { + lines.add("X/Y/Z radius: " + radius); + } + + return lines; + } + + public String getTypeId() { + return "ellipsoid"; + } + + public String getLegacyTypeId() { + return "cuboid"; + } + + public void describeCUI(LocalPlayer player) { + player.dispatchCUIEvent(new SelectionEllipsoidPointEvent(0, region.getCenter())); + player.dispatchCUIEvent(new SelectionEllipsoidPointEvent(1, region.getRadius())); + legacyDescribeCUI(player); + } + + protected void legacyDescribeCUI(LocalPlayer player, LocalSession session) { + if (!session.hasCUISupport()) { + return; + } + + legacyDescribeCUI(player); + } + + private void legacyDescribeCUI(LocalPlayer player) { + player.dispatchCUIEvent(new SelectionPointEvent(0, region.getMinimumPoint(), getArea())); + player.dispatchCUIEvent(new SelectionPointEvent(1, region.getMaximumPoint(), getArea())); + } + + public int getArea() { + return region.getArea(); + } + + @Override + public BlockVector getPrimaryPosition() throws IncompleteRegionException { + return region.getCenter().toBlockVector(); + } +} diff --git a/src/main/java/com/sk89q/worldedit/regions/SphereRegionSelector.java b/src/main/java/com/sk89q/worldedit/regions/SphereRegionSelector.java new file mode 100644 index 000000000..2eb3c56f6 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/regions/SphereRegionSelector.java @@ -0,0 +1,76 @@ +// $Id$ +/* + * WorldEdit + * Copyright (C) 2010, 2011 sk89q 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; + +import com.sk89q.worldedit.LocalPlayer; +import com.sk89q.worldedit.LocalSession; +import com.sk89q.worldedit.LocalWorld; +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.cui.SelectionEllipsoidPointEvent; + +/** + * Selector for spheres. + * + * @author TomyLobo + */ +public class SphereRegionSelector extends EllipsoidRegionSelector { + public SphereRegionSelector(LocalWorld world) { + super(world); + } + + public SphereRegionSelector() { + super(); + } + + public SphereRegionSelector(RegionSelector oldSelector) { + super(oldSelector); + final Vector radius = region.getRadius(); + double radiusScalar = Math.max(Math.max(radius.getX(), radius.getY()), radius.getZ()); + region.setRadius(new Vector(radiusScalar, radiusScalar, radiusScalar)); + } + + @Override + public boolean selectSecondary(Vector pos) { + final Vector diff = pos.subtract(region.getCenter()); + final Vector minRadius = Vector.getMaximum(diff, diff.multiply(-1.0)); + + double minRadiusScalar = Math.max(Math.max(minRadius.getX(), minRadius.getY()), minRadius.getZ()); + + region.extendRadius(new Vector(minRadiusScalar, minRadiusScalar, minRadiusScalar)); + return true; + } + + @Override + public void explainSecondarySelection(LocalPlayer player, LocalSession session, Vector pos) { + if (isDefined()) { + player.print("Radius set to " + region.getRadius().getX() + " (" + region.getArea() + ")."); + } else { + player.print("Radius set to " + region.getRadius().getX() + "."); + } + + session.dispatchCUIEvent(player, new SelectionEllipsoidPointEvent(1, region.getRadius())); + legacyDescribeCUI(player, session); + } + + @Override + public String getTypeName() { + return "sphere"; + } +}