/*
* Copyright 2013 MovingBlocks
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.terasology.math;
import org.terasology.math.geom.BaseVector3f;
import org.terasology.math.geom.BaseVector3i;
import org.terasology.math.geom.Vector3f;
import org.terasology.math.geom.Vector3i;
import java.util.Iterator;
/**
* Describes an axis-aligned bounded space in 3D integer.
*
*/
public final class Region3i implements Iterable<Vector3i> {
public static final Region3i EMPTY = new Region3i();
private final Vector3i min = new Vector3i();
private final Vector3i size = new Vector3i();
/**
* Constructs an empty Region with size (0,0,0).
*/
private Region3i() {
}
private Region3i(BaseVector3i min, BaseVector3i size) {
this.min.set(min);
this.size.set(size);
}
/**
* @param min the min point of the region
* @param size the size of the region
* @return a new region base on the min point and region size, empty if the size is negative
*/
public static Region3i createFromMinAndSize(BaseVector3i min, BaseVector3i size) {
if (size.x() <= 0 || size.y() <= 0 || size.z() <= 0) {
return EMPTY;
}
return new Region3i(min, size);
}
/**
* Create a region with center point and x,y,z coordinate extents size
* @param center the center point of region
* @param extents the extents size of each side of region
* @return a new region base on the center point and extents size
*/
public static Region3i createFromCenterExtents(BaseVector3f center, BaseVector3f extents) {
Vector3f min = new Vector3f(center.x() - extents.x(), center.y() - extents.y(), center.z() - extents.z());
Vector3f max = new Vector3f(center.x() + extents.x(), center.y() + extents.y(), center.z() + extents.z());
max.x = max.x - Math.ulp(max.x);
max.y = max.y - Math.ulp(max.y);
max.z = max.z - Math.ulp(max.z);
return createFromMinMax(new Vector3i(min), new Vector3i(max));
}
/**
* Create a region with center point and x,y,z coordinate extents size
* @param center the center point of region
* @param extents the extents size of each side of region
* @return a new region base on the center point and extents size
*/
public static Region3i createFromCenterExtents(BaseVector3i center, BaseVector3i extents) {
Vector3i min = new Vector3i(center.x() - extents.x(), center.y() - extents.y(), center.z() - extents.z());
Vector3i max = new Vector3i(center.x() + extents.x(), center.y() + extents.y(), center.z() + extents.z());
return createFromMinMax(min, max);
}
/**
* Create a region with center point and extents size
* @param center the center point of region
* @param extents the extents size of region
* @return a new region base on the center point and extents size
*/
public static Region3i createFromCenterExtents(BaseVector3i center, int extent) {
Vector3i min = new Vector3i(center.x() - extent, center.y() - extent, center.z() - extent);
Vector3i max = new Vector3i(center.x() + extent, center.y() + extent, center.z() + extent);
return createFromMinMax(min, max);
}
/**
* Create a region by two point
* @param a vertex a
* @param b the diagonal vertex of a
* @return a new region base on vertex a and b
*/
public static Region3i createBounded(BaseVector3i a, BaseVector3i b) {
Vector3i min = new Vector3i(a);
min.min(b);
Vector3i max = new Vector3i(a);
max.max(b);
return createFromMinMax(min, max);
}
/**
* Create a region by two point
* @param min the min point of the region
* @param max the max point of the region
* @return a new region base on min and max point
*/
public static Region3i createFromMinMax(BaseVector3i min, BaseVector3i max) {
Vector3i size = new Vector3i(max.x() - min.x() + 1, max.y() - min.y() + 1, max.z() - min.z() + 1);
if (size.x <= 0 || size.y <= 0 || size.z <= 0) {
return EMPTY;
}
return new Region3i(min, size);
}
public static Region3i createEncompassing(Region3i a, Region3i b) {
if (a.isEmpty()) {
return b;
}
if (b.isEmpty()) {
return a;
}
Vector3i min = a.min();
min.min(b.min());
Vector3i max = a.max();
max.max(b.max());
return createFromMinMax(min, max);
}
public boolean isEmpty() {
return size.x + size.y + size().z == 0;
}
/**
* @return The smallest vector in the region
*/
public Vector3i min() {
return new Vector3i(min);
}
public int minX() {
return min.x;
}
public int minY() {
return min.y;
}
public int minZ() {
return min.z;
}
/**
* @return The size of the region
*/
public Vector3i size() {
return new Vector3i(size);
}
public int sizeX() {
return size.x;
}
public int sizeY() {
return size.y;
}
public int sizeZ() {
return size.z;
}
/**
* @return The largest vector in the region
*/
public Vector3i max() {
Vector3i max = new Vector3i(min);
max.add(size);
max.sub(1, 1, 1);
return max;
}
public int maxX() {
return min.x + size.x - 1;
}
public int maxY() {
return min.y + size.y - 1;
}
public int maxZ() {
return min.z + size.z - 1;
}
/**
* @param other
* @return The region that is encompassed by both this and other. If they
* do not overlap then the empty region is returned
*/
public Region3i intersect(Region3i other) {
Vector3i intersectMin = min();
intersectMin.max(other.min());
Vector3i intersectMax = max();
intersectMax.min(other.max());
return createFromMinMax(intersectMin, intersectMax);
}
/**
* @param other
* @return An iterator over the positions in this region that aren't in other
*/
public Iterator<Vector3i> subtract(Region3i other) {
return new SubtractiveIterator(other);
}
/**
* Creates a new region that is the same as this region but expanded in all directions by the given amount
*
* @param amount
* @return A new region
*/
public Region3i expand(int amount) {
Vector3i expandedMin = min();
expandedMin.sub(amount, amount, amount);
Vector3i expandedMax = max();
expandedMax.add(amount, amount, amount);
return createFromMinMax(expandedMin, expandedMax);
}
public Region3i expand(BaseVector3i amount) {
Vector3i expandedMin = min();
expandedMin.sub(amount);
Vector3i expandedMax = max();
expandedMax.add(amount);
return createFromMinMax(expandedMin, expandedMax);
}
public Region3i expandToContain(BaseVector3i adjPos) {
Vector3i expandedMin = min();
expandedMin.min(adjPos);
Vector3i expandedMax = max();
expandedMax.max(adjPos);
return createFromMinMax(expandedMin, expandedMax);
}
/**
* @return The position at the center of the region
*/
public Vector3f center() {
Vector3f result = min.toVector3f();
Vector3f halfSize = size.toVector3f();
halfSize.scale(0.5f);
result.add(halfSize);
return result;
}
/**
* @param offset
* @return A copy of the region offset by the given value
*/
public Region3i move(BaseVector3i offset) {
Vector3i newMin = min();
newMin.add(offset);
return Region3i.createFromMinAndSize(newMin, size);
}
/**
* @param pos
* @return Whether this region includes pos
*/
public boolean encompasses(BaseVector3i pos) {
return encompasses(pos.getX(), pos.getY(), pos.getZ());
}
public boolean encompasses(int x, int y, int z) {
return (x >= min.x) && (y >= min.y) && (z >= min.z) && (x < min.x + size.x) && (y < min.y + size.y) && (z < min.z + size.z);
}
/**
* @param pos
* @return The nearest position within the region to the given pos.
*/
public Vector3i getNearestPointTo(BaseVector3i pos) {
Vector3i result = new Vector3i(pos);
result.min(max());
result.max(min);
return result;
}
@Override
public Iterator<Vector3i> iterator() {
return new Region3iIterator();
}
@Override
public String toString() {
return "(Min: " + min + ", Size: " + size + ")";
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj instanceof Region3i) {
Region3i other = (Region3i) obj;
return min.equals(other.min) && size.equals(other.size);
}
return false;
}
@Override
public int hashCode() {
int hash = 37;
hash += 37 * hash + min.hashCode();
hash += 37 * hash + size.hashCode();
return hash;
}
private class Region3iIterator implements Iterator<Vector3i> {
Vector3i pos;
Region3iIterator() {
this.pos = new Vector3i();
}
@Override
public boolean hasNext() {
return pos.x < size.x;
}
@Override
public Vector3i next() {
Vector3i result = new Vector3i(pos.x + min.x, pos.y + min.y, pos.z + min.z);
pos.z++;
if (pos.z >= size.z) {
pos.z = 0;
pos.y++;
if (pos.y >= size.y) {
pos.y = 0;
pos.x++;
}
}
return result;
}
@Override
public void remove() {
throw new UnsupportedOperationException("Not supported.");
}
}
private class SubtractiveIterator implements Iterator<Vector3i> {
private Iterator<Vector3i> innerIterator;
private Vector3i next;
private Region3i other;
SubtractiveIterator(Region3i other) {
this.other = other;
innerIterator = iterator();
updateNext();
}
private void updateNext() {
while (innerIterator.hasNext()) {
next = innerIterator.next();
if (!other.encompasses(next)) {
return;
}
}
next = null;
}
@Override
public boolean hasNext() {
return next != null;
}
@Override
public Vector3i next() {
Vector3i result = new Vector3i(next);
updateNext();
return result;
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
}
}