// $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.HashSet; import java.util.Iterator; import java.util.Set; 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.data.ChunkStore; /** * Represents a cylindrical region. * * @author yetanotherx */ public class CylinderRegion extends AbstractRegion implements FlatRegion { private Vector center; private Vector2D center2D; private Vector2D radius; private int minY; private int maxY; private boolean hasY = false; /** * Construct the region */ public CylinderRegion() { this((LocalWorld) null); } /** * Construct the region. * * @param world */ public CylinderRegion(LocalWorld world) { this(world, new Vector(), new Vector2D(), 0, 0); hasY = false; } /** * Construct the region. * * @param world * @param center * @param radius * @param minY * @param maxY */ public CylinderRegion(LocalWorld world, Vector center, Vector2D radius, int minY, int maxY) { super(world); setCenter(center); setRadius(radius); this.minY = minY; this.maxY = maxY; hasY = true; } public CylinderRegion(CylinderRegion region) { this(region.world, region.center, region.getRadius(), region.minY, region.maxY); hasY = region.hasY; } /** * Returns the main center point of the cylinder * * @return */ public Vector getCenter() { return center; } /** * Sets the main center point of the region * */ public void setCenter(Vector center) { this.center = center; this.center2D = center.toVector2D(); } /** * Returns the radius of the cylinder * * @return */ public Vector2D getRadius() { return radius.subtract(0.5, 0.5); } /** * Sets the radius of the cylinder * * @param radius */ public void setRadius(Vector2D radius) { this.radius = radius.add(0.5, 0.5); } /** * Extends the radius to be at least the given radius * * @param minRadius */ public void extendRadius(Vector2D minRadius) { setRadius(Vector2D.getMaximum(minRadius, getRadius())); } /** * Set the minimum Y. * * @param y */ public void setMinimumY(int y) { hasY = true; minY = y; } /** * Se the maximum Y. * * @param y */ public void setMaximumY(int y) { hasY = true; maxY = y; } /** * Get the lower point of a region. * * @return min. point */ public Vector getMinimumPoint() { return center2D.subtract(getRadius()).toVector(minY); } /** * Get the upper point of a region. * * @return max. point */ public Vector getMaximumPoint() { return center2D.add(getRadius()).toVector(maxY); } /** * Gets the maximum Y value * @return */ public int getMaximumY() { return maxY; } /** * Gets the minimum Y value * @return */ public int getMinimumY() { return minY; } /** * Get the number of blocks in the region. * * @return number of blocks */ public int getArea() { return (int) Math.floor(radius.getX() * radius.getZ() * Math.PI * getHeight()); } /** * Get X-size. * * @return width */ public int getWidth() { return (int) (2 * radius.getX()); } /** * Get Y-size. * * @return height */ public int getHeight() { return maxY - minY + 1; } /** * Get Z-size. * * @return length */ 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 */ public void expand(Vector... changes) throws RegionOperationException { setCenter(getCenter().add(calculateDiff2D(changes).toVector())); radius = radius.add(calculateChanges2D(changes)); 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 */ public void contract(Vector... changes) throws RegionOperationException { setCenter(getCenter().subtract(calculateDiff2D(changes).toVector())); Vector2D newRadius = radius.subtract(calculateChanges2D(changes)); radius = Vector2D.getMaximum(new Vector2D(1.5, 1.5), newRadius); for (Vector change : changes) { int changeY = change.getBlockY(); if (changeY > 0) { minY += changeY; } else { maxY += changeY; } } } @Override public void shift(Vector change) throws RegionOperationException { setCenter(getCenter().add(change)); int changeY = change.getBlockY(); maxY += changeY; minY += changeY; } /** * Checks to see if a point is inside this region. */ public boolean contains(Vector pt) { final int blockY = pt.getBlockY(); if (blockY < minY || blockY > maxY) { return false; } return pt.toVector2D().subtract(center2D).divide(radius).lengthSq() <= 1; } /** * Get a list of chunks. * * @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 z = min.getBlockZ(); z <= max.getBlockZ(); ++z) { if (contains(new BlockVector(x, minY, z))) { chunks.add(new BlockVector2D(x >> ChunkStore.CHUNK_SHIFTS, z >> ChunkStore.CHUNK_SHIFTS)); } } } return chunks; } @Override public Set getChunkCubes() { 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) { if (contains(new BlockVector(x, y, z))) { chunks.add(new BlockVector(x >> ChunkStore.CHUNK_SHIFTS, y >> ChunkStore.CHUNK_SHIFTS, z >> ChunkStore.CHUNK_SHIFTS)); } } } } return chunks; } /** * Sets the height of the cylinder to fit the specified Y. * * @param y * @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, centerY, centerZ) - (radiusX, radiusZ)" * * @return string */ @Override public String toString() { return center + " - " + radius; } public CylinderRegion clone() { return (CylinderRegion) super.clone(); } }