/*
* Copyright 2012 Benjamin Glatzel <benjamin.glatzel@me.com>
*
* 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 javax.vecmath.Vector3f;
import java.util.Iterator;
/**
* Describes an axis-aligned bounded space in 3D integer.
*
* @author Immortius
*/
public class Region3i implements Iterable<Vector3i> {
public static final Region3i EMPTY = new Region3i();
private final Vector3i min = new Vector3i();
private final Vector3i size = new Vector3i();
public static Region3i createFromMinAndSize(Vector3i min, Vector3i size) {
if (size.x <= 0 || size.y <= 0 || size.z <= 0) {
return EMPTY;
}
return new Region3i(min, size);
}
public static Region3i createFromCenterExtents(Vector3f center, Vector3f 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));
}
public static Region3i createFromCenterExtents(Vector3i center, Vector3i 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);
}
public static Region3i createFromCenterExtents(Vector3i 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);
}
public static Region3i createBounded(Vector3i a, Vector3i b) {
Vector3i min = new Vector3i(a);
min.min(b);
Vector3i max = new Vector3i(a);
max.max(b);
return createFromMinMax(min, max);
}
public static Region3i createFromMinMax(Vector3i min, Vector3i 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);
}
/**
* Constructs an empty Region with size (0,0,0).
*/
public Region3i() {
}
private Region3i(Vector3i min, Vector3i size) {
this.min.set(min);
this.size.set(size);
}
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);
}
/**
* @return The size of the region
*/
public Vector3i size() {
return new Vector3i(size);
}
/**
* @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;
}
/**
* @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 min = min();
min.max(other.min());
Vector3i max = max();
max.min(other.max());
return createFromMinMax(min, max);
}
/**
* 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 min = min();
min.sub(amount, amount, amount);
Vector3i max = max();
max.add(amount, amount, amount);
return createFromMinMax(min, max);
}
public Region3i expand(Vector3i amount) {
Vector3i min = min();
min.sub(amount);
Vector3i max = max();
max.add(amount);
return createFromMinMax(min, max);
}
public Region3i expandToContain(Vector3i adjPos) {
Vector3i min = min();
min.min(adjPos);
Vector3i max = max();
max.max(adjPos);
return createFromMinMax(min, max);
}
/**
* @return The position at the center of the region
*/
public Vector3f center() {
Vector3f result = min.toVector3f();
result.add(size.toVector3f());
result.scale(0.5f);
return result;
}
/**
* @param offset
* @return A copy of the region offset by the given value
*/
public Region3i move(Vector3i offset) {
Vector3i newMin = min();
newMin.add(offset);
return Region3i.createFromMinAndSize(newMin, size);
}
/**
* @param pos
* @return Whether this region includes pos
*/
public boolean encompasses(Vector3i pos) {
return encompasses(pos.x, pos.y, pos.z);
}
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(Vector3i pos) {
Vector3i result = new Vector3i(pos);
result.min(max());
result.max(min);
return result;
}
@Override
public Iterator<Vector3i> iterator() {
return new Region3iIterator(this);
}
@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;
Vector3i result = new Vector3i();
public Region3iIterator(Region3i region) {
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.");
}
}
}