/*
* 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 java.math.RoundingMode;
import org.terasology.math.geom.Vector3f;
import org.terasology.math.geom.Vector3i;
import org.terasology.world.chunks.ChunkConstants;
/**
* Collection of math functions.
*
*/
public final class ChunkMath {
private ChunkMath() {
}
/**
* Returns the chunk position of a given coordinate.
*
* @param x The X-coordinate of the block
* @return The X-coordinate of the chunk
*/
public static int calcChunkPosX(int x, int chunkPowerX) {
return (x >> chunkPowerX);
}
/**
* Returns the chunk position of a given coordinate.
*
* @param y The Y-coordinate of the block
* @return The Y-coordinate of the chunk
*/
public static int calcChunkPosY(int y, int chunkPowerY) {
return (y >> chunkPowerY);
}
/**
* Returns the chunk position of a given coordinate.
*
* @param z The Z-coordinate of the block
* @return The Z-coordinate of the chunk
*/
public static int calcChunkPosZ(int z, int chunkPowerZ) {
return (z >> chunkPowerZ);
}
public static int calcChunkPosX(int x) {
return calcChunkPosX(x, ChunkConstants.CHUNK_POWER.x);
}
public static int calcChunkPosY(int y) {
return calcChunkPosY(y, ChunkConstants.CHUNK_POWER.y);
}
public static int calcChunkPosZ(int z) {
return calcChunkPosZ(z, ChunkConstants.CHUNK_POWER.z);
}
public static Vector3i calcChunkPos(Vector3i pos, Vector3i chunkPower) {
return calcChunkPos(pos.x, pos.y, pos.z, chunkPower);
}
public static Vector3i calcChunkPos(Vector3f pos) {
return calcChunkPos(new Vector3i(pos, RoundingMode.HALF_UP));
}
public static Vector3i calcChunkPos(Vector3i pos) {
return calcChunkPos(pos.x, pos.y, pos.z);
}
public static Vector3i calcChunkPos(int x, int y, int z) {
return calcChunkPos(x, y, z, ChunkConstants.CHUNK_POWER);
}
public static Vector3i[] calcChunkPos(Region3i region) {
return calcChunkPos(region, ChunkConstants.CHUNK_POWER);
}
public static Vector3i calcChunkPos(int x, int y, int z, Vector3i chunkPower) {
return new Vector3i(calcChunkPosX(x, chunkPower.x), calcChunkPosY(y, chunkPower.y), calcChunkPosZ(z, chunkPower.z));
}
public static Vector3i[] calcChunkPos(Region3i region, Vector3i chunkPower) {
int minX = calcChunkPosX(region.minX(), chunkPower.x);
int minY = calcChunkPosY(region.minY(), chunkPower.y);
int minZ = calcChunkPosZ(region.minZ(), chunkPower.z);
int maxX = calcChunkPosX(region.maxX(), chunkPower.x);
int maxY = calcChunkPosY(region.maxY(), chunkPower.y);
int maxZ = calcChunkPosZ(region.maxZ(), chunkPower.z);
int size = (maxX - minX + 1) * (maxY - minY + 1) * (maxZ - minZ + 1);
Vector3i[] result = new Vector3i[size];
int index = 0;
for (int x = minX; x <= maxX; x++) {
for (int y = minY; y <= maxY; y++) {
for (int z = minZ; z <= maxZ; z++) {
result[index++] = new Vector3i(x, y, z);
}
}
}
return result;
}
/**
* Returns the internal position of a block within a chunk.
*
* @param blockX The X-coordinate of the block in the world
* @return The X-coordinate of the block within the chunk
*/
public static int calcBlockPosX(int blockX, int chunkPosFilterX) {
return blockX & chunkPosFilterX;
}
public static int calcBlockPosY(int blockY, int chunkPosFilterY) {
return blockY & chunkPosFilterY;
}
/**
* Returns the internal position of a block within a chunk.
*
* @param blockZ The Z-coordinate of the block in the world
* @return The Z-coordinate of the block within the chunk
*/
public static int calcBlockPosZ(int blockZ, int chunkPosFilterZ) {
return blockZ & chunkPosFilterZ;
}
public static int calcBlockPosX(int blockX) {
return calcBlockPosX(blockX, ChunkConstants.INNER_CHUNK_POS_FILTER.x);
}
public static int calcBlockPosY(int blockY) {
return calcBlockPosY(blockY, ChunkConstants.INNER_CHUNK_POS_FILTER.y);
}
public static int calcBlockPosZ(int blockZ) {
return calcBlockPosZ(blockZ, ChunkConstants.INNER_CHUNK_POS_FILTER.z);
}
public static Vector3i calcBlockPos(Vector3i worldPos) {
return calcBlockPos(worldPos.x, worldPos.y, worldPos.z, ChunkConstants.INNER_CHUNK_POS_FILTER);
}
public static Vector3i calcBlockPos(int x, int y, int z) {
return calcBlockPos(x, y, z, ChunkConstants.INNER_CHUNK_POS_FILTER);
}
public static Vector3i calcBlockPos(int x, int y, int z, Vector3i chunkFilterSize) {
return new Vector3i(calcBlockPosX(x, chunkFilterSize.x), calcBlockPosY(y, chunkFilterSize.y), calcBlockPosZ(z, chunkFilterSize.z));
}
public static Region3i getChunkRegionAroundWorldPos(Vector3i pos, int extent) {
Vector3i minPos = new Vector3i(-extent, -extent, -extent);
minPos.add(pos);
Vector3i maxPos = new Vector3i(extent, extent, extent);
maxPos.add(pos);
Vector3i minChunk = calcChunkPos(minPos);
Vector3i maxChunk = calcChunkPos(maxPos);
return Region3i.createFromMinMax(minChunk, maxChunk);
}
// TODO: This doesn't belong in this class, move it.
public static Side getSecondaryPlacementDirection(Vector3f direction, Vector3f normal) {
Side surfaceDir = Side.inDirection(normal);
Vector3f attachDir = surfaceDir.reverse().getVector3i().toVector3f();
Vector3f rawDirection = new Vector3f(direction);
float dot = rawDirection.dot(attachDir);
rawDirection.sub(new Vector3f(dot * attachDir.x, dot * attachDir.y, dot * attachDir.z));
return Side.inDirection(rawDirection.x, rawDirection.y, rawDirection.z).reverse();
}
/**
* Produces a region containing the region touching the side of the given region, both in and outside the region.
*
* @param region
* @param side
* @return
*/
public static Region3i getEdgeRegion(Region3i region, Side side) {
Vector3i sideDir = side.getVector3i();
Vector3i min = region.min();
Vector3i max = region.max();
Vector3i edgeMin = new Vector3i(min);
Vector3i edgeMax = new Vector3i(max);
if (sideDir.x < 0) {
edgeMin.x = min.x;
edgeMax.x = min.x;
} else if (sideDir.x > 0) {
edgeMin.x = max.x;
edgeMax.x = max.x;
} else if (sideDir.y < 0) {
edgeMin.y = min.y;
edgeMax.y = min.y;
} else if (sideDir.y > 0) {
edgeMin.y = max.y;
edgeMax.y = max.y;
} else if (sideDir.z < 0) {
edgeMin.z = min.z;
edgeMax.z = min.z;
} else if (sideDir.z > 0) {
edgeMin.z = max.z;
edgeMax.z = max.z;
}
return Region3i.createFromMinMax(edgeMin, edgeMax);
}
/**
* Populates a target array with the minimum value adjacent to each location, including the location itself.
* TODO: this is too specific for a general class like this. Move to a new class AbstractBatchPropagator
*
* @param source
* @param target
* @param populateMargins Whether to populate the edges of the target array
*/
public static void populateMinAdjacent2D(int[] source, int[] target, int dimX, int dimY, boolean populateMargins) {
System.arraycopy(source, 0, target, 0, target.length);
// 0 < x < dimX - 1; 0 < y < dimY - 1
for (int y = 1; y < dimY - 1; ++y) {
for (int x = 1; x < dimX - 1; ++x) {
target[x + y * dimX] = Math.min(Math.min(source[x + (y - 1) * dimX], source[x + (y + 1) * dimX]),
Math.min(source[x + 1 + y * dimX], source[x - 1 + y * dimX]));
}
}
if (populateMargins) {
// x == 0, y == 0
target[0] = Math.min(source[1], source[dimX]);
// 0 < x < dimX - 1, y == 0
for (int x = 1; x < dimX - 1; ++x) {
target[x] = Math.min(source[x - 1], Math.min(source[x + 1], source[x + dimX]));
}
// x == dimX - 1, y == 0
target[dimX - 1] = Math.min(source[2 * dimX - 1], source[dimX - 2]);
// 0 < y < dimY - 1
for (int y = 1; y < dimY - 1; ++y) {
// x == 0
target[y * dimX] = Math.min(source[dimX * (y - 1)], Math.min(source[dimX * (y + 1)], source[1 + dimX * y]));
// x == dimX - 1
target[dimX - 1 + y * dimX] = Math.min(source[dimX - 1 + dimX * (y - 1)], Math.min(source[dimX - 1 + dimX * (y + 1)], source[dimX - 2 + dimX * y]));
}
// x == 0, y == dimY - 1
target[dimX * (dimY - 1)] = Math.min(source[1 + dimX * (dimY - 1)], source[dimX * (dimY - 2)]);
// 0 < x < dimX - 1; y == dimY - 1
for (int x = 1; x < dimX - 1; ++x) {
target[x + dimX * (dimY - 1)] = Math.min(source[x - 1 + dimX * (dimY - 1)], Math.min(source[x + 1 + dimX * (dimY - 1)], source[x + dimX * (dimY - 2)]));
}
// x == dimX - 1; y == dimY - 1
target[dimX - 1 + dimX * (dimY - 1)] = Math.min(source[dimX - 2 + dimX * (dimY - 1)], source[dimX - 1 + dimX * (dimY - 2)]);
}
}
}