XOR Neural Network in Java - java

I'm trying to implement and train a five neuron neural network with back propagation for the XOR function in Java. My code (please excuse it's hideousness):
public class XORBackProp {
private static final int MAX_EPOCHS = 500;
//weights
private static double w13, w23, w14, w24, w35, w45;
private static double theta3, theta4, theta5;
//neuron outputs
private static double gamma3, gamma4, gamma5;
//neuron error gradients
private static double delta3, delta4, delta5;
//weight corrections
private static double dw13, dw14, dw23, dw24, dw35, dw45, dt3, dt4, dt5;
//learning rate
private static double alpha = 0.1;
private static double error;
private static double sumSqrError;
private static int epochs = 0;
private static boolean loop = true;
private static double sigmoid(double exponent)
{
return (1.0/(1 + Math.pow(Math.E, (-1) * exponent)));
}
private static void activateNeuron(int x1, int x2, int gd5)
{
gamma3 = sigmoid(x1*w13 + x2*w23 - theta3);
gamma4 = sigmoid(x1*w14 + x2*w24 - theta4);
gamma5 = sigmoid(gamma3*w35 + gamma4*w45 - theta5);
error = gd5 - gamma5;
weightTraining(x1, x2);
}
private static void weightTraining(int x1, int x2)
{
delta5 = gamma5 * (1 - gamma5) * error;
dw35 = alpha * gamma3 * delta5;
dw45 = alpha * gamma4 * delta5;
dt5 = alpha * (-1) * delta5;
delta3 = gamma3 * (1 - gamma3) * delta5 * w35;
delta4 = gamma4 * (1 - gamma4) * delta5 * w45;
dw13 = alpha * x1 * delta3;
dw23 = alpha * x2 * delta3;
dt3 = alpha * (-1) * delta3;
dw14 = alpha * x1 * delta4;
dw24 = alpha * x2 * delta4;
dt4 = alpha * (-1) * delta4;
w13 = w13 + dw13;
w14 = w14 + dw14;
w23 = w23 + dw23;
w24 = w24 + dw24;
w35 = w35 + dw35;
w45 = w45 + dw45;
theta3 = theta3 + dt3;
theta4 = theta4 + dt4;
theta5 = theta5 + dt5;
}
public static void main(String[] args)
{
w13 = 0.5;
w14 = 0.9;
w23 = 0.4;
w24 = 1.0;
w35 = -1.2;
w45 = 1.1;
theta3 = 0.8;
theta4 = -0.1;
theta5 = 0.3;
System.out.println("XOR Neural Network");
while(loop)
{
activateNeuron(1,1,0);
sumSqrError = error * error;
activateNeuron(0,1,1);
sumSqrError += error * error;
activateNeuron(1,0,1);
sumSqrError += error * error;
activateNeuron(0,0,0);
sumSqrError += error * error;
epochs++;
if(epochs >= MAX_EPOCHS)
{
System.out.println("Learning will take more than " + MAX_EPOCHS + " epochs, so program has terminated.");
System.exit(0);
}
System.out.println(epochs + " " + sumSqrError);
if (sumSqrError < 0.001)
{
loop = false;
}
}
}
}
If it helps any, here's a diagram of the network.
The initial values for all the weights and the learning rate are taken straight from an example in my textbook. The goal is to train the network until the sum of the squared errors is less than .001. The textbook also gives the values of all the weights after the first iteration (1,1,0) and I've tested my code and its results match the textbook's results perfectly. But according to the book, this should only take 224 epochs to converge. But when I run it, it always reaches MAX_EPOCHS unless it is set to several thousand. What am I doing wrong?

//Add this in the constants declaration section.
private static double alpha = 3.8, g34 = 0.13, g5 = 0.21;
// Add this in activate neuron
gamma3 = sigmoid(x1 * w13 + x2 * w23 - theta3);
gamma4 = sigmoid(x1 * w14 + x2 * w24 - theta4);
if (gamma3 > 1 - g34 ) {gamma3 = 1;}
if (gamma3 < g34) {gamma3 = 0;}
if (gamma4 > 1- g34) {gamma4 = 1;}
if (gamma4 < g34) {gamma4 = 0;}
gamma5 = sigmoid(gamma3 * w35 + gamma4 * w45 - theta5);
if (gamma5 > 1 - g5) {gamma5 = 1;}
if (gamma5 < g5) {gamma5 = 0;}
ANN should learn in 66 iterations, but is on the brink of divergence.

Try making rounding of gamma3, gamma4, gamma5 while in activation phase for instace:
if (gamma3 > 0.7) gamma3 = 1;
if (gamma3 < 0.3) gamma3 = 0;
and rise little bit learnig variable ( alpha )
alpha = 0.2;
learning ends in 466 epochs.
Of course if u make bigger rounding and higher alpha u set u can achieve even better result than 224.

Whole point of this network is to show how to deal with a situation when grouping isn't based on "top = yes, bottom = no", but rather there is a central line (going through points (0,1) and (1,0) in this case) and if value is close to the line, then answer is "yes", while if it is far, then answer is "no". You can't cluster such system with just one layer. However two layers is enough.

Related

My code does exactly what it has to do, but i have no idea why

is supposed to calculate the coordinates of a projectile launched with respect to time (steps of 100ms), with a linear equation, and it outputs linear numbers, but if i plot this equation with CalcMe.com (math tool) it makes a parabolic plot
InVel = Double.parseDouble(jTextField1.getText());
g = Double.parseDouble(jTextField8.getText());
y = 1;
while(y >= -1) {
t += 100;
x = InVel * TimeUnit.MILLISECONDS.toSeconds(t) * Math.cos(45);
y = InVel * TimeUnit.MILLISECONDS.toSeconds(t) * Math.sin(45) - (1 / 2) * g * Math.pow(TimeUnit.MILLISECONDS.toSeconds(t), 2);
//System.out.print(Double.toString(x));
//System.out.printf(" ");
System.out.print(Double.toString(y));
System.out.printf("%n");
}
jTextField6.setText(Double.toString(x));
the code is in java
g is constant (9.8)
and invel is given by user so its constant too
g is the gravity and invel the initial velocity of the projectile
the equation is:x=invel*time*cos(45) and y=invel*time*sin(45)-(1/2)*g*t^2
anyone can help me?
Your milisecond to second value conversion method TimeUnit.MILLISECONDS.toSeconds(t) is the main fact. Its returning long value which one you are wanted double. Please take a look on below code. Probably its your answer. Just replace hard-coded value with your jTextField
public static void main(String[] args) {
double InVel = Double.parseDouble("10.555");
double g = Double.parseDouble("9.8");
double y = 1;
double x=0;
double t=0;
while(y >= -1) {
t += 100;
double timeInSeconds = (t / (double)1000) % (double)60;
x = InVel * timeInSeconds * Math.cos(45);
y = InVel * timeInSeconds * Math.sin(45) - ((double) 1 / (double) 2) * g * Math.pow(timeInSeconds, 2);
//System.out.print(Double.toString(x));
//System.out.printf(" ");
System.out.println("X = " + x + " Y = " + Double.toString(y));
System.out.printf("%n");
}
}

Calling out a class giving me problems (bad class - class file contains wrong class) [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 7 years ago.
Improve this question
Here is the error i'm getting:
airfoilNumber.java:5: error: cannot access airfoil
private airfoil myAirfoil = new airfoil();
^
bad class file: ./airfoil.class
class file contains wrong class: airfoil.airfoil
Please remove or make sure it appears in the correct subdirectory of the classpath.
Here is my main class:
import java.util.Scanner;
public class airfoilNumber
{
private airfoil myAirfoil = new airfoil();
public static void main(String[] args)
{
Scanner numNaca1 = new Scanner(System.in); //make a scanner and prompt user for their desired NACA number
System.out.println("What is your NACA number?"); //prompt user for NACA number
int numNaca = numNaca1.nextInt(); //apply the number to numNaca
new airfoil(numNaca); //call out airfoil class and run calculations
}
}
Here is my calculator class:
package airfoil;
import java.text.DecimalFormat;
import java.text.NumberFormat;
public class airfoil
{
private static final int numOfCoord = 250;
double dx = 1
.0 / numOfCoord;
private double m; // maximum camber in % of chord
private double p; // chordwise position of max ord., 10th of chord
private double t; // thickness in % of the cord
private String nacaNum; // NACA number - 4 digits
private double[][] coordinates; // Coordinates of the upper half or lower half of the airfoil
private double[][] meanLine; // mean line coordinates
public airfoil(String number) {
nacaNum = number;
m = Double.parseDouble(nacaNum.substring(0,1)) / 100.0;
p = Double.parseDouble(nacaNum.substring(1,2)) / 10.0;
t = Double.parseDouble(nacaNum.substring(2,4)) / 100.0;
meanLine = new double[2][numOfCoord]; // x values row 0, y values row 1
// x upper = row 0,
// y upper = row 1,
// x lower = row 2,
// y lower = row 3
coordinates = new double [4][numOfCoord];
System.out.println("NACA: " + nacaNum);
System.out.println("Number of coordinates: " + numOfCoord);
calcMeanLine();
calcAirfoil();
}
/*
* Calculates the values for the mean line forward of the maximum
* ordinate and aft of the maximum ordinate.
*/
private void calcMeanLine() {
double x = dx;
int j = 0;
// fwd of max ordinate
while (x <= p) {
meanLine[0][j] = x;
meanLine[1][j] = (m / (p * p))*(2*p*x - (x*x));
x += dx;
j++;
}
// aft of max ordinate
while (x <= 1.0 + dx) {
meanLine[0][j] = x;
meanLine[1][j] = (m / ((1 - p) * (1 - p))) *
((1 - 2*p) + 2*p*x - x * x);
x += dx;
j++;
}
} // end calcMeanLine
/*
* Calculate the upper and lower coordinates of the airfoil surface.
*/
private void calcAirfoil() {
double theta; // arctan(dy_dx)
double dy; // derivative of mean line equation
double yt, ml; // thickness and meanline values, respectively
double x = dx; // x-value w.r.t. chord
int j = 0; // counter for array
// calculate upper/lower surface coordinates fwd of max ordinate
while (x <= p) {
dy = (m / (p*p)) * (2*p - 2*x);
theta = Math.atan(dy);
yt = thicknessEQ(x);
ml = meanLine[1][j];
// upper surface coordinates;
coordinates[0][j] = x - yt * Math.sin(theta);
coordinates[1][j] = ml + yt * Math.cos(theta);
// lower surface coordinates
coordinates[2][j] = x + yt*Math.sin(theta);
coordinates[3][j] = ml - yt * Math.cos(theta);
x += dx;
j++;
}
// calculate the coordinates aft of max ordinate
while (x <= 1.0 + dx) {
dy = (m / ((1 - p) * (1 - p))) * ((2 * p) - (2 * x));
theta = Math.atan(dy);
yt = thicknessEQ(x);
ml = meanLine[1][j];
// upper surface coordinates;
coordinates[0][j] = x - yt * Math.sin(theta);
coordinates[1][j] = ml + yt * Math.cos(theta);
// lower surface coordinates
coordinates[2][j] = x + yt * Math.sin(theta);
coordinates[3][j] = ml - yt * Math.cos(theta);
x += dx;
j++;
}
System.out.println("j = " + j);
} // end calcAirfoil
/*
* Thickness equation
*/
private double thicknessEQ(double x) {
return ((t / 0.2) * (0.2969 * Math.sqrt(x) - (0.126 * x) -
(0.3526 * x * x) + (0.28430 * x * x * x) -
(0.1015 * x * x * x * x)));
}
public String toString() {
String str = "";
NumberFormat df = new DecimalFormat("0.0000");
System.out.println("Xu\tYu\tXl\tYl");
for (int j = 0; j < numOfCoord; j++) {
str += df.format(coordinates[0][j]) + "\t" +
df.format(coordinates[1][j]) + "\t" +
df.format(coordinates[2][j]) + "\t" +
df.format(coordinates[3][j]) + "\n";
}
return str;
}
/*
* Return the coordinates array
*/
public double[][] getCoordinates() { return coordinates; }
public int getSize() { return numOfCoord; }
} // end Airfoil class
Here is what i've tried:
Moving the airfoil.class file around from place to place to get it to work
use "new airfoil ("");" by itself, still gives me the same error
used any other type of code to call out my calculator class, same error.
I don't know what else to change. I don't know what it's telling me about "Wrong class airfoil.airfoil", that might be able to lead me to the solution.
What am i doing wrong here?
error is in:
new airfoil(numNaca); //call out airfoil class and run calculations
delete this line and call:
myAirfoil(numNaca);
new airfoil make new instance of youre class, not run calculations.
New instance exist in code, see line:
private airfoil myAirfoil = new airfoil();
and acces to this is myAirfoil.
You have the following class structure:
package airfoil;
public class airfoil {
...
}
public class airfoilNumber {
...
}
This needs to be reflected on the file system like this:
MyProject Project's top level directory
+-airfoilNumber.java
+-airfoil directory for "airfoil" package
+-airfoil.java
In addition, you then need to import airfoil.airfoil in your airfoilNumber class:
import airfoil.airfoil;
public class airfoilNumber {
}
With this setup, you can compile the two classes from within the project's top level directory with
C:> javac *.java
As a side comment, class names should start with uppercase letters. I kept the names from the question since casing mismatches between the class definition and its containing .java file can lead to other issues which are difficult to track, at least on MS Windows.
Once that is done, you need to fix the constructors of your airfoil class. There is currently only one constructor which takes a String argument, but you call it as follows:
private airfoil myAirfoil = new airfoil();
// does not compile - no non-arg constructor available
...
new airfoil(numNaca);
// does not compile - numNaca is int, constructor expects String

Mahout: adjusted cosine similarity for item based recommender

For an assignment I'm supposed to test different types of recommenders, which I have to implement first. I've been looking around for a good library to do that (I had thought about Weka at first) and stumbled upon Mahout.
I must therefore put forward that: a) I'm completely new to Mahout b) I do not have a strong background in recommenders nor their algorithms (otherwise I wouldn't be doing this class...) and c) sorry but I'm far from being the best developper in the world ==> I'd appreciate if you could use layman terms (as far as possible...) :)
I've been following some tutorials (e.g. this, as well as part2) and got some preliminary results on item-based and user-based recommenders.
However, I'm not very happy with the item-based prediction. So far, I've only found similarity functions that do not take into consideration the users' rating-biases. I was wondering if there is something like adjusted cosine similarity. Any hints?
Here is a sample of the AdjustedCosineSimilarity I created. You must remember that this will be slower than PearsonCorrelationSimilarity because of the sqrt computations, but will produce better results. At least for my dataset results were much better. But you should make a trade off, quality/performance, and depending of your needs you should use the implementation you want.
/**
* Custom implementation of {#link AdjustedCosineSimilarity}
*
* #author dmilchevski
*
*/
public class AdjustedCosineSimilarity extends AbstractSimilarity {
/**
* Creates new {#link AdjustedCosineSimilarity}
*
* #param dataModel
* #throws TasteException
*/
public AdjustedCosineSimilarity(DataModel dataModel)
throws TasteException {
this(dataModel, Weighting.UNWEIGHTED);
}
/**
* Creates new {#link AdjustedCosineSimilarity}
*
* #param dataModel
* #param weighting
* #throws TasteException
*/
public AdjustedCosineSimilarity(DataModel dataModel, Weighting weighting)
throws TasteException {
super(dataModel, weighting, true);
Preconditions.checkArgument(dataModel.hasPreferenceValues(),
"DataModel doesn't have preference values");
}
/**
* Compute the result
*/
#Override
double computeResult(int n, double sumXY, double sumX2, double sumY2, double sumXYdiff2) {
if (n == 0) {
return Double.NaN;
}
// Note that sum of X and sum of Y don't appear here since they are
// assumed to be 0;
// the data is assumed to be centered.
double denominator = Math.sqrt(sumX2) * Math.sqrt(sumY2);
if (denominator == 0.0) {
// One or both parties has -all- the same ratings;
// can't really say much similarity under this measure
return Double.NaN;
}
return sumXY / denominator;
}
/**
* Gets the average preference
* #param prefs
* #return
*/
private double averagePreference(PreferenceArray prefs){
double sum = 0.0;
int n = prefs.length();
for(int i=0; i<n; i++){
sum+=prefs.getValue(i);
}
if(n>0){
return sum/n;
}
return 0.0d;
}
/**
* Compute the item similarity between two items
*/
#Override
public double itemSimilarity(long itemID1, long itemID2) throws TasteException {
DataModel dataModel = getDataModel();
PreferenceArray xPrefs = dataModel.getPreferencesForItem(itemID1);
PreferenceArray yPrefs = dataModel.getPreferencesForItem(itemID2);
int xLength = xPrefs.length();
int yLength = yPrefs.length();
if (xLength == 0 || yLength == 0) {
return Double.NaN;
}
long xIndex = xPrefs.getUserID(0);
long yIndex = yPrefs.getUserID(0);
int xPrefIndex = 0;
int yPrefIndex = 0;
double sumX = 0.0;
double sumX2 = 0.0;
double sumY = 0.0;
double sumY2 = 0.0;
double sumXY = 0.0;
double sumXYdiff2 = 0.0;
int count = 0;
// No, pref inferrers and transforms don't appy here. I think.
while (true) {
int compare = xIndex < yIndex ? -1 : xIndex > yIndex ? 1 : 0;
if (compare == 0) {
// Both users expressed a preference for the item
double x = xPrefs.getValue(xPrefIndex);
double y = yPrefs.getValue(yPrefIndex);
long xUserId = xPrefs.getUserID(xPrefIndex);
long yUserId = yPrefs.getUserID(yPrefIndex);
double xMean = averagePreference(dataModel.getPreferencesFromUser(xUserId));
double yMean = averagePreference(dataModel.getPreferencesFromUser(yUserId));
sumXY += (x - xMean) * (y - yMean);
sumX += x;
sumX2 += (x - xMean) * (x - xMean);
sumY += y;
sumY2 += (y - yMean) * (y - yMean);
double diff = x - y;
sumXYdiff2 += diff * diff;
count++;
}
if (compare <= 0) {
if (++xPrefIndex == xLength) {
break;
}
xIndex = xPrefs.getUserID(xPrefIndex);
}
if (compare >= 0) {
if (++yPrefIndex == yLength) {
break;
}
yIndex = yPrefs.getUserID(yPrefIndex);
}
}
double result;
// See comments above on these computations
double n = (double) count;
double meanX = sumX / n;
double meanY = sumY / n;
// double centeredSumXY = sumXY - meanY * sumX - meanX * sumY + n *
// meanX * meanY;
double centeredSumXY = sumXY - meanY * sumX;
// double centeredSumX2 = sumX2 - 2.0 * meanX * sumX + n * meanX *
// meanX;
double centeredSumX2 = sumX2 - meanX * sumX;
// double centeredSumY2 = sumY2 - 2.0 * meanY * sumY + n * meanY *
// meanY;
double centeredSumY2 = sumY2 - meanY * sumY;
// result = computeResult(count, centeredSumXY, centeredSumX2,
// centeredSumY2, sumXYdiff2);
result = computeResult(count, sumXY, sumX2, sumY2, sumXYdiff2);
if (!Double.isNaN(result)) {
result = normalizeWeightResult(result, count,
dataModel.getNumUsers());
}
return result;
}
}

Trying to shoot a bullet in a 3D world. Experiencing an odd offset in x and z

Alright, so I am using Bukkit API (Minecraft), which should not be too much of a problem in this as it is used minimally. So a Location contains world, x, y, z, yaw and pitch. This may come in handy, but I doubt it.
My problem is that I go to shoot using the Shot class (below), and there appears to be a +-5 difference in either one when approximately 3 blocks away, and the HitBox is instantiated about 5 blocks away (this could be the issue (move/rotate methods)). I have tried working this out on paper, and have used half a notepad doing so, but I am not yet able to figure out a solution. What I need is someone who understands trigonometry and java well, so they can help out.
Other information that may be of use:
+z is 0 degrees yaw, -x is 90 degrees, -z is 180, and +x is 270.
The variables seem to be incorrect sporadically, and under certain circumstances they work correctly and constitute a hit.
The Location (from) parameter in the Shot constructor is of the player's location in the world, therefore from is not (0, 0, 0).
ShotData should not affect any values, as wind speed and wind direction in my case are 0 (if there is a problem with the math involving this though, feel free to let me know, haha)
Pitch appears to be fine, although +y is -90, and -y is 90 (weird right?)
So my question is... Where is the problem, and how do I fix it? I am sorry that it is such a general question and that it is a very common one, but this is one of those times when it is truly necessary. I tried to remove all unnecessary code, but you can remove more if needed. Also, if you want to see anything else that may be referenced in here, I can get that for you.
Shot.java:
private final Location from;
private ShotData data;
public Shot(Location from, ShotData data) {
this.from = from;
this.data = data;
}
// TODO - Checking for obstacles
public List<Hit> shoot(List<HitBox> hitBoxes) {
List<Hit> hits = new ArrayList<Hit>();
for (HitBox hitBox : hitBoxes) {
hitBox.update();
float fromYaw = from.getYaw() % 360;
float fromPitch = from.getPitch() % 360;
// making sure the center location is within range
if (hitBox.getCenter().distanceSquared(from) > Math.pow(data.getDistanceToTravel(), 2)) {
continue;
}
/* TODO Only allow hits on parts of the rectangle that are within range,
* not just the whole thing if the center is within range. */
// accounting for wind speed/direction
float windCompassDirection = data.getWindCompassDirection(from.getWorld());
float windSpeed = data.getWindSpeedMPH(from.getWorld());
fromYaw += (windCompassDirection > fromYaw ? 1 : windCompassDirection < fromYaw ? -1 : 0) * windSpeed;
fromYaw %= 360;
int[] orderClockwise = new int[] {0, 1, 4, 3};
Location thisSideCorner = hitBox.getCorner(0);
Location oppositeSideCorner = hitBox.getCorner(0);
for (int i = 0; i < orderClockwise.length; i++) {
int num = orderClockwise[i];
Location corner = hitBox.getCorner(num);
Location clockwise = hitBox.getCorner(orderClockwise[(i + 1) % 3]);
if ((Math.atan2(from.getZ() - corner.getZ(), from.getX() - corner.getX()) * 180 / Math.PI) > 0 && corner.distanceSquared(from) < clockwise.distanceSquared(from)) {
thisSideCorner = corner;
int exitCornerClockwiseAmount = (Math.atan2(from.getZ() - clockwise.getZ(), from.getX() - clockwise.getX()) * 180 / Math.PI) < 0 ? 2 : 3;
oppositeSideCorner = hitBox.getCorner((i + exitCornerClockwiseAmount) % 3);
}
}
Location entrance = getProjectileLocation(thisSideCorner, data, hitBox, fromYaw, fromPitch);
double distance = entrance.distance(from);
double deltaX = data.getDeltaX(distance, fromYaw);
double deltaY = data.getDeltaY(distance, fromPitch);
double deltaZ = data.getDeltaZ(distance, fromYaw);
entrance.add(deltaX, deltaY, deltaZ);
Location exit = getProjectileLocation(oppositeSideCorner, data, hitBox, deltaX, deltaY, deltaZ, fromYaw, fromPitch);
// hit detection and reaction
boolean hitX = entrance.getX() <= hitBox.getHighestX() && entrance.getX() >= hitBox.getLowestX();
boolean hitY = entrance.getY() <= hitBox.getHighestY() && entrance.getY() >= hitBox.getLowestY();
boolean hitZ = entrance.getZ() <= hitBox.getHighestZ() && entrance.getZ() >= hitBox.getLowestZ();
if (hitX && hitY && hitZ) {
hits.add(new Hit(from, entrance, exit, hitBox, data));
}
}
return hits;
}
private Location getProjectileLocation(Location thisSideCorner, ShotData data, HitBox hitBox, float fromYaw, float fromPitch) {
return getProjectileLocation(thisSideCorner, data, hitBox, 0, 0, 0, fromYaw, fromPitch);
}
private Location getProjectileLocation(Location thisSideCorner, ShotData data, HitBox hitBox, double addX, double addY, double addZ, float fromYaw, float fromPitch) {
double deltaFromToSideCornerX = thisSideCorner.getX() - from.getX();
double deltaFromToSideCornerY = thisSideCorner.getY() - from.getY();
double deltaFromToSideCornerZ = thisSideCorner.getZ() - from.getZ();
double xzDistFromSideCorner = Math.sqrt(Math.pow(deltaFromToSideCornerX, 2) + Math.pow(deltaFromToSideCornerZ, 2));
double yawToSideCorner = Math.atan2(deltaFromToSideCornerX, deltaFromToSideCornerZ) * 180 / Math.PI;// flipped x and z from normal
double theta1 = yawToSideCorner - fromYaw;
double theta2 = yawToSideCorner - theta1;
double outerAngle = 180 - yawToSideCorner - 90;// previously theta1
double outerAngleInShotCone = outerAngle + 90 + hitBox.getYawRotation();
double lastAngleInShotCone = 180 - theta1 - outerAngleInShotCone;
double xzDistanceFromHit = (xzDistFromSideCorner * Math.sin(Math.toRadians(outerAngleInShotCone))) / Math.sin(Math.toRadians(lastAngleInShotCone));
double deltaX = xzDistanceFromHit * Math.sin(Math.toRadians(theta2));// leaves out sin 90 because its just equal to 1...
double deltaZ = xzDistanceFromHit * Math.sin(Math.toRadians(90 - theta2));// leaves out sin 90 because its just equal to 1...
double xyzDistFromSideCorner = Math.sqrt(Math.pow(xzDistFromSideCorner, 2) + Math.pow(deltaFromToSideCornerY, 2));
double theta3 = Math.atan2(Math.abs(deltaFromToSideCornerY), xzDistFromSideCorner) * 180 / Math.PI;
double theta4 = Math.abs(fromPitch) - theta3;
double theta5 = 90 + theta3;
double theta6 = 180 - theta4 - theta5;
double hitDistance = (xyzDistFromSideCorner * Math.sin(Math.toRadians(theta5))) / Math.sin(Math.toRadians(theta6));
double deltaY = hitDistance * Math.sin(Math.toRadians(Math.abs(fromPitch)));// leaves out sin 90 because its just equal to 1...
if (deltaFromToSideCornerX < 0 && deltaX > 0) {
deltaX *= -1;
}
if (fromPitch > 0 && deltaY > 0) {// pitch in minecraft is backwards, normally it would be fromPitch < 0
deltaY *= -1;
}
if (deltaFromToSideCornerZ < 0 && deltaZ > 0) {
deltaZ *= -1;
}
Location hit = from.clone().add(deltaX + addX, deltaY + addY, deltaZ + addZ);
hit.setYaw(fromYaw);
hit.setPitch(fromPitch);
return hit;
}
HitBox.java:
private float yawRotation;
private double x, y, z;
private double[][] additions;
private Location center;
private Location[] corners = new Location[8];
private List<DataZone> dataZones = new ArrayList<DataZone>();
private UUID uuid = UUID.randomUUID();
//#formatter:off
/*
* O = origin
* X = x-axis
* Y = y-axis
* Z = z-axis
* C = center
*
* ---------------------
* / /|
* / / |
* Y-------------------- |
* | 90 | | 0 yaw
* | ^ | | /
* | | | |
* | | | | /
* | HEIGHT C | |
* | | | |/
* | | | Z
* | v | /
* | <---WIDTH---> |/<---LENGTH
* O-------------------X - - - - - - - - - -270 yaw
*/
/**
* An invisible box in the world that can be hit with a shot.
* Additionally, {#link DataZone} instances can be added to this,
* allowing for different damage and thickness on an area of the box.
*
* #param center The center of the hit box
* #param length The length (z axis) of the hit box
* #param width The width (x axis) of the hit box
* #param height The height (y axis) of the hit box
* #param yawRotation The rotation around the center of the origin (or any other point)
*/
public HitBox(Location center, double length, double width, double height, float yawRotation) {
corners[0] = center.clone().add(-1 * width / 2, -1 * height / 2, -1 * length / 2);
this.center = center;
this.x = width;
this.y = height;
this.z = length;
rotate(yawRotation);
}
//#formatter:on
public Location[] getCorners() {
return corners;
}
public Location getCorner(int corner) {
return corners[corner];
}
public Location getOrigin() {
return corners[0];
}
public void update() {};
public boolean isZoneOpen(DataZone zone) {
for (DataZone placed : dataZones) {
boolean Xs = overlap_1D(placed.xFrom, placed.xTo, zone.xFrom, zone.xTo);
boolean Ys = overlap_1D(placed.yFrom, placed.yTo, zone.yFrom, zone.yTo);
boolean Zs = overlap_1D(placed.zFrom, placed.zTo, zone.zFrom, zone.zTo);
if (Xs && Ys && Zs) {
return true;
}
}
return false;
}
public void rotate(float degrees) {
Location origin = corners[0];
this.yawRotation = (yawRotation + degrees) % 360;
additions = new double[][] { {0, 0, 0}, {x, 0, 0}, {0, y, 0}, {0, 0, z}, {x, 0, z}, {x, y, 0}, {x, y, z}, {0, y, z}};
for (int i = 0; i < 8; i++) {
double[] addition = additions[i];
double xPrime = center.getX() + (center.getX() - (origin.getX() + addition[0])) * Math.cos(Math.toRadians(yawRotation)) - (center.getZ() - (origin.getZ() + addition[2])) * Math.sin(Math.toRadians(yawRotation));
double zPrime = center.getZ() + (center.getX() - (origin.getX() + addition[0])) * Math.sin(Math.toRadians(yawRotation)) + (center.getZ() - (origin.getZ() + addition[2])) * Math.cos(Math.toRadians(yawRotation));
corners[i] = new Location(center.getWorld(), xPrime, origin.getY() + addition[1], zPrime, yawRotation, 0);
}
}
public void move(Location center) {
double deltaX = center.getX() - this.center.getX();
double deltaY = center.getY() - this.center.getY();
double deltaZ = center.getZ() - this.center.getZ();
for (int i = 0; i < 8; i++) {
corners[i].add(deltaX, deltaY, deltaZ);
}
this.center = center;
}
protected void setY(double y) {
int[] toChange = new int[] {2, 5, 6, 7};
for (int i : toChange) {
corners[i].setY(corners[0].getY() + y);
}
this.y = y;
}
public double getHighestX() {
double highestX = Double.MIN_VALUE;
for (Location location : corners) {
if (location.getX() > highestX) {
highestX = location.getX();
}
}
return highestX;
}
public double getHighestY() {
return corners[0].getY() + y;
}
public double getHighestZ() {
double highestZ = Double.MIN_VALUE;
for (Location location : corners) {
if (location.getZ() > highestZ) {
highestZ = location.getZ();
}
}
return highestZ;
}
public double getLowestX() {
double lowestX = Double.MAX_VALUE;
for (Location location : corners) {
if (location.getX() < lowestX) {
lowestX = location.getX();
}
}
return lowestX;
}
public double getLowestY() {
return corners[0].getY();
}
public double getLowestZ() {
double lowestZ = Double.MAX_VALUE;
for (Location location : corners) {
if (location.getZ() < lowestZ) {
lowestZ = location.getZ();
}
}
return lowestZ;
}
public float getYawRotation() {
return yawRotation;
}
Perhaps consider drawing a line following the same vector that your bullet travels along, this will provide a visual indicator for what is happening, pass in the same calculations and etc.
As other have mentioned also include lots of debug printouts. Hopefully once you have a visual cue you can see when/where the problem calculations are occuring.
Also you should aim to use a standard data type for calculations, a float or a double, NOT both as this can cause some weird rounding and calculation problems.
I know this is extremely late (almost 5 years in fact), but I developed a solution a few weeks after this question. After revisiting StackOverflow I decided to provide my solution for any who may find it useful.
The issue with the massive wall of code found in the question is that many values are being computed and every computation loses precision, resulting in some degree of variation (like I said, +/-5 blocks).
The solution source may be found here:
https://github.com/JamesNorris/MCShot
To compute intersect (found in util/Plane3D.java):
public Vector getIntersect(LineSegment3D segment) {
Vector u = segment.getEnd().subtract(segment.getStart());
Vector w = segment.getStart().subtract(point);
double D = normal.dot(u);
double N = -normal.dot(w);
if (Math.abs(D) < .00000001) {
/* if N == 0, segment lies in the plane */
return null;
}
double sI = N / D;
if (sI < 0 || sI > 1) {
return null;
}
return segment.getStart().add(u.multiply(sI));
}
As you can see, this requires way fewer computations and as a result provides much greater precision. This function gets the intersection of a 3D plane. A 3D plane is constructed using the values:
point - some point found within the plane normal - the normal
vector of the plane
I hope someone finds this solution helpful, or at least can use it as a lesson in computational precision!

Calculating the Moment Of Inertia for a concave 2D polygon relative to its orgin

I want to compute the moment of inertia of a (2D) concave polygon. I found this on the internet. But I'm not very sure how to interpret the formula...
Formula http://img101.imageshack.us/img101/8141/92175941c14cadeeb956d8f.gif
1) Is this formula correct?
2) If so, is my convertion to C++ correct?
float sum (0);
for (int i = 0; i < N; i++) // N = number of vertices
{
int j = (i + 1) % N;
sum += (p[j].y - p[i].y) * (p[j].x + p[i].x) * (pow(p[j].x, 2) + pow(p[i].x, 2)) - (p[j].x - p[i].x) * (p[j].y + p[i].y) * (pow(p[j].y, 2) + pow(p[i].y, 2));
}
float inertia = (1.f / 12.f * sum) * density;
Martijn
#include <math.h> //for abs
float dot (vec a, vec b) {
return (a.x*b.x + a.y*b.y);
}
float lengthcross (vec a, vec b) {
return (abs(a.x*b.y - a.y*b.x));
}
...
do stuff
...
float sum1=0;
float sum2=0;
for (int n=0;n<N;++n) { //equivalent of the Σ
sum1 += lengthcross(P[n+1],P[n])*
(dot(P[n+1],P[n+1]) + dot(P[n+1],P[n]) + dot(P[n],P[n]));
sum2 += lengthcross(P[n+1],P[n]);
}
return (m/6*sum1/sum2);
Edit: Lots of small math changes
I think you have more work to do that merely translating formulas into code. You need to understand exactly what this formula means.
When you have a 2D polygon, you have three moments of inertia you can calculate relative to a given coordinate system: moment about x, moment about y, and polar moment of inertia. There's a parallel axis theorem that allows you to translate from one coordinate system to another.
Do you know precisely which moment and coordinate system this formula applies to?
Here's some code that might help you, along with a JUnit test to prove that it works:
import java.awt.geom.Point2D;
/**
* PolygonInertiaCalculator
* User: Michael
* Date: Jul 25, 2010
* Time: 9:51:47 AM
*/
public class PolygonInertiaCalculator
{
private static final int MIN_POINTS = 2;
public static double dot(Point2D u, Point2D v)
{
return u.getX()*v.getX() + u.getY()*v.getY();
}
public static double cross(Point2D u, Point2D v)
{
return u.getX()*v.getY() - u.getY()*v.getX();
}
/**
* Calculate moment of inertia about x-axis
* #param poly of 2D points defining a closed polygon
* #return moment of inertia about x-axis
*/
public static double ix(Point2D [] poly)
{
double ix = 0.0;
if ((poly != null) && (poly.length > MIN_POINTS))
{
double sum = 0.0;
for (int n = 0; n < (poly.length-1); ++n)
{
double twiceArea = poly[n].getX()*poly[n+1].getY() - poly[n+1].getX()*poly[n].getY();
sum += (poly[n].getY()*poly[n].getY() + poly[n].getY()*poly[n+1].getY() + poly[n+1].getY()*poly[n+1].getY())*twiceArea;
}
ix = sum/12.0;
}
return ix;
}
/**
* Calculate moment of inertia about y-axis
* #param poly of 2D points defining a closed polygon
* #return moment of inertia about y-axis
* #link http://en.wikipedia.org/wiki/Second_moment_of_area
*/
public static double iy(Point2D [] poly)
{
double iy = 0.0;
if ((poly != null) && (poly.length > MIN_POINTS))
{
double sum = 0.0;
for (int n = 0; n < (poly.length-1); ++n)
{
double twiceArea = poly[n].getX()*poly[n+1].getY() - poly[n+1].getX()*poly[n].getY();
sum += (poly[n].getX()*poly[n].getX() + poly[n].getX()*poly[n+1].getX() + poly[n+1].getX()*poly[n+1].getX())*twiceArea;
}
iy = sum/12.0;
}
return iy;
}
/**
* Calculate polar moment of inertia xy
* #param poly of 2D points defining a closed polygon
* #return polar moment of inertia xy
* #link http://en.wikipedia.org/wiki/Second_moment_of_area
*/
public static double ixy(Point2D [] poly)
{
double ixy = 0.0;
if ((poly != null) && (poly.length > MIN_POINTS))
{
double sum = 0.0;
for (int n = 0; n < (poly.length-1); ++n)
{
double twiceArea = poly[n].getX()*poly[n+1].getY() - poly[n+1].getX()*poly[n].getY();
sum += (poly[n].getX()*poly[n+1].getY() + 2.0*poly[n].getX()*poly[n].getY() + 2.0*poly[n+1].getX()*poly[n+1].getY() + poly[n+1].getX()*poly[n].getY())*twiceArea;
}
ixy = sum/24.0;
}
return ixy;
}
/**
* Calculate the moment of inertia of a 2D concave polygon
* #param poly array of 2D points defining the perimeter of the polygon
* #return moment of inertia
* #link http://www.physicsforums.com/showthread.php?t=43071
* #link http://www.physicsforums.com/showthread.php?t=25293
* #link http://stackoverflow.com/questions/3329383/help-me-with-converting-latex-formula-to-code
*/
public static double inertia(Point2D[] poly)
{
double inertia = 0.0;
if ((poly != null) && (poly.length > MIN_POINTS))
{
double numer = 0.0;
double denom = 0.0;
double scale;
double mag;
for (int n = 0; n < (poly.length-1); ++n)
{
scale = dot(poly[n + 1], poly[n + 1]) + dot(poly[n + 1], poly[n]) + dot(poly[n], poly[n]);
mag = Math.sqrt(cross(poly[n], poly[n+1]));
numer += mag * scale;
denom += mag;
}
inertia = numer / denom / 6.0;
}
return inertia;
}
}
Here's the JUnit test to accompany it:
import org.junit.Test;
import java.awt.geom.Point2D;
import static org.junit.Assert.assertEquals;
/**
* PolygonInertiaCalculatorTest
* User: Michael
* Date: Jul 25, 2010
* Time: 10:16:04 AM
*/
public class PolygonInertiaCalculatorTest
{
#Test
public void testTriangle()
{
Point2D[] poly =
{
new Point2D.Double(0.0, 0.0),
new Point2D.Double(1.0, 0.0),
new Point2D.Double(0.0, 1.0)
};
// Moment of inertia about the y1 axis
// http://www.efunda.com/math/areas/triangle.cfm
double expected = 1.0/3.0;
double actual = PolygonInertiaCalculator.inertia(poly);
assertEquals(expected, actual, 1.0e-6);
}
#Test
public void testSquare()
{
Point2D[] poly =
{
new Point2D.Double(0.0, 0.0),
new Point2D.Double(1.0, 0.0),
new Point2D.Double(1.0, 1.0),
new Point2D.Double(0.0, 1.0)
};
// Polar moment of inertia about z axis
// http://www.efunda.com/math/areas/Rectangle.cfm
double expected = 2.0/3.0;
double actual = PolygonInertiaCalculator.inertia(poly);
assertEquals(expected, actual, 1.0e-6);
}
#Test
public void testRectangle()
{
// This gives the moment of inertia about the y axis for a coordinate system
// through the centroid of the rectangle
Point2D[] poly =
{
new Point2D.Double(0.0, 0.0),
new Point2D.Double(4.0, 0.0),
new Point2D.Double(4.0, 1.0),
new Point2D.Double(0.0, 1.0)
};
double expected = 5.0 + 2.0/3.0;
double actual = PolygonInertiaCalculator.inertia(poly);
assertEquals(expected, actual, 1.0e-6);
double ix = PolygonInertiaCalculator.ix(poly);
double iy = PolygonInertiaCalculator.iy(poly);
double ixy = PolygonInertiaCalculator.ixy(poly);
assertEquals(ix, (1.0 + 1.0/3.0), 1.0e-6);
assertEquals(iy, (21.0 + 1.0/3.0), 1.0e-6);
assertEquals(ixy, 4.0, 1.0e-6);
}
}
For reference, here's a mutable 2D org.gcs.kinetic.Vector implementation and a more versatile, immutable org.jscience.mathematics.vector implementation. This article on Calculating a 2D Vector’s Cross Product is helpful, too.
I did it with Tesselation. And take the MOI's all together.

Categories