regarding the following code, can I make it better for distance() method?
It feels like it's not completely OOP with this method.. how can I change code to be better OOD for this one ?
Thanks !!
public class Line extends Shape {
private Point lineP1;
private Point lineP2;
public Line(int x1, int x2, int y1, int y2, Color myColor) {
super(x1, x2, y1, y2, myColor);
lineP1 = new Point(this.getX1(),this.getY1());
lineP2 = new Point(this.getX2(),this.getY2());
}
#Override
public void draw(Graphics g) {
g.drawLine(this.getX1(), this.getY1(), this.getX2(), this.getY2());
g.setColor(Color.GREEN);
}
#Override
public boolean contains(Point p) {
if((this.distance(lineP1, p)+this.distance(lineP2, p)) == this.distance(lineP1, lineP2))
return true;
return false;
}
/**#return distance between two given points
* This method return the distance between two given points*/
private double distance(Point p1,Point p2 ){
return Math.sqrt(Math.pow((p1.getX()-p2.getX()), 2) + Math.pow((p1.getY()-p2.getY()), 2));
}
}//class Line
Your distance method seems to be ok (but it would be more performant, if you saved the differences in variables and used the * operator to multiply those values with themselfs instear of using Math.pow).
However, since floating point calculations tend to return inexact results, I don't recomend using the sum of the distances between the end node and the point to test as criterium.
But there's another good way determining, if a point is near a line or not: using the hesse normal form. It works like this:
Let P1 and P2 be vectors corresponing to the end points. * denotes the scalar multiplication and || the length of a vector:
D = (P2 - P1) / |P2 - P1|;
Let N be the vector D with coordinates swaped and the new x coordinate multiplied with -1 (i.e. a vector ortogonal to D).
Then the distance of a point H to the line can be determined like this
| N * H - N * P1 |
Also if H is between P1 and P2 can be checked like this (assuming without loss of generality D * P1 < D * P2):
D * P1 <= D * H <= D * P2
Using scalar products has the additional benefit, that calculating a scalar product only takes 2 multiplication and 1 addition in a 2D space.
This is how you could do this in java code
// components of normal vector
private double normalX;
private double normalY;
// components of direction vector
private double directionX;
private double directionY;
// the value of (N * P) for all points P on the line
private double normalScalarProduct;
// the range allowed for (D * P) for points on the line
private double minDirectionScalarProduct;
private double maxDirectionScalarProduct;
// error ranges; adjust as appropriate
private static final double directionAllowedError = 0.1;
private static final double normalAllowedError = 0.1;
public Line(int x1, int x2, int y1, int y2, Color myColor) {
...
double dx = x2 - x1;
double dy = y2 - y1;
double length = distance(dx, dy);
if (length == 0) {
// choose arbitrary direction, if length == 0
length = 1;
dx = 1;
}
// normalize direction
dx /= length;
dy /= length;
// set D and N values
this.directionX = dx;
this.directionY = dy;
this.normalX = -dy;
this.normalY = dx;
double prod1 = scalarProduct(directionX, directionY, x1, y1);
double prod2 = scalarProduct(directionX, directionY, x2, y2);
if (prod1 < prod2) {
minDirectionScalarProduct = prod1 - directionAllowedError;
maxDirectionScalarProduct = prod2 + directionAllowedError;
} else {
minDirectionScalarProduct = prod2 - directionAllowedError;
maxDirectionScalarProduct = prod1 + directionAllowedError;
}
normalScalarProduct = scalarProduct(x1, y1, normalX, normalY);
}
private static double scalarProduct(double x1, double y1, double x2, double y2) {
return x1*x2 + y1*y2;
}
public boolean contains(Point p) {
if (Math.abs(scalarProduct(p.getX(), p.getX(), normalX, normalY) - normalScalarProduct) <= normalAllowedError) {
// close enough to the line -> check, if between end points
double d = scalarProduct(p.getX(), p.getX(), directionX, directionY);
return minDirectionScalarProduct <= d && d <= maxDirectionScalarProduct;
}
return false;
}
private double distance(double dx, double dy) {
return Math.sqrt(dx*dx + dy*dy);
}
Related
I am trying to make simulation of the three-body problem in Processing/Java. I have created a Planet object and defined several functions to calculate the attraction, the forces, and the speeds of the Planets. The program runs without any errors. The hitch is that the calculation are returning Infinity and
NaN outputs.
I haven't found any divide by zero errors.
Why is it doing this?
float simulationTime = 0.01;
float earthMass = pow(10,24)*5.972;
Planet[] bodies = {new Planet(100,200, 0, 0,earthMass/2),
new Planet(400,500, 0, 0,earthMass/2),
new Planet(800,200, 0, 0,earthMass/2)};
int size = 500;
float G = 6.674*pow(10, -11);
float gravAttract(float mass1, float mass2, float x1, float y1, float x2, float y2) {
float force = G*mass1*mass2/pow(dist1(x1, y1, x2, y2), 2);
//println(pow(dist1(x1, y1, x2, y2), 2));
//println(force);
return force;
}
float attract(Planet one,Planet two){
float force = G*one.mass*two.mass/pow(dist1(one.xpos,one.ypos,two.xpos,two.ypos),2);
println(one.xpos,two.xpos,one.xspeed,two.xspeed);
return force;
}
float[] forceXY(int body){
float[] xy = {0,0};
for (int ii = 0; ii<bodies.length; ii = ii+1){
if (ii == body){
continue;
}
else{
xy[0] = xy[0]+attract(bodies[body],bodies[ii])*
(bodies[ii].xpos-bodies[body].xpos)/dist1(bodies[body].xpos,bodies[body].ypos,bodies[ii].xpos,bodies[ii].ypos);
xy[1] = xy[1]+attract(bodies[body],bodies[ii])*
(bodies[ii].ypos-bodies[body].ypos)/dist1(bodies[body].xpos,bodies[body].ypos,bodies[ii].xpos,bodies[ii].ypos);
println(xy);
}
}
return xy;
}
float dist1(float x1,float y1,float x2,float y2){
float x = dist(x1,y1,x2,y2);
x = x*149.6*pow(10,7);
//println(x1,y1,x2,y2);
return x;
}
class Planet {
float xpos;
float ypos;
float xspeed;
float yspeed;
float mass;
Planet(float a, float b, float c, float d, float e) {
xpos = a;
ypos = b;
xspeed = c;
yspeed = d;
mass = e;
}
void update(float xforce, float yforce) {
//println("xy",xpos,ypos);
float xa = xforce/mass;
float ya = yforce/mass;
xspeed = xspeed + xa*simulationTime;
yspeed = yspeed + ya*simulationTime;
xpos = xpos + xspeed*simulationTime;
ypos = ypos + yspeed*simulationTime;
}
}
void setup() {
size(1000, 1000);
frameRate(1);
}
void draw() {
background(0);
for (int ii = 0; ii < bodies.length; ii = ii+1){
float[] asdf = new float[2];
asdf = forceXY(ii);
circle(bodies[ii].xpos,bodies[ii].ypos,10);
bodies[ii].update(asdf[0],asdf[1]);
}
}
I see you're trying to do the calculations using the real physical values. The problem with that is that the astronomical masses, distances and forces are astronomical, not just literally but also figuratively. The numbers are so big that the results of the intermediate calculations cannot be represented using the limited range of the float data type.
The solution is to change the units. For example, instead of using meters, kilograms, and seconds, measure the distance in "astronomical units" (distance between Earth and Sun), mass in multiples of Earth masses, and time in years. Or use the standard "astronomical system of units". Most importantly, this changes the value of the gravitational constant G.
If you're not too attached to making a simulation that lets you predict the real positions of the planets, it's much easier to just "wing it" and set G=1, set the masses to small numbers, and play around with the velocities. You'll see it's very easy to come up with a realistic simulation.
I am trying to understand why I am having a difficult time calling two different points into a method in order to determine the distance between the two points.
This is the text provided on the website "Practice-It", where I am currently working through problems:
Add the following method to the Point class:
public double distance(Point other)
Returns the distance between the current Point object and the given other Point object. The distance between two points is equal to the square root of the sum of the squares of the differences of their x- and y-coordinates. In other words, the distance between two points (x1, y1) and (x2, y2) can be expressed as the square root of (x2 - x1)2 + (y2 - y1)2. Two points with the same (x, y) coordinates should return a distance of 0.0."
I have tried multiple ways to get the points into the method without naming individual coordinates. I believe that I am only getting the coordinates of the first point and am trying to understand where I am going wrong as I've had this problem before. I realize that the "int x1 = p1.x", etc. is redundant and I could just type "p1.x" in the return, but I am wanting to see all of the moving parts individually. Thanks for your consideration and help.
public double distance(Point other){
Point p1 = new Point();
int x1 = p1.x;
int y1 = p1.y;
Point p2 = new Point();
int x2 = p2.x;
int y2 = p2.y;
return Math.sqrt(Math.pow(x * x2, 2) + Math.pow(y * y2, 2));
}
This is my result from one of the situations tested:
test #1:(5, 2) to (8, 6)
expected output:
5.0
5.0
your output:
0.0
0.0
differences:
1,2c1,2
< 5.0
< 5.0
> 0.0
> 0.0
result: fail
Your comparison logic is off. Assuming that distance() is a method inside the Point class, then you should be comparing the x,y values of the incoming other point against the x,y value in the class:
public double distance(Point other) {
int x1 = other.x;
int y1 = other.y;
return Math.sqrt(Math.pow(this.x * x1, 2) + Math.pow(this.y * y1, 2));
}
Note that is would be more ideal to provide getters for the x and y fields in Point. Here is a more complete suggested refactor:
public class Point {
private int x;
private int y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
public int getX() { return x; }
public int getY() { return y; }
public double distance(Point other) {
int x1 = other.getX();
int y1 = other.getY();
return Math.sqrt(Math.pow(this.x * x1, 2) + Math.pow(this.y * y1, 2));
}
}
I want to get the Point where a Polygon and a Line collides. I know there is a class called Intersector, but in this there is only a method for checking wether they collide or not, but I need the point where they are colliding.
I am happy with any help
public static List<RayTrace> rayTrace(Line2D line, boolean quick, Collisions... collisions) {
List<RayTrace> l = new ArrayList<RayTrace>();
for (Collisions collisions1 : collisions) {
for (Collision3D collision3D : collisions1) {
RayTrace rayTrace = new RayTrace();
if (quick) {
if (Intersector.intersectLinePolygon(line.getStartV(), line.getEndV(), collision3D.getBoundingPolygon())) {
rayTrace.collisionHit = collision3D;
rayTrace.hasHit = true;
l.add(rayTrace);
}
} else {
Point2f hit = new Point2f();
if (CollisionHelper.getLinePolygonIntersection(collision3D.getBoundingPolygon(), line, hit)) {
rayTrace.collisionHit = collision3D;
rayTrace.hasHit = true;
rayTrace.hitX = hit.x;
rayTrace.hitZ = hit.y;
l.add(rayTrace);
}
}
}
}
return l;
}
public static List<Vector2> getLinePolygonIntersections(Polygon polygon, Line2D line) {
float f[] = polygon.getTransformedVertices();
//Go through every side
List<Vector2> intersections = new ArrayList<Vector2>();
for (int i = 0; i < f.length - 2; i += 2) {
Vector2 intersection = new Vector2();
Intersector.intersectLines(line.x, line.y, line.x2, line.y2, f[i], f[i + 1], f[i + 2], f[i + 3], intersection);
intersections.add(intersection);
}
return intersections;
}
public static boolean getLinePolygonIntersection(#NotNull Polygon polygon, #NotNull Line2D line, #NotNull Point2f point) {
List<Vector2> list = getLinePolygonIntersections(polygon, line);
if (list.size() == 0) return false;
double shortestDistance = line.getStart().distance(new Point2f(list.get(0).x, list.get(0).y));
int indexClosest = 0;
for (int i = 1; i < list.size(); i++) {
double d = new Point2f(list.get(i).x, list.get(i).y).distance(line.getStart());
if (shortestDistance > d) {
indexClosest = i;
shortestDistance = d;
}
}
point.set(list.get(indexClosest).x, list.get(indexClosest).y);
return true;
}
Here is the method from the LibGDX Intersector class that could be modified:
public static boolean intersectLinePolygon (Vector2 p1, Vector2 p2, Polygon polygon) {
float[] vertices = polygon.getTransformedVertices();
float x1 = p1.x, y1 = p1.y, x2 = p2.x, y2 = p2.y;
int n = vertices.length;
float x3 = vertices[n - 2], y3 = vertices[n - 1];
for (int i = 0; i < n; i += 2) {
float x4 = vertices[i], y4 = vertices[i + 1];
float d = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1);
if (d != 0) {
float yd = y1 - y3;
float xd = x1 - x3;
float ua = ((x4 - x3) * yd - (y4 - y3) * xd) / d;
if (ua >= 0 && ua <= 1) {
return true;
}
}
x3 = x4;
y3 = y4;
}
return false;
}
What this method is actually doing is finding the intersection between the line segment from p1 to p2 with the edge of the polygon. (In particular, it is determining if there is any intersection between the given line segments, which will be important later.) In particular, the computations are being performed on the parametric equation of these two line segments; for example, the line segment from (x1,y1) to (x2,y2) has parametric equation
L(t) = [ x2-x1, y2-y1 ] * t + [ x1, y1 ]
where t ranges from 0 to 1.
The intersection of the the lines is calculated using Cramer's rule; the variable d above represents the determinant of the matrix appearing in the denominator of that formula. When d is nonzero, there is guaranteed to be an intersection between the lines, but we aren't done yet, because we are interested in the intersection of the line segments. The variable ua in the method yields the value of t in the parametric equation above when the intersection occurs; it must be between 0 and 1 for the intersection point to lie between the endpoints of the line segment.
Thus, the coordinates of the point of intersection can be calculated by evaluating L(t) when t = ua. To find the point of intersection, therefore, you could create your own version of this function that returns the value
Vector2( (x2-x1)*ua + x1, (y2-y1)*ua + y1)
Hope this helps!
IMHO: In the Intersector class many methods calculate collisions and generally the last parameter is a Vector3 filled with the coordinates of the collision.
I've never used this library, but at first glance, it's the way it works.
So I've been trying to implement Perlin noise recently, and have run into some unusual problems. Whenever the edges of the grid in which the random vectors are stored are crossed, the derivative appears to be discontinuous.
Here's a link to a picture of the output (on the right), along with a 1 dimensional slice (on the left).
The Output
class perlin{
private double[][][] grid;
public perlin(int x,int y, int seed){
Random r = new Random(seed);
grid = new double[x+1][y+1][2];
for(int i=0;i<grid.length;i++){
for(int j=0;j<grid[0].length;j++){
grid[i][j][0] = 2*r.nextDouble()-1;
grid[i][j][1] = 2*r.nextDouble()-1;
}
}
}
public static double lerp(double a, double b, double t){
double c = t * t * t * (t * (t * 6 - 15) + 10);
return (b * c) + (a * (1 - c));
}
public double get(double x, double y){
double x2;
double y2;
double x3;
double y3;
x2 = x * (grid.length-1);
y2 = y * (grid[0].length-1);
x3 = down(x2);
y3 = down(y2);
x2 = x2 - x3;
y2 = y2 - y3;
int i = (int) (x3);
int j = (int) (y3);
return lerp(lerp(dot(x2, y2, grid[i][j][0], grid[i][j][1] ), dot(1 - x2, y2, grid[i + 1][j][0], grid[i + 1][j][1]),x2), lerp(dot(x2, 1 - y2, grid[i][j + 1][0], grid[i][j +1][1] ), dot(1 - x2,1 - y2, grid[i + 1][j + 1][0], grid[i + 1][j + 1][1] ), x2),y2 );
// return 0;
}
public static double dot(double x1, double y1, double x2, double y2){
return x1 * x2 + y1 * y2;
}
private static double down(double a){
if (a == 0){
return 0;
}
if(a == Math.floor(a)){
return a - 1;
}else{
return Math.floor(a);
}
}
}
From what I understand about the math behind this, the derivative of the noise should be continuous at all points, but that does not appear to be the case.
There may have been questions asked about finding if a point is on a line but not in this context and they don't go into answering why various methods have different flaws. What is the best method for accurately finding if a point is on a line given the the coordinate of the point and the coordinates of the line? I've attempted to implement certain methods on my own but they all seem to have their own problems after tests. I'm sure there are other methods out there and I know Java has a method that calculates the distance of a point from a line and returns 0 if its on the line. What method does Java use to find out if a point is on a line?
Method One
The first method compares the distance from point A to point B with the distance from point A to C plus point C to B. The problem with this method is that it isn't precise since it uses Math.sqrt.
public static boolean inLine(double x1, double y1, double x2, double y2, double x3, double y3){
if((distance(x1, y1, x3, y3) + distance(x2, y2, x3, y3)) == distance(x1, y1, x2, y2)){
return true;
}
return false;
}
public static double distance(double x1, double y1, double x2, double y2){
double base = x2 - x1;
double height = y2 - y1;
double hypotenuse = Math.sqrt((base * base) + (height * height));
return hypotenuse;
}
Method Two
This is the second method I came up with. It checks if the point is on the line by comparing the the y value of the point to the y value on the line with the same x value as the point. The problem with this method is that it won't work when the line is vertical or horizontal so I implemented some tests to check if its on the line if the line is horizontal or vertical.
public static boolean inLine(double x1, double y1, double x2, double y2, double x3, double y3){
//Check if X and Y Values of the point are within the range of the line
if(inBetween(x1, x2, x3) & inBetween(y1, y2, y3) ){
//Check if denominator is going to equal 0 when finding the slope and x of point has the same value
if(x1 == x2 && x2 == x3){
if(inBetween(y1, y2, y3)){
return true;
}else{
return false;
}
}else if(y1 == y2 && y2 == y3){
if(inBetween(x1, x2, x3)){
return true;
}else{
return false;
}
}else{
double slope = (y2-y1)/(x2-x1);
//Check if the y value of the line is equal to the y value of the point
if(findYIntercept(slope, x1, y1)+slope*x3 == y3){
return true;
}
}
}else{
return false;
}
return false;
}
public static double findYIntercept(double slope, double x, double y){
return y-(slope*x);
}
public static boolean inBetween(double a, double b, double c){
if(a <= c && c <= b){
return true;
}
else if(a >= c && c >= b){
return true;
}
return false;
}
Method Three
This method is similar to the second method but checks if the slopes are identical. It also doesn't work if the line is horizontal or vertical. I also have implemented a situation if the line is horizontal or vertical.
public static boolean inLine(double x1, double y1, double x2, double y2, double x3, double y3){
//Check if X and Y Values of the point are within the range of the line
if(inBetween(x1, x2, x3) & inBetween(y1, y2, y3) ){
//Check if denominator is going to equal 0 when finding the slope and x of point has the same value
if(x1 == x2 && x2 == x3){
if(inBetween(y1, y2, y3)){
return true;
}else{
return false;
}
}else if(y1 == y2 && y2 == y3){
if(inBetween(x1, x2, x3)){
return true;
}else{
return false;
}
}else{
double slope1 = (y2-y1)/(x2-x1);
double slope2 = (y3-y1)/(x3-x1);
//Check if the y value of the line is equal to the y value of the point
if(slope1 == slope2){
return true;
}
}
}else{
return false;
}
return false;
}
public static double findYIntercept(double slope, double x, double y){
return y-(slope*x);
}
public static boolean inBetween(double a, double b, double c){
if(a <= c && c <= b){
return true;
}
else if(a >= c && c >= b){
return true;
}
return false;
}
The best method is probably the first. Any method using slopes is problematic because vertical lines have infinite slope.
The problem with your code for method one is not Math.sqrt, it's that floating point calculations are not exact. As a result it is almost always wrong to compare double values a and b using ==. Instead you should use something like Math.abs(a - b) < 0x1p-32 to see if they are close enough.
Because of the limitations of floating point calculations, it is extremely difficult to do this kind of thing properly. java.awt doesn't do it very well. For example, the following program prints 0.5, which is incredibly inaccurate.
double a = 18981256.0;
System.out.println(new Line2D.Double(1.0, 2.0, 2.0, 4.0).ptLineDist(a, 2 * a));
If this question refers to the java.awt.geom package then the answer is that there is no built in method. There is a Line2D.contains method but that always returns false because it refers to an area containing a point and a line has no area.
Personally I find your third method easiest to use. However you don't need to actually find the slope - you can compare the cross-multiplication to see if they have the same gradient.
That is, dy1 / dx1 = dy2 / dx2 => dy1 * dx2 = dx1 * dy2 (only if dx1 & dx2 != 0)
that takes care of the horizontal case (dy = 0).
I think using vectors is preferable.
class Vector {
private double x;
private double y;
Vector( double x, double y ){
this.x = x;
this.y = y;
}
Vector( Point p1, Point p2 ){
this( p1.getX() - p2.getX(), p1.getY() - p2.getY() );
}
public double getX(){ return x; }
public double getY(){ return y; }
public boolean isColl( Vector v ){
return y*v.getX() == x*v.getY();
}
}
class Point extends Vector {
Point( double x, double y ){
super( x, y );
}
}
And here's a test:
public static void main(String[] args) throws Exception {
{
Point g1 = new Point( 3, 0 );
Point g2 = new Point( 6, 0 );
for( Point p: new Point[]{ new Point ( 9, 0 ),
new Point ( 0, 9 ),
new Point ( 3, 3 ) } ){
Vector g = new Vector( g1, g2 );
Vector v1 = new Vector( g1, p );
Vector v2 = new Vector( g2, p );
System.out.println( v1.isColl( v2 ) );
}
}
{
Point g1 = new Point( 0, 3 );
Point g2 = new Point( 0, 6 );
for( Point p: new Point[]{ new Point ( 9, 0 ),
new Point ( 0, 9 ),
new Point ( 3, 3 ) } ){
Vector g = new Vector( g1, g2 );
Vector v1 = new Vector( g1, p );
Vector v2 = new Vector( g2, p );
System.out.println( v1.isColl( v2 ) );
}
}
{
Point g1 = new Point( 2, 3 );
Point g2 = new Point( 10, 15 );
for( Point p: new Point[]{ new Point ( 9, 0 ),
new Point ( 0, 9 ),
new Point ( 20, 30 ) } ){
Vector g = new Vector( g1, g2 );
Vector v1 = new Vector( g1, p );
Vector v2 = new Vector( g2, p );
System.out.println( v1.isColl( v2 ) );
}
}
}