Reputation: 193
I'm learning Java right now and one of my assignments has got me pretty stumped. I'm being asked to create a class which designates a coordinate in the first quadrant of a Cartesian plane and do so using only two values: the distance from said point to the origin (0,0) and the angle that vector draws to the X axis. The program looks like this so far:
public class Point2 {
private double _radius;
private double _alpha;
public Point2 (int x, int y) {
this._radius = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
this._alpha = Math.toDegrees(Math.atan2(y, x));
}
public Point2 (Point2 other) {
this._radius = other._radius;
this._alpha = other._alpha;
}
public int getX() {
return (int)(this._radius * Math.cos(Math.toRadians(this._alpha)));
}
public int getY() {
return (int)(this._radius * Math.sin(Math.toRadians(this._alpha)));
}
public void setX(int x) {
if(x > 0) {
this._radius = Math.sqrt(Math.pow(x, 2) + Math.pow(this.getY(), 2));
this._alpha = Math.toDegrees(Math.atan2(this.getY(), x));
}
}
public void setY(int y) {
if(y > 0) {
this._radius = Math.sqrt(Math.pow(this.getX(), 2) + Math.pow(y, 2));
this._alpha = Math.toDegrees(Math.atan2(y, this.getX()));
}
}
public boolean equals(Point2 other) {
if(this._radius == other._radius && this._alpha == this._radius) {
return true;
} else {
return false;
}
}
public boolean isAbove(Point2 other) {
if(this.getY() > other.getY()) {
return true;
} else {
return false;
}
}
public boolean isUnder(Point2 other) {
if(this.getY() < other.getY()) {
return true;
} else {
return false;
}
}
public boolean isLeft(Point2 other) {
if(this.getX() < other.getX()) {
return true;
} else {
return false;
}
}
public boolean isRight(Point2 other) {
if(this.getX() > other.getX()) {
return true;
} else {
return false;
}
}
double distance(Point2 other) {
double dist = Math.sqrt(Math.pow(this.getX() - other.getX(), 2) + Math.pow(this.getY() - other.getY(), 2));
return dist;
}
public void move(int dX, int dY) {
if(this.getX() + dX > 0 && this.getY() + dY > 0) {
this._radius = Math.sqrt(Math.pow(this.getX() + dX, 2) + Math.pow(this.getY() + dY, 2));
this._alpha = Math.toDegrees(Math.atan2(this.getY() + dY, this.getX() + dX));
}
}
public String toString() {
return "(" + _radius + "," + _alpha + ")";
}
}
Anyway, what happens when I create an instance of this class given x and y coordinates there's really no problem -- the calculations come out correctly and everything is just fine. The trouble is when I try to use any of the methods that change the X or Y coordinates.
For example, I'll run the setX(10)
and when I run getX()
, I'll get 9, but when I run setX(10)
again and then rerun get(X)
, I'll get 10. The same goes for move()
- calling the method once returns an inaccurate result for getX()
and getY()
but calling the method with the same parameters once or twice more will correct the result.
It's weird - almost like the calculations here are incorrect or there's a rounding problem. Anyone have a clue?
Upvotes: 0
Views: 167
Reputation: 917
You are definitely running into rounding errors. The first way to solve this would be to store x and y directly. Generally storing calculated data is a bad idea. However, I'm assuming this isn't possible.
You're running into two problems.
in this code:
this._radius = Math.sqrt(Math.pow(x, 2) + Math.pow(this.getY(), 2));
this._alpha = Math.toDegrees(Math.atan2(this.getY(), x));
Note that this.getY()
depends on both radius and alpha. When you change the radius in the first call, the value you get from getY will be different in the second call! You want to cache the value returned from getY
:
int y = this.getY();
this._radius = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
this._alpha = Math.toDegrees(Math.atan2(y, x));
The same problem is found in setY, with getX changing mid-function.
The other problem you're getting is due to casting a double to an integer in getX/getY. Java does this by truncation: (int) 4.99999999
will become 4
. It's likely that you will get better results by using (int) Math.round(...)
(Math.round() returns a long if you pass a double).
getY
isn't safe to call when you've only modified the radius based on the new x value.Upvotes: 1