/*
* This file is part of NucleusFramework for Bukkit, licensed under the MIT License (MIT).
*
* Copyright (c) JCThePants (www.jcwhatever.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.jcwhatever.nucleus.internal.providers.math;
import com.jcwhatever.nucleus.internal.providers.InternalProviderInfo;
import com.jcwhatever.nucleus.providers.Provider;
import com.jcwhatever.nucleus.providers.math.IFastMathProvider;
import com.jcwhatever.nucleus.providers.math.IRotationMatrix;
public class NucleusFastMathProvider extends Provider implements IFastMathProvider {
public static final String NAME = "NucleusFastMath";
static final XORShiftRandom XOR_RANDOM = new XORShiftRandom();
private static final int SIN_BITS = 12;
private static final int SIN_MASK = ~(-1 << SIN_BITS);
private static final float DEG_TO_INDEX;
private static final float[] SIN_TABLE, COS_TABLE;
private static final int ATAN2_BITS = 12;
private static final int ATAN2_BITS2 = ATAN2_BITS << 1;
private static final int ATAN2_MASK = ~(-1 << ATAN2_BITS2);
private static final int ATAN2_COUNT = ATAN2_MASK + 1;
private static final int ATAN2_DIM = (int) Math.sqrt(ATAN2_COUNT);
private static final float ATAN2_DIM_MINUS_1 = (ATAN2_DIM - 1);
private static final float[] ATAN2_TABLE = new float[ATAN2_COUNT];
static {
// cos/sin code from http://riven8192.blogspot.com/2009/08/fastmath-sincos-lookup-tables.html
// licensed: http://creativecommons.org/licenses/by/3.0/
int sinCount = SIN_MASK + 1;
float degFull = (float) (360.0);
float radFull = (float) (Math.PI * 2.0);
DEG_TO_INDEX = sinCount / degFull;
SIN_TABLE = new float[sinCount];
COS_TABLE = new float[sinCount];
for (int i = 0; i < sinCount; i++) {
SIN_TABLE[i] = (float) Math.sin((i + 0.5f) / sinCount * radFull);
COS_TABLE[i] = (float) Math.cos((i + 0.5f) / sinCount * radFull);
}
for (int i = 0; i < 360; i += 90) {
SIN_TABLE[(int)(i * DEG_TO_INDEX) & SIN_MASK] = (float)Math.sin(i * Math.PI / 180.0);
COS_TABLE[(int)(i * DEG_TO_INDEX) & SIN_MASK] = (float)Math.cos(i * Math.PI / 180.0);
}
// atan2 code from http://riven8192.blogspot.com/2009/08/fastmath-atan2-lookup-table.html
// licensed: http://creativecommons.org/licenses/by/3.0/
for (int i = 0; i < ATAN2_DIM; i++) {
for (int j = 0; j < ATAN2_DIM; j++) {
float x = (float) i / ATAN2_DIM;
float y = (float) j / ATAN2_DIM;
ATAN2_TABLE[j * ATAN2_DIM + i] = (float) Math.atan2(y, x);
}
}
}
public NucleusFastMathProvider() {
setInfo(new InternalProviderInfo(this.getClass(),
NAME, "Default fast math provider."));
}
@Override
public float sin(double angleDegrees) {
return SIN_TABLE[(int) (angleDegrees * DEG_TO_INDEX) & SIN_MASK];
}
@Override
public float cos(double angleDegrees) {
return COS_TABLE[(int) (angleDegrees * DEG_TO_INDEX) & SIN_MASK];
}
@Override
public float tan(double angleDegrees) {
return (float)Math.tan(Math.toRadians(angleDegrees));
}
@Override
public float asin(double angleDegrees) {
return (float)Math.toDegrees(Math.asin(Math.toRadians(angleDegrees)));
}
@Override
public float acos(double angleDegrees) {
return (float)Math.toDegrees(Math.acos(Math.toRadians(angleDegrees)));
}
@Override
public float atan(double angleDegrees) {
return (float)Math.toDegrees(Math.atan(Math.toRadians(angleDegrees)));
}
@Override
public float atan2(double y, double x) {
float add, mul;
if (x < 0.0f) {
if (y < 0.0f) {
x = -x;
y = -y;
mul = 1.0f;
} else {
x = -x;
mul = -1.0f;
}
add = -3.141592653f;
} else {
if (y < 0.0f) {
y = -y;
mul = -1.0f;
}
else {
mul = 1.0f;
}
add = 0.0f;
}
float invDiv = ATAN2_DIM_MINUS_1 / (float)((x < y) ? y : x);
int xi = Math.min((int) (x * invDiv), (int)ATAN2_DIM_MINUS_1);
int yi = Math.min((int) (y * invDiv), (int)ATAN2_DIM_MINUS_1);
return (ATAN2_TABLE[yi * ATAN2_DIM + xi] + add) * mul;
}
@Override
public float sqrt(double value) {
return (float)Math.sqrt(value); // this is already very fast
}
@Override
public float cbrt(double value) {
return (float)Math.cbrt(value);
}
@Override
public float sinh(double angleDegrees) {
return (float)Math.sinh(Math.toRadians(angleDegrees));
}
@Override
public float cosh(double angleDegrees) {
return (float)Math.cosh(Math.toRadians(angleDegrees));
}
@Override
public float tanh(double angleDegrees) {
return (float)Math.tanh(Math.toRadians(angleDegrees));
}
@Override
public long randomLong() {
return XOR_RANDOM.next();
}
@Override
public double randomDouble() {
return Math.random();
}
@Override
public IRotationMatrix getRotationMatrix(float angleDegrees) {
return NucleusRotationMatrix.get(angleDegrees);
}
private static class XORShiftRandom {
volatile long x = System.currentTimeMillis();
long next() {
long w = x;
w ^= (w << 21);
w ^= (w >>> 35);
w ^= (w << 4);
return x = w;
}
}
}