/*
* Copyright (c) 2015 NOVA, All rights reserved.
* This library is free software, licensed under GNU Lesser General Public License version 3
*
* This file is part of NOVA.
*
* NOVA 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.
*
* NOVA 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 NOVA. If not, see <http://www.gnu.org/licenses/>.
*/
package nova.core.util.shape;
import nova.core.util.Direction;
import nova.core.util.math.Transformer;
import nova.core.util.math.Vector3DUtil;
import org.apache.commons.math3.geometry.euclidean.threed.Rotation;
import org.apache.commons.math3.geometry.euclidean.threed.Vector3D;
import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import java.util.function.Consumer;
/**
* A cuboid is a shape that represents a cube.
* @author Calclavia
*/
public class Cuboid extends Shape<Cuboid, Cuboid> {
public static final Cuboid ZERO = new Cuboid(Vector3D.ZERO, Vector3D.ZERO);
public static final Cuboid ONE = new Cuboid(Vector3D.ZERO, Vector3DUtil.ONE);
public final Vector3D min;
public final Vector3D max;
/**
* New cuboid defined by the specified vectors as bounds
*
* @param min min vector
* @param max max vextor
*/
public Cuboid(Vector3D min, Vector3D max) {
this.min = min;
this.max = max;
}
/**
* New Cuboid with the specified coordinates as bounds
*
* @param minX min x coord
* @param minY min y coord
* @param minZ min z coord
* @param maxX max x coord
* @param maxY max y coord
* @param maxZ max z coord
*/
public Cuboid(double minX, double minY, double minZ, double maxX, double maxY, double maxZ) {
this(new Vector3D(minX, minY, minZ), new Vector3D(maxX, maxY, maxZ));
}
@Override
public Cuboid add(Cuboid other) {
return new Cuboid(min.add(other.min), max.add(other.max));
}
/**
* Adds a vector to the cuboid
*
* @param other The vector to add
* @return The new cuboid
*/
public Cuboid add(Vector3D other) {
return new Cuboid(min.add(other), max.add(other));
}
@Override
public Cuboid add(double other) {
return new Cuboid(min.add(Vector3DUtil.ONE.scalarMultiply(other)), max.add(Vector3DUtil.ONE.scalarMultiply(other)));
}
public Cuboid $plus(Vector3D other) {
return add(other);
}
public Cuboid subtract(Vector3D other) {
return new Cuboid(min.subtract(other), max.subtract(other));
}
public Cuboid $minus(Vector3D other) {
return subtract(other);
}
@Override
public Cuboid multiply(double other) {
return new Cuboid(min.scalarMultiply(other), max.scalarMultiply(other));
}
@Override
public Cuboid reciprocal() {
return new Cuboid(Vector3DUtil.reciprocal(min), Vector3DUtil.reciprocal(max));
}
/**
* Expands the cuboid by a certain vector.
*
* @param other Given vector
* @return New cuboid
*/
public Cuboid expand(Vector3D other) {
return new Cuboid(min.subtract(other), max.add(other));
}
/**
* Expands the cuboid by a certain amount.
*
* @param other The amount
* @return New cuboid
*/
public Cuboid expand(double other) {
return new Cuboid(min.subtract(Vector3DUtil.ONE.scalarMultiply(other)), max.add(Vector3DUtil.ONE.scalarMultiply(other)));
}
/**
* Returns if this cuboid is a cube.
*
* @return If this cuboid is a cube.
*/
public boolean isCube() {
return size().getX() == size().getY() && size().getY() == size().getZ();
}
/**
* How large the cuboid is
*
* @return The size of the cuboid
*/
public Vector3D size() {
return max.subtract(min);
}
/**
* The center of the cuboid
*
* @return Vector representing the cuboid
*/
public Vector3D center() {
return Vector3DUtil.midpoint(max, min);
}
/**
* The volume of the cuboid
*
* @return The volume of the cuboid
*/
public double volume() {
return size().getX() * size().getY() * size().getZ();
}
/**
* The surface area of the cuboid
*
* @return The surface area of the cuvoid
*/
public double surfaceArea() {
return (2 * size().getX() * size().getZ()) + (2 * size().getX() * size().getY()) + (2 * size().getZ() * size().getY());
}
/**
* Checks if another cuboid is within this cuboid
*
* @param other Cuboid to check
* @return Result of the check
*/
public boolean intersects(Cuboid other) {
return (other.max.getX() >= min.getX() && other.min.getX() < max.getX()) ?
((other.max.getY() >= min.getY() && other.min.getY() < max.getY()) ? other.max.getZ() >= min.getZ() && other.min.getZ() < max.getZ() : false) : false;
}
/**
* Checks if a vector is within this cuboid.
*
* @param other Vector to check
* @return Result of the check
*/
public boolean intersects(Vector3D other) {
return other.getX() >= this.min.getX() && other.getX() < this.max.getX() ?
(other.getY() >= this.min.getY() && other.getY() < this.max.getY() ? other.getZ() >= this.min.getZ() && other.getZ() < this.max.getZ() : false) : false;
}
/**
* Applies the given transformer to the cuboid
*
* @param transform The transformer to apply
* @return The transformed cuboid
*/
public Cuboid transform(Transformer transform) {
Vector3D transMin = transform.apply(min);
Vector3D transMax = transform.apply(max);
return new Cuboid(Vector3DUtil.min(transMin, transMax), Vector3DUtil.max(transMax, transMin));
}
/**
* Applies the given rotation to the Cuboid
*
* @param transform The rotation to apply
* @return The rotated cuboid
*/
public Cuboid transform(Rotation transform) {
Vector3D transMin = transform.applyTo(min);
Vector3D transMax = transform.applyTo(max);
return new Cuboid(Vector3DUtil.min(transMin, transMax), Vector3DUtil.max(transMax, transMin));
}
public void forEach(Consumer<Vector3D> consumer) {
forEach(consumer::accept, 1);
}
public void forEach(Consumer<Vector3D> consumer, double step) {
for (double x = min.getX(); x < max.getX(); x += step)
for (double y = min.getY(); y < max.getY(); y += step)
for (double z = min.getZ(); z < max.getZ(); z += step)
consumer.accept(new Vector3D(x, y, z));
}
public Direction sideOf(Vector3D position) {
return Direction.fromVector(position.subtract(center()).normalize());
}
@Override
public String toString() {
MathContext cont = new MathContext(4, RoundingMode.HALF_UP);
return "Cuboid[" + new BigDecimal(min.getX(), cont) + ", " + new BigDecimal(min.getY(), cont) + ", " + new BigDecimal(min.getZ(), cont) + "] -> [" + new BigDecimal(max.getX(), cont) + ", " + new BigDecimal(max.getY(), cont) + ", " + new BigDecimal(max.getZ(), cont) + "]";
}
}