/*
* Aphelion
* Copyright (c) 2013-2014 Joris van der Wel
*
* This file is part of Aphelion
*
* Aphelion is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, version 3 of the License.
*
* Aphelion 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 Affero General Public License
* along with Aphelion. If not, see <http://www.gnu.org/licenses/>.
*
* In addition, the following supplemental terms apply, based on section 7 of
* the GNU Affero General Public License (version 3):
* a) Preservation of all legal notices and author attributions
* b) Prohibition of misrepresentation of the origin of this material, and
* modified versions are required to be marked in reasonable ways as
* different from the original version (for example by appending a copyright notice).
*
* Linking this library statically or dynamically with other modules is making a
* combined work based on this library. Thus, the terms and conditions of the
* GNU Affero General Public License cover the whole combination.
*
* As a special exception, the copyright holders of this library give you
* permission to link this library with independent modules to produce an
* executable, regardless of the license terms of these independent modules,
* and to copy and distribute the resulting executable under terms of your
* choice, provided that you also meet, for each linked independent module,
* the terms and conditions of the license of that module. An independent
* module is a module which is not derived from or based on this library.
*/
package aphelion.shared.physics;
import static aphelion.shared.physics.EnvironmentConf.ROTATION_POINTS;
import static aphelion.shared.physics.EnvironmentConf.ROTATION_1_2TH;
import static aphelion.shared.physics.EnvironmentConf.ROTATION_1_4TH;
import aphelion.shared.swissarmyknife.ThreadSafe;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
/**
*
* @author Joris
*/
public class PhysicsTrig
{
public static final int MAX_VALUE = 1 << 30; // foo / MAX_VALUE === foo >> 30
private PhysicsTrig()
{
}
/** Calculates the sine of a rotation angle.
* @param angle An angle in which ROTATION_POINTS = pi*2
* @return A value between -2^30 and 2^30 (1073741824)
*/
@ThreadSafe
public static int sin(int angle)
{
return sin((long) angle);
}
/** Calculates the cosine of a rotation angle.
* @param angle An angle in which ROTATION_POINTS = pi*2
* @return A value between -2^30 and 2^30 (1073741824)
*/
@ThreadSafe
public static int cos(int angle)
{
return sin((long) angle + ROTATION_1_4TH);
}
/** Calculates the tangent of a rotation angle.
* @param angle An angle in which ROTATION_POINTS = pi*2
* @return A value between -2^30 and 2^30 (1073741824)
* @throws ArithmeticException For invalid angles
*/
public static int tan(int angle)
{
return sin(angle) / cos(angle);
}
/** Calculates the cotangent of a rotation angle.
* @param angle An angle in which ROTATION_POINTS = pi*2
* @return A value between -2^30 and 2^30 (1073741824)
* @throws ArithmeticException For invalid angles
*/
public static int cotan(int angle)
{
return cos(angle) / sin(angle);
}
private static int sin(long angle)
{
angle += ROTATION_POINTS * 4L; // instead of abs()
angle %= ROTATION_POINTS;
long i = angle / LOOKUP_GAP;
long i_mod = angle - LOOKUP_GAP * i;
int y1 = LOOKUP_TABLE[(int) i];
int y2 = LOOKUP_TABLE[(int)i+1];
return (int) (y1 + (y2 - y1) * i_mod / LOOKUP_GAP);
}
private static final int LOOKUP_SIZE = 65520; // about 256 KiB
private static final long LOOKUP_GAP = ROTATION_POINTS / LOOKUP_SIZE;
private static int[] LOOKUP_TABLE = new int[LOOKUP_SIZE+1];
static
{
assert ROTATION_POINTS * 4L > Integer.MAX_VALUE;
assert ROTATION_POINTS % LOOKUP_SIZE == 0;
load();
}
private static void load()
{
try
{
InputStream in = PhysicsTrig.class.getResourceAsStream("/aphelion/shared/physics/trig-lookup.bin");
if (in == null)
{
throw new Error("Lookup file missing");
}
BufferedInputStream b = new BufferedInputStream(in);
int i = 0;
ByteBuffer entry = ByteBuffer.allocate(4);
while (true)
{
entry.clear();
int read = b.read(entry.array());
if (read < 0)
{
break;
}
if (read < 4)
{
throw new Error("Invalid lookup file");
}
LOOKUP_TABLE[i] = entry.getInt();
++i;
}
if (i != LOOKUP_SIZE+1)
{
throw new Error("Invalid amount of entries in look up file. Expected: " + (LOOKUP_SIZE+1) + "; found: "+i+";");
}
b.close();
in.close();
}
catch(IOException ex)
{
throw new Error(ex);
}
}
/**
* @param angle An angle in which ROTATION_POINTS = pi/2
* @return A value between -2^30 and 2^30 (1073741824)
*/
private static strictfp int phySin(double angle)
{
double s = StrictMath.sin(angle / ROTATION_1_2TH * Math.PI);
s *= MAX_VALUE;
return (int) s;
// Why not use always use this method?:
// It should be possible to support other platforms than java,
// which may not support fdlibm. This way all they have to do
// is load trig-lookup.bin
}
private static void generate() throws IOException
{
BufferedOutputStream b = new BufferedOutputStream(new FileOutputStream("trig-lookup.bin"));
ByteBuffer entry = ByteBuffer.allocate(4);
for (int i = 0; i < LOOKUP_SIZE+1; ++i)
{
int r = phySin(i * LOOKUP_GAP);
entry.clear();
entry.putInt(r);
b.write(entry.array());
}
b.close();
System.out.println("done");
}
private static String test(int x)
{
return sin(x) + "\t" + phySin(x) + "\n";
}
public static void main(String[] args) throws Exception
{
/** /
FileWriter b = new FileWriter("bla.csv");
for (int x = -ROTATION_POINTS*2; x <= ROTATION_POINTS*2; x += 4000)
{
b.write(test(x));
}
b.close();
/**/
generate();
}
}