Added polygonal regions.

This commit is contained in:
sk89q 2011-02-19 17:44:39 -08:00
parent 9667e92d66
commit 97529abaca
17 changed files with 1184 additions and 208 deletions

View File

@ -178,15 +178,20 @@ commands:
//count:
description: Counts the number of a certain type of block
usage: /<command> <block>
/size:
/m:
description: Get information about the selection
usage: /<command>
aliases: ['//size']
/shift:
description: Shift the selection area
usage: /<command> <amount> [direction]
/expand:
description: Expand the selection area
usage: /<command> <amount> [reverse-amount] <direction>
/sel:
description: Choose a region selector
usage: /<command> [type]
aliases: [',']
/inset:
description: Inset the selection area
usage: /<command> [-hv] <amount>
@ -299,6 +304,10 @@ commands:
/drain:
description: Drain a pool
usage: /<command> <radius>
/version:
version:
description: Get WorldEdit version
usage: /<command>
aliases: ['ver']
reload:
description: Reload WorldEdit
usage: /<command>

View File

@ -28,8 +28,9 @@ import com.sk89q.worldedit.tools.SinglePickaxe;
import com.sk89q.worldedit.tools.BlockTool;
import com.sk89q.worldedit.tools.Tool;
import com.sk89q.worldedit.bags.BlockBag;
import com.sk89q.worldedit.regions.CuboidRegionSelector;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.regions.CuboidRegion;
import com.sk89q.worldedit.regions.RegionSelector;
/**
* An instance of this represents the WorldEdit session of a user. A session
@ -51,9 +52,9 @@ public class LocalSession {
private LocalConfiguration config;
private LocalWorld selectionWorld;
private RegionSelector regionSelector = new CuboidRegionSelector();
private boolean placeAtPos1 = false;
private Vector pos1, pos2;
private Region region;
private LinkedList<EditSession> history = new LinkedList<EditSession>();
private int historyPointer = 0;
private CuboidClipboard clipboard;
@ -147,25 +148,41 @@ public class LocalSession {
}
/**
* Checks to make sure that position 1 is defined.
* Get the region selector for defining the selection. If the selection
* was defined for a different world, the old selection will be discarded.
*
* @throws IncompleteRegionException
* @param world
* @return position
*/
private void checkPos1() throws IncompleteRegionException {
if (pos1 == null) {
throw new IncompleteRegionException();
public RegionSelector getRegionSelector(LocalWorld world) {
if (selectionWorld == null) {
selectionWorld = world;
} else if (!selectionWorld.equals(world)) {
selectionWorld = world;
regionSelector.clear();
}
return regionSelector;
}
/**
* Checks to make sure that position 2 is defined.
* Get the region selector. This won't check worlds so make sure that
* this region selector isn't used blindly.
*
* @throws IncompleteRegionException
* @return position
*/
private void checkPos2() throws IncompleteRegionException {
if (pos2 == null) {
throw new IncompleteRegionException();
public RegionSelector getRegionSelector() {
return regionSelector;
}
/**
* Set the region selector.
*
* @param world
* @param selector
*/
public void setRegionSelector(LocalWorld world, RegionSelector selector) {
selectionWorld = world;
regionSelector = selector;
}
/**
@ -173,80 +190,50 @@ public class LocalSession {
*
* @return
*/
@Deprecated
public boolean isRegionDefined() {
return pos1 != null && pos2 != null;
return regionSelector.isDefined();
}
/**
* Gets defined position 1.
* Returns true if the region is fully defined for the specified world.
*
* @return position 1
* @throws IncompleteRegionException
* @param world
* @return
*/
public Vector getPos1() throws IncompleteRegionException {
checkPos1();
return pos1;
public boolean isSelectionDefined(LocalWorld world) {
if (selectionWorld == null || !selectionWorld.equals(world)) {
return false;
}
return regionSelector.isDefined();
}
/**
* Sets position 1.
*
* @param pt
*/
public void setPos1(Vector pt) {
pos1 = pt;
if (pos1 != null && pos2 != null) {
region = new CuboidRegion(pos1, pos2);
}
}
/**
* Gets position 2.
*
* @return position 2
* @throws IncompleteRegionException
*/
public Vector getPos2() throws IncompleteRegionException {
checkPos2();
return pos2;
}
/**
* Sets position 2.
*
* @param pt
*/
public void setPos2(Vector pt) {
pos2 = pt;
if (pos1 != null && pos2 != null) {
region = new CuboidRegion(pos1, pos2);
}
}
/**
* Update session position 1/2 based on the currently set region,
* provided that the region is of a cuboid.
*/
public void learnRegionChanges() {
if (region instanceof CuboidRegion) {
CuboidRegion cuboidRegion = (CuboidRegion)region;
pos1 = cuboidRegion.getPos1();
pos2 = cuboidRegion.getPos2();
}
}
/**
* Get the region. If you change the region, you should
* call learnRegionChanges().
* Use <code>getSelection()</code>.
*
* @return region
* @throws IncompleteRegionException
*/
@Deprecated
public Region getRegion() throws IncompleteRegionException {
if (region == null) {
return regionSelector.getRegion();
}
/**
* Get the selection region. If you change the region, you should
* call learnRegionChanges(). If the selection is defined in
* a different world, the <code>IncompleteRegionException</code>
* exception will be thrown.
*
* @param world
* @return region
* @throws IncompleteRegionException
*/
public Region getSelection(LocalWorld world) throws IncompleteRegionException {
if (selectionWorld == null || !selectionWorld.equals(world)) {
throw new IncompleteRegionException();
}
return region;
return regionSelector.getRegion();
}
/**
@ -353,8 +340,7 @@ public class LocalSession {
return player.getBlockIn();
}
checkPos1();
return pos1;
return regionSelector.getPrimaryPosition();
}
/**

View File

@ -145,6 +145,15 @@ public class Vector2D {
return new Vector2D(x, z);
}
/**
* Gets a BlockVector version.
*
* @return BlockVector
*/
public BlockVector2D toBlockVector2D() {
return new BlockVector2D(this);
}
/**
* Checks if another object is equivalent.
*

View File

@ -35,6 +35,7 @@ import com.sk89q.worldedit.LocalSession.CompassMode;
import com.sk89q.worldedit.bags.BlockBag;
import com.sk89q.worldedit.blocks.*;
import com.sk89q.worldedit.commands.*;
import com.sk89q.worldedit.regions.RegionSelector;
import com.sk89q.worldedit.scripting.*;
import com.sk89q.worldedit.tools.BlockTool;
import com.sk89q.worldedit.tools.Tool;
@ -782,13 +783,9 @@ public class WorldEdit {
if (itemInHand == config.wandItem && session.isToolControlEnabled()
&& player.hasPermission("worldedit.selection.pos")) {
session.setPos2(clicked);
try {
player.print("Second position set to " + clicked
+ " (" + session.getRegion().getSize() + ").");
} catch (IncompleteRegionException e) {
player.print("Second position set to " + clicked + ".");
RegionSelector selector = session.getRegionSelector(player.getWorld());
if (selector.selectSecondary(clicked)) {
selector.explainSecondarySelection(player, clicked);
}
return true;
@ -823,19 +820,9 @@ public class WorldEdit {
return false;
}
try {
if (session.getPos1().equals(clicked)) {
return false;
}
} catch (IncompleteRegionException e) {
}
session.setPos1(clicked);
try {
player.print("First position set to " + clicked
+ " (" + session.getRegion().getSize() + ").");
} catch (IncompleteRegionException e) {
player.print("First position set to " + clicked + ".");
RegionSelector selector = session.getRegionSelector(player.getWorld());
if (selector.selectPrimary(clicked)) {
selector.explainPrimarySelection(player, clicked);
}
return true;

View File

@ -208,7 +208,8 @@ public class WorldEditPlugin extends JavaPlugin {
*/
public Region getPlayerSelection(Player player)
throws IncompleteRegionException {
return controller.getSession(wrapPlayer(player)).getRegion();
return controller.getSession(wrapPlayer(player))
.getSelection(new BukkitWorld(player.getWorld()));
}
/**

View File

@ -70,7 +70,7 @@ public class ChunkCommands {
LocalSession session, LocalPlayer player, EditSession editSession)
throws WorldEditException {
Set<Vector2D> chunks = session.getRegion().getChunks();
Set<Vector2D> chunks = session.getSelection(player.getWorld()).getChunks();
for (Vector2D chunk : chunks) {
player.print(NestedFileChunkStore.getFilename(chunk));
@ -91,7 +91,7 @@ public class ChunkCommands {
LocalConfiguration config = we.getConfiguration();
Set<Vector2D> chunks = session.getRegion().getChunks();
Set<Vector2D> chunks = session.getSelection(player.getWorld()).getChunks();
FileOutputStream out = null;
if (config.shellSaveType == null) {

View File

@ -47,7 +47,7 @@ public class ClipboardCommands {
LocalSession session, LocalPlayer player, EditSession editSession)
throws WorldEditException {
Region region = session.getRegion();
Region region = session.getSelection(player.getWorld());
Vector min = region.getMinimumPoint();
Vector max = region.getMaximumPoint();
Vector pos = player.getBlockIn();
@ -79,7 +79,7 @@ public class ClipboardCommands {
block = we.getBlock(player, args.getString(0));
}
Region region = session.getRegion();
Region region = session.getSelection(player.getWorld());
Vector min = region.getMinimumPoint();
Vector max = region.getMaximumPoint();
Vector pos = player.getBlockIn();
@ -90,7 +90,7 @@ public class ClipboardCommands {
clipboard.copy(editSession);
session.setClipboard(clipboard);
editSession.setBlocks(session.getRegion(), block);
editSession.setBlocks(session.getSelection(player.getWorld()), block);
player.print("Block(s) cut.");
}

View File

@ -53,10 +53,10 @@ public class RegionCommands {
int affected;
if (pattern instanceof SingleBlockPattern) {
affected = editSession.setBlocks(session.getRegion(),
affected = editSession.setBlocks(session.getSelection(player.getWorld()),
((SingleBlockPattern)pattern).getBlock());
} else {
affected = editSession.setBlocks(session.getRegion(), pattern);
affected = editSession.setBlocks(session.getSelection(player.getWorld()), pattern);
}
player.print(affected + " block(s) have been changed.");
@ -86,10 +86,10 @@ public class RegionCommands {
int affected = 0;
if (to instanceof SingleBlockPattern) {
affected = editSession.replaceBlocks(session.getRegion(), from,
affected = editSession.replaceBlocks(session.getSelection(player.getWorld()), from,
((SingleBlockPattern)to).getBlock());
} else {
affected = editSession.replaceBlocks(session.getRegion(), from, to);
affected = editSession.replaceBlocks(session.getSelection(player.getWorld()), from, to);
}
player.print(affected + " block(s) have been replaced.");
@ -109,7 +109,7 @@ public class RegionCommands {
Pattern pat = we.getBlockPattern(player, args.getString(0));
Region region = session.getRegion();
Region region = session.getSelection(player.getWorld());
int affected = 0;
if (pat instanceof SingleBlockPattern) {
affected = editSession.overlayCuboidBlocks(region,
@ -133,7 +133,7 @@ public class RegionCommands {
throws WorldEditException {
BaseBlock block = we.getBlock(player, args.getString(0));
int affected = editSession.makeCuboidWalls(session.getRegion(), block);
int affected = editSession.makeCuboidWalls(session.getSelection(player.getWorld()), block);
player.print(affected + " block(s) have been changed.");
}
@ -151,7 +151,7 @@ public class RegionCommands {
throws WorldEditException {
BaseBlock block = we.getBlock(player, args.getString(0));
int affected = editSession.makeCuboidFaces(session.getRegion(), block);
int affected = editSession.makeCuboidFaces(session.getSelection(player.getWorld()), block);
player.print(affected + " block(s) have been changed.");
}
@ -172,7 +172,7 @@ public class RegionCommands {
iterations = args.getInteger(0);
}
HeightMap heightMap = new HeightMap(editSession, session.getRegion());
HeightMap heightMap = new HeightMap(editSession, session.getSelection(player.getWorld()));
HeightMapFilter filter = new HeightMapFilter(new GaussianKernel(5, 1.0));
int affected = heightMap.applyFilter(filter, iterations);
player.print("Terrain's height map smoothed. " + affected + " block(s) changed.");
@ -203,7 +203,7 @@ public class RegionCommands {
replace = new BaseBlock(0);
}
int affected = editSession.moveCuboidRegion(session.getRegion(),
int affected = editSession.moveCuboidRegion(session.getSelection(player.getWorld()),
dir, count, true, replace);
player.print(affected + " blocks moved.");
}
@ -226,7 +226,7 @@ public class RegionCommands {
Vector dir = we.getDirection(player,
args.argsLength() > 1 ? args.getString(1).toLowerCase() : "me");
int affected = editSession.stackCuboidRegion(session.getRegion(),
int affected = editSession.stackCuboidRegion(session.getSelection(player.getWorld()),
dir, count, !args.hasFlag('a'));
player.print(affected + " blocks changed. Undo with //undo");
}

View File

@ -27,7 +27,10 @@ import com.sk89q.minecraft.util.commands.CommandContext;
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.Polygonal2DRegionSelector;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.regions.RegionOperationException;
import com.sk89q.worldedit.blocks.*;
/**
@ -48,14 +51,14 @@ public class SelectionCommands {
LocalSession session, LocalPlayer player, EditSession editSession)
throws WorldEditException {
session.setPos1(player.getBlockIn());
if (session.isRegionDefined()) {
player.print("First position set to " + player.getBlockIn()
+ " (" + session.getRegion().getSize() + ").");
} else {
player.print("First position set to " + player.getBlockIn() + ".");
if (!session.getRegionSelector(player.getWorld())
.selectPrimary(player.getBlockIn())) {
player.printError("Position already set.");
return;
}
session.getRegionSelector(player.getWorld())
.explainPrimarySelection(player, player.getBlockIn());
}
@Command(
@ -70,14 +73,15 @@ public class SelectionCommands {
LocalSession session, LocalPlayer player, EditSession editSession)
throws WorldEditException {
session.setPos2(player.getBlockIn());
if (session.isRegionDefined()) {
player.print("Second position set to " + player.getBlockIn()
+ " (" + session.getRegion().getSize() + ").");
} else {
player.print("Second position set to " + player.getBlockIn() + ".");
if (!session.getRegionSelector(player.getWorld())
.selectSecondary(player.getBlockIn())) {
player.printError("Position already set.");
return;
}
session.getRegionSelector(player.getWorld())
.explainSecondarySelection(player, player.getBlockIn());
}
@Command(
@ -95,13 +99,14 @@ public class SelectionCommands {
Vector pos = player.getBlockTrace(300);
if (pos != null) {
session.setPos1(pos);
if (session.isRegionDefined()) {
player.print("First position set to " + pos
+ " (" + session.getRegion().getSize() + ").");
} else {
player.print("First position set to " + pos.toString() + " .");
if (!session.getRegionSelector(player.getWorld())
.selectPrimary(pos)) {
player.printError("Position already set.");
return;
}
session.getRegionSelector(player.getWorld())
.explainPrimarySelection(player, pos);
} else {
player.printError("No block in sight!");
}
@ -122,14 +127,14 @@ public class SelectionCommands {
Vector pos = player.getBlockTrace(300);
if (pos != null) {
session.setPos2(pos);
if (session.isRegionDefined()) {
player.print("Second position set to " + pos
+ " (" + session.getRegion().getSize() + ").");
} else {
player.print("Second position set to " + pos.toString() + " .");
if (!session.getRegionSelector(player.getWorld())
.selectSecondary(pos)) {
player.printError("Position already set.");
return;
}
session.getRegionSelector(player.getWorld())
.explainSecondarySelection(player, pos);
} else {
player.printError("No block in sight!");
}
@ -151,8 +156,10 @@ public class SelectionCommands {
Vector min = new Vector(min2D.getBlockX() * 16, 0, min2D.getBlockZ() * 16);
Vector max = min.add(15, 127, 15);
session.setPos1(min);
session.setPos2(max);
CuboidRegionSelector selector = new CuboidRegionSelector();
selector.selectPrimary(min);
selector.selectSecondary(max);
session.setRegionSelector(player.getWorld(), selector);
player.print("Chunk selected: "
+ min2D.getBlockX() + ", " + min2D.getBlockZ());
@ -213,14 +220,18 @@ public class SelectionCommands {
// sky and bedrock.
if (args.getString(0).equalsIgnoreCase("vert")
|| args.getString(0).equalsIgnoreCase("vertical")) {
Region region = session.getRegion();
int oldSize = region.getSize();
Region region = session.getSelection(player.getWorld());
try {
int oldSize = region.getArea();
region.expand(new Vector(0, 128, 0));
region.expand(new Vector(0, -128, 0));
session.learnRegionChanges();
int newSize = region.getSize();
session.getRegionSelector().learnChanges();
int newSize = region.getArea();
player.print("Region expanded " + (newSize - oldSize)
+ " blocks [top-to-bottom].");
} catch (RegionOperationException e) {
player.printError(e.getMessage());
}
return;
}
@ -246,16 +257,16 @@ public class SelectionCommands {
dir = we.getDirection(player, "me");
}
Region region = session.getRegion();
int oldSize = region.getSize();
Region region = session.getSelection(player.getWorld());
int oldSize = region.getArea();
region.expand(dir.multiply(change));
if (reverseChange != 0) {
region.expand(dir.multiply(reverseChange));
}
session.learnRegionChanges();
int newSize = region.getSize();
session.getRegionSelector().learnChanges();
int newSize = region.getArea();
player.print("Region expanded " + (newSize - oldSize) + " blocks.");
}
@ -294,16 +305,20 @@ public class SelectionCommands {
dir = we.getDirection(player, "me");
}
Region region = session.getRegion();
int oldSize = region.getSize();
try {
Region region = session.getSelection(player.getWorld());
int oldSize = region.getArea();
region.contract(dir.multiply(change));
if (reverseChange != 0) {
region.contract(dir.multiply(reverseChange));
}
session.learnRegionChanges();
int newSize = region.getSize();
session.getRegionSelector().learnChanges();
int newSize = region.getArea();
player.print("Region contracted " + (oldSize - newSize) + " blocks.");
} catch (RegionOperationException e) {
player.printError(e.getMessage());
}
}
@Command(
@ -327,12 +342,16 @@ public class SelectionCommands {
dir = we.getDirection(player, "me");
}
Region region = session.getRegion();
try {
Region region = session.getSelection(player.getWorld());
region.expand(dir.multiply(change));
region.contract(dir.multiply(change));
session.learnRegionChanges();
session.getRegionSelector().learnChanges();
player.print("Region shifted.");
} catch (RegionOperationException e) {
player.printError(e.getMessage());
}
}
@Command(
@ -349,8 +368,9 @@ public class SelectionCommands {
throws WorldEditException {
int change = args.getInteger(0);
Region region = session.getRegion();
Region region = session.getSelection(player.getWorld());
try {
if (!args.hasFlag('h')) {
region.expand((new Vector(0, 1, 0)).multiply(change));
region.expand((new Vector(0, -1, 0)).multiply(change));
@ -363,9 +383,12 @@ public class SelectionCommands {
region.expand((new Vector(0, 0, -1)).multiply(change));
}
session.learnRegionChanges();
session.getRegionSelector().learnChanges();
player.print("Region outset.");
} catch (RegionOperationException e) {
player.printError(e.getMessage());
}
}
@Command(
@ -382,7 +405,7 @@ public class SelectionCommands {
throws WorldEditException {
int change = args.getInteger(0);
Region region = session.getRegion();
Region region = session.getSelection(player.getWorld());
if (!args.hasFlag('h')) {
region.contract((new Vector(0, 1, 0)).multiply(change));
@ -396,13 +419,13 @@ public class SelectionCommands {
region.contract((new Vector(0, 0, -1)).multiply(change));
}
session.learnRegionChanges();
session.getRegionSelector().learnChanges();
player.print("Region inset.");
}
@Command(
aliases = {"/size"},
aliases = {"/m", "//size"},
usage = "",
desc = "Get information about the selection",
min = 0,
@ -413,16 +436,20 @@ public class SelectionCommands {
LocalSession session, LocalPlayer player, EditSession editSession)
throws WorldEditException {
Region region = session.getRegion();
Region region = session.getSelection(player.getWorld());
Vector size = region.getMaximumPoint()
.subtract(region.getMinimumPoint())
.add(1, 1, 1);
player.print("First position: " + session.getPos1());
player.print("Second position: " + session.getPos2());
player.print("Type: " + session.getRegionSelector().getTypeName());
for (String line : session.getRegionSelector().getInformationLines()) {
player.print(line);
}
player.print("Size: " + size);
player.print("Distance: " + region.getMaximumPoint().distance(region.getMinimumPoint()));
player.print("# of blocks: " + region.getSize());
player.print("Cuboid distance: " + region.getMaximumPoint().distance(region.getMinimumPoint()));
player.print("# of blocks: " + region.getArea());
}
@Command(
@ -440,7 +467,7 @@ public class SelectionCommands {
Set<Integer> searchIDs = we.getBlockIDs(player,
args.getString(0), true);
player.print("Counted: " +
editSession.countBlocks(session.getRegion(), searchIDs));
editSession.countBlocks(session.getSelection(player.getWorld()), searchIDs));
}
@Command(
@ -457,12 +484,12 @@ public class SelectionCommands {
throws WorldEditException {
List<Countable<Integer>> distribution =
editSession.getBlockDistribution(session.getRegion());
editSession.getBlockDistribution(session.getSelection(player.getWorld()));
Logger logger = Logger.getLogger("Minecraft.WorldEdit");
if (distribution.size() > 0) { // *Should* always be true
int size = session.getRegion().getSize();
int size = session.getSelection(player.getWorld()).getArea();
player.print("# total blocks: " + size);
@ -486,4 +513,27 @@ public class SelectionCommands {
player.printError("No blocks counted.");
}
}
@Command(
aliases = {"/sel", ","},
usage = "[type]",
desc = "Choose a region selector",
min = 1,
max = 1
)
public static void select(CommandContext args, WorldEdit we,
LocalSession session, LocalPlayer player, EditSession editSession)
throws WorldEditException {
String typeName = args.getString(0);
if (typeName.equalsIgnoreCase("cuboid")) {
session.setRegionSelector(player.getWorld(), new CuboidRegionSelector());
player.print("Cuboid: left click for point 1, right click for point 2");
} else if (typeName.equalsIgnoreCase("poly")) {
session.setRegionSelector(player.getWorld(), new Polygonal2DRegionSelector());
player.print("2D polygon selector: Left/right click to add a point.");
} else {
player.printError("Only 'cuboid' and 'poly' are accepted.");
}
}
}

View File

@ -71,7 +71,7 @@ public class SnapshotUtilCommands {
return;
}
Region region = session.getRegion();
Region region = session.getSelection(player.getWorld());
Snapshot snapshot;
if (args.argsLength() > 0) {

View File

@ -19,6 +19,7 @@
package com.sk89q.worldedit.regions;
import com.sk89q.worldedit.BlockVector;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.Vector2D;
import com.sk89q.worldedit.data.ChunkStore;
@ -28,7 +29,7 @@ import java.util.HashSet;
/**
*
* @author Albert
* @author sk89q
*/
public class CuboidRegion implements Region {
/**
@ -80,7 +81,7 @@ public class CuboidRegion implements Region {
*
* @return number of blocks
*/
public int getSize() {
public int getArea() {
Vector min = getMinimumPoint();
Vector max = getMaximumPoint();
@ -288,12 +289,30 @@ public class CuboidRegion implements Region {
return chunks;
}
/**
* Returns true based on whether the region contains the point,
*
* @param pt
*/
public boolean contains(Vector pt) {
double x = pt.getX();
double y = pt.getY();
double z = pt.getZ();
Vector min = getMinimumPoint();
Vector max = getMaximumPoint();
return x >= min.getBlockX() && x <= max.getBlockX()
&& y >= min.getBlockY() && y <= max.getBlockY()
&& z >= min.getBlockZ() && z <= max.getBlockZ();
}
/**
* Get the iterator.
*
* @return iterator of Points
* @return iterator of points inside the region
*/
public Iterator<Vector> iterator() {
public Iterator<BlockVector> iterator() {
throw new UnsupportedOperationException("Not implemented");
}
}

View File

@ -0,0 +1,122 @@
// $Id$
/*
* WorldEdit
* Copyright (C) 2010, 2011 sk89q <http://www.sk89q.com>
*
* 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;
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.Vector;
/**
* Selector for cuboids.
*
* @author sk89q
*/
public class CuboidRegionSelector implements RegionSelector {
protected BlockVector pos1;
protected BlockVector pos2;
protected CuboidRegion region = new CuboidRegion(new Vector(), new Vector());
public boolean selectPrimary(Vector pos) {
if (pos1 != null && pos1.equals(pos)) {
return false;
}
pos1 = pos.toBlockVector();
region.setPos1(pos1);
return true;
}
public boolean selectSecondary(Vector pos) {
if (pos2 != null && pos2.equals(pos)) {
return false;
}
pos2 = pos.toBlockVector();
region.setPos2(pos2);
return true;
}
public void explainPrimarySelection(LocalPlayer player, Vector pos) {
if (pos1 != null && pos2 != null) {
player.print("First position set to " + pos1
+ " (" + region.getArea() + ").");
} else {
player.print("First position set to " + pos1 + ".");
}
}
public void explainSecondarySelection(LocalPlayer player, Vector pos) {
if (pos1 != null && pos2 != null) {
player.print("Second position set to " + pos1
+ " (" + region.getArea() + ").");
} else {
player.print("Second position set to " + pos1 + ".");
}
}
public BlockVector getPrimaryPosition() throws IncompleteRegionException {
if (pos1 == null) {
throw new IncompleteRegionException();
}
return pos1;
}
public boolean isDefined() {
return pos1 != null && pos2 != null;
}
public Region getRegion() throws IncompleteRegionException {
if (pos1 == null || pos2 == null) {
throw new IncompleteRegionException();
}
return region;
}
public void learnChanges() {
pos1 = region.getPos1().toBlockVector();
pos2 = region.getPos2().toBlockVector();
}
public void clear() {
pos1 = null;
pos2 = null;
}
public String getTypeName() {
return "cuboid";
}
public List<String> getInformationLines() {
List<String> lines = new ArrayList<String>();
if (pos1 != null) {
lines.add("Position 1: " + pos1);
}
if (pos2 != null) {
lines.add("Position 2: " + pos2);
}
return lines;
}
}

View File

@ -0,0 +1,527 @@
// $Id$
/*
* WorldEdit
* Copyright (C) 2010, 2011 sk89q <http://www.sk89q.com>
*
* 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;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import com.sk89q.worldedit.BlockVector;
import com.sk89q.worldedit.BlockVector2D;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.Vector2D;
import com.sk89q.worldedit.data.ChunkStore;
/**
* Represents a 2D polygonal region.
*
* @author sk89q
*/
public class Polygonal2DRegion implements Region {
protected List<BlockVector2D> points;
protected BlockVector min;
protected BlockVector max;
protected int minY;
protected int maxY;
protected boolean hasY = false;
/**
* Construct the region.
*/
public Polygonal2DRegion() {
points = new ArrayList<BlockVector2D>();
minY = 0;
maxY = 0;
hasY = false;
recalculate();
}
/**
* Construct the region.
*
* @param points
* @param minY
* @param maxY
*/
public Polygonal2DRegion(List<BlockVector2D> points, int minY, int maxY) {
this.points = points;
this.minY = minY;
this.maxY = maxY;
hasY = true;
recalculate();
}
/**
* Get the list of points.
*
* @return
*/
public List<BlockVector2D> getPoints() {
return Collections.unmodifiableList(points);
}
/**
* Recalculate the bounding box of this polygonal region. This should be
* called after points have been changed.
*/
protected void recalculate() {
if (points.size() == 0) {
min = new BlockVector(0, 0, 0);
max = new BlockVector(0, 0, 0);
return;
}
int minX = points.get(0).getBlockX();
int minZ = points.get(0).getBlockZ();
int maxX = points.get(0).getBlockX();
int maxZ = points.get(0).getBlockZ();
for (BlockVector2D v : points) {
int x = v.getBlockX();
int z = v.getBlockZ();
if (x < minX) minX = x;
if (z < minZ) minZ = z;
if (x > maxX) maxX = x;
if (z > maxZ) maxZ = z;
}
int oldMinY = minY;
int oldMaxY = maxY;
minY = Math.min(oldMinY, oldMaxY);
maxY = Math.max(oldMinY, oldMaxY);
min = new BlockVector(minX, minY, minZ);
max = new BlockVector(maxX, maxY, maxZ);
}
/**
* Add a point to the list.
*
* @param pt
*/
public void addPoint(Vector2D pt) {
points.add(pt.toBlockVector2D());
recalculate();
}
/**
* Add a point to the list.
*
* @param pt
*/
public void addPoint(BlockVector2D pt) {
points.add(pt);
recalculate();
}
/**
* Add a point to the list.
*
* @param pt
*/
public void addPoint(Vector pt) {
points.add(new BlockVector2D(pt.getBlockX(), pt.getBlockZ()));
recalculate();
}
/**
* Get the lower point of a region.
*
* @return min. point
*/
public Vector getMinimumPoint() {
return min;
}
/**
* Get the upper point of a region.
*
* @return max. point
*/
public Vector getMaximumPoint() {
return max;
}
/**
* Get the number of blocks in the region.
*
* @return number of blocks
*/
public int getArea() {
double area = 0;
int i, j = points.size() - 1;
for (i = 0; i < points.size(); i++) {
area += (points.get(j).getBlockX() + points.get(i).getBlockX())
* (points.get(j).getBlockZ() - points.get(i).getBlockZ());
j = i;
}
return (int)Math.floor(Math.abs(area * 0.5)
* (maxY - minY + 1));
}
/**
* Get X-size.
*
* @return width
*/
public int getWidth() {
return max.getBlockX() - min.getBlockX();
}
/**
* Get Y-size.
*
* @return height
*/
public int getHeight() {
return max.getBlockY() - min.getBlockY();
}
/**
* Get Z-size.
*
* @return length
*/
public int getLength() {
return max.getBlockZ() - min.getBlockZ();
}
/**
* Expand the region.
*
* @param change
*/
public void expand(Vector change) throws RegionOperationException {
if (change.getBlockX() != 0 || change.getBlockZ() != 0) {
throw new RegionOperationException("Polygons can only be expanded vertically.");
}
int changeY = change.getBlockY();
if (changeY > 0) {
maxY += changeY;
} else {
minY += changeY;
}
recalculate();
}
/**
* Contract the region.
*
* @param change
*/
public void contract(Vector change) throws RegionOperationException {
if (change.getBlockX() != 0 || change.getBlockZ() != 0) {
throw new RegionOperationException("Polygons can only be contracted vertically.");
}
int changeY = change.getBlockY();
if (changeY > 0) {
minY += changeY;
} else {
maxY += changeY;
}
recalculate();
}
/**
* Checks to see if a point is inside this region.
*/
@Override
public boolean contains(Vector pt) {
return contains(points, minY, maxY, pt);
}
/**
* Checks to see if a point is inside a region.
*
* @param points
* @param minY
* @param maxY
* @param pt
* @return
*/
public static boolean contains(List<BlockVector2D> points, int minY,
int maxY, Vector pt) {
if (points.size() < 3) {
return false;
}
int targetX = pt.getBlockX();
int targetY = pt.getBlockY();
int targetZ = pt.getBlockZ();
if (targetY < minY || targetY > maxY) {
return false;
}
boolean inside = false;
int npoints = points.size();
int xNew, zNew;
int xOld, zOld;
int x1, z1;
int x2, z2;
int i;
xOld = points.get(npoints - 1).getBlockX();
zOld = points.get(npoints - 1).getBlockZ();
for (i = 0; i < npoints; i++) {
xNew = points.get(i).getBlockX();
zNew = points.get(i).getBlockZ();
if (xNew > xOld) {
x1 = xOld - 1;
x2 = xNew;
z1 = zOld;
z2 = zNew + 1;
} else {
x1 = xNew - 1;
x2 = xOld;
z1 = zNew;
z2 = zOld + 1;
}
if (xNew < targetX == targetX <= xOld) {
long v1 = ((long) targetZ - (long) z1) * (long) (x2 - x1);
long v2 = ((long) z2 - (long) z1) * (long) (targetX - x1);
if (v1 < v2) {
inside = !inside;
}
}
xOld = xNew;
zOld = zNew;
}
return inside;
}
/**
* Get a list of chunks.
*
* @return
*/
public Set<Vector2D> getChunks() {
Set<Vector2D> chunks = new HashSet<Vector2D>();
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);
if (contains(pt)) { // Not the best
chunks.add(ChunkStore.toChunk(pt));
}
}
}
}
return chunks;
}
/**
* Return the number of points.
*
* @return
*/
public int size() {
return points.size();
}
/**
* Expand the height of the polygon to fit the specified Y.
*
* @param y
* @return true if the area was expanded
*/
public boolean expandY(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;
}
/**
* Get the iterator.
*
* @return iterator of points inside the region
*/
public Iterator<BlockVector> iterator() {
return new Polygonal2DRegionIterator(this);
/*
Incomplete iterator. Where's my yield?!
ArrayList<BlockVector> items = new ArrayList<BlockVector>();
int nodes, pixelZ, i, j, swap;
int n = points.size();
int[] nodeX = new int[n];
int minZ = getMinimumPoint().getBlockZ();
int maxZ = getMaximumPoint().getBlockZ();
for (pixelZ = minZ; pixelZ < maxZ; pixelZ++) {
// Build a list of nodes
nodes = 0;
j = n - 1;
for (i = 0; i < n; i++) {
if (points.get(i).getBlockZ() < (double) pixelZ
&& points.get(j).getBlockZ() >= (double) pixelZ
|| points.get(j).getBlockZ() < (double) pixelZ
&& points.get(i).getBlockZ() >= (double) pixelZ) {
nodeX[nodes++] = (int) (points.get(i).getBlockX()
+ (pixelZ - points.get(i).getBlockZ())
/ (points.get(j).getBlockZ() - points.get(i)
.getBlockZ())
* (points.get(j).getBlockX() - points.get(i)
.getBlockX()));
}
j = i;
}
// Sort the nodes, via a simple bubble sort
i = 0;
while (i < nodes - 1) {
if (nodeX[i] > nodeX[i + 1]) {
swap = nodeX[i];
nodeX[i] = nodeX[i + 1];
nodeX[i + 1] = swap;
if (i > 0)
i--;
} else {
i++;
}
}
// Fill the pixels between node pairs
for (i = 0; i < nodes; i += 2) {
for (j = nodeX[i]; j < nodeX[i + 1]; j++) {
for (int y = minY; y >= maxY; y++) {
items.add(new BlockVector(j, y, pixelZ));
}
}
}
}
return items.iterator();*/
}
/**
* A terrible polygonal region iterator.
*/
public class Polygonal2DRegionIterator implements Iterator<BlockVector> {
protected List<BlockVector2D> points = new ArrayList<BlockVector2D>();
protected int minX;
protected int minY;
protected int minZ;
protected int maxX;
protected int maxY;
protected int maxZ;
protected int n;
protected int i;
protected int curX;
protected int curZ;
protected int curY;
protected BlockVector next;
public Polygonal2DRegionIterator(Polygonal2DRegion region) {
points = new ArrayList<BlockVector2D>(region.points);
Vector min = region.getMinimumPoint();
Vector max = region.getMaximumPoint();
minX = min.getBlockX();
minY = min.getBlockY();
minZ = min.getBlockZ();
maxX = max.getBlockX();
maxY = max.getBlockY();
maxZ = max.getBlockZ();
n = (maxX - minX + 1) * (maxZ - minZ + 1);
i = 0;
curX = 0;
curZ = 0;
curY = minY;
next = null;
findNext();
}
private void findNext() {
if (i >= n) {
next = null;
return;
}
if (next != null && curY <= maxY) {
curY++;
next = new BlockVector(curX, curY, curZ);
if (curY > maxY) {
i++;
curY = minY;
} else {
return;
}
}
while (i < n) {
curZ = i / (maxX - minX + 1) + minZ;
curX = (i % (maxX - minX + 1)) + minX;
BlockVector pt = new BlockVector(curX, minY, curZ);
if (contains(points, minY, maxY, pt)) {
next = pt;
return;
}
i++;
}
next = null;
}
public boolean hasNext() {
return next != null;
}
public BlockVector next() {
BlockVector next = this.next;
findNext();
return next;
}
public void remove() {
throw new UnsupportedOperationException("Not supported");
}
}
}

View File

@ -0,0 +1,114 @@
// $Id$
/*
* WorldEdit
* Copyright (C) 2010, 2011 sk89q <http://www.sk89q.com>
*
* 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;
import java.util.ArrayList;
import java.util.List;
import com.sk89q.worldedit.BlockVector;
import com.sk89q.worldedit.BlockVector2D;
import com.sk89q.worldedit.IncompleteRegionException;
import com.sk89q.worldedit.LocalPlayer;
import com.sk89q.worldedit.Vector;
/**
* Selector for polygonal regions.
*
* @author sk89q
*/
public class Polygonal2DRegionSelector implements RegionSelector {
protected BlockVector pos1;
protected Polygonal2DRegion region = new Polygonal2DRegion();
public boolean selectPrimary(Vector pos) {
if (pos1 != null && pos1.equals(pos)) {
return false;
}
pos1 = pos.toBlockVector();
region = new Polygonal2DRegion();
region.addPoint(pos);
region.expandY(pos.getBlockY());
return true;
}
public boolean selectSecondary(Vector pos) {
if (region.size() > 0) {
List<BlockVector2D> points = region.getPoints();
BlockVector2D lastPoint = points.get(region.size() - 1);
if (lastPoint.getBlockX() == pos.getBlockX()
&& lastPoint.getBlockZ() == pos.getBlockZ()) {
return false;
}
if (points.size() >= 20) {
return false;
}
}
region.addPoint(pos);
region.expandY(pos.getBlockY());
return true;
}
public void explainPrimarySelection(LocalPlayer player, Vector pos) {
player.print("Starting a new polygon at " + pos + ".");
}
public void explainSecondarySelection(LocalPlayer player, Vector pos) {
player.print("Added point #" + region.size() + " at " + pos + ".");
}
public BlockVector getPrimaryPosition() throws IncompleteRegionException {
if (pos1 == null) {
throw new IncompleteRegionException();
}
return pos1;
}
public Region getRegion() throws IncompleteRegionException {
if (!isDefined()) {
throw new IncompleteRegionException();
}
return region;
}
public boolean isDefined() {
return region.size() > 2;
}
public void learnChanges() {
}
public void clear() {
pos1 = null;
region = new Polygonal2DRegion();
}
public String getTypeName() {
return "2Dx1D polygon";
}
public List<String> getInformationLines() {
List<String> lines = new ArrayList<String>();
lines.add("# points: " + region.size());
return lines;
}
}

View File

@ -19,15 +19,16 @@
package com.sk89q.worldedit.regions;
import com.sk89q.worldedit.BlockVector;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.Vector2D;
import java.util.Set;
/**
*
* @author Albert
* @author sk89q
*/
public interface Region extends Iterable<Vector> {
public interface Region extends Iterable<BlockVector> {
/**
* Get the lower point of a region.
*
@ -45,7 +46,7 @@ public interface Region extends Iterable<Vector> {
*
* @return number of blocks
*/
public int getSize();
public int getArea();
/**
* Get X-size.
*
@ -68,14 +69,23 @@ public interface Region extends Iterable<Vector> {
* Expand the region.
*
* @param change
* @throws RegionOperationException
*/
public void expand(Vector change);
public void expand(Vector change) throws RegionOperationException;
/**
* Contract the region.
*
* @param change
* @throws RegionOperationException
*/
public void contract(Vector change);
public void contract(Vector change) throws RegionOperationException;
/**
* Returns true based on whether the region contains the point,
*
* @param pt
* @return
*/
public boolean contains(Vector pt);
/**
* Get a list of chunks.
*

View File

@ -0,0 +1,30 @@
// $Id$
/*
* WorldEdit
* Copyright (C) 2010, 2011 sk89q <http://www.sk89q.com>
*
* 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;
import com.sk89q.worldedit.WorldEditException;
public class RegionOperationException extends WorldEditException {
private static final long serialVersionUID = -6180325009115242142L;
public RegionOperationException(String msg) {
super(msg);
}
}

View File

@ -0,0 +1,112 @@
// $Id$
/*
* WorldEdit
* Copyright (C) 2010, 2011 sk89q <http://www.sk89q.com>
*
* 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;
import java.util.List;
import com.sk89q.worldedit.BlockVector;
import com.sk89q.worldedit.IncompleteRegionException;
import com.sk89q.worldedit.LocalPlayer;
import com.sk89q.worldedit.Vector;
/**
* Region selection factory.
*
* @author sk89q
*/
public interface RegionSelector {
/**
* Called when the first point is selected.
*
* @param pos
* @return true if something changed
*/
public boolean selectPrimary(Vector pos);
/**
* Called when the second point is selected.
*
* @param pos
* @return true if something changed
*/
public boolean selectSecondary(Vector pos);
/**
* Tell the player information about his/her primary selection.
*
* @param player
* @param pos
*/
public void explainPrimarySelection(LocalPlayer player, Vector pos);
/**
* Tell the player information about his/her secondary selection.
*
* @param player
* @param pos
*/
public void explainSecondarySelection(LocalPlayer player, Vector pos);
/**
* Get the primary position.
*
* @return
* @throws IncompleteRegionException
*/
public BlockVector getPrimaryPosition() throws IncompleteRegionException;
/**
* Get the selection.
*
* @return
* @throws IncompleteRegionException
*/
public Region getRegion() throws IncompleteRegionException;
/**
* Returns whether the region has been fully defined.
*
* @return
*/
public boolean isDefined();
/**
* Update the selector with changes to the region.
*/
public void learnChanges();
/**
* Clear the selection.
*/
public void clear();
/**
* Get a lowercase name of this region selector type.
*
* @return
*/
public String getTypeName();
/**
* Get lines of information about the selection.
*
* @return
*/
public List<String> getInformationLines();
}