Ray-sphere intersection method not working - java

public double intersect(Ray r)
{
double t;
Vector L = r.origin.sub(pos);
double a = r.direction.dot(r.direction);
double b = 2*(r.direction.dot(L));
double c = (L.dot(L)) - (radius*radius);
double disc = b*b - 4*a*c;
if (disc < 0)
return -1;
double distSqrt = Math.sqrt(disc);
double q;
if (b < 0)
q = (-b - distSqrt)/2;
else
q = (-b + distSqrt)/2;
double t0 = q/a;
double t1 = c/q;
if (t0 > t1)
{
double temp = t0;
t0 = t1;
t1 = temp;
}
if (t1 < 0)
return -1;
if (t0 < 0)
t = t1;
else
t = t0;
return t;
}
It should return -1 when there is no intersection.
There is a sphere at (5,0,0) with a radius of 2.
When I pass a ray with origin (0,0,0) and direction (5,0,0).unit, it returns 3 as it should.
When I pass a ray with origin (0,0,0) and direction (5,2,0).unit, it returns 3.9 as it should.
When I pass a ray with origin (0,0,0) and direction (5,0,1).unit, it returns -1, even though the ray intersects.
When the direction is (5,0,-1).unit, it returns 2.73, even though it is not possible for t to be less than 3 and it should return the same thing as (5,0,1).unit returns.

I think the condition used to calculate q is not right:
if (b < 0)
q = (-b - distSqrt)/2;
else
q = (-b + distSqrt)/2;
Here, you're deciding upon the sign of b. You should calculate both the values. It's clear that the first one, (-b - distSqrt)/2 would always give the smaller value of q, because distSqrt is always non-negative. Only if (-b - distSqrt)/2 is negative, you should check (-b + distSqrt)/2 which might be positive in some cases. This case will appear when the origin of the ray is located inside the sphere.
And is the t1 = c/q thing necessary? When you have the smaller positive value of q, you are done (because a must be positive, and 1 if the direction is an unit vector).
In my implementation, I did the calculation this way:
double Sphere::getIntersection(Ray ray)
{
Vector v = ray.origin - center;
double b = 2 * dot(ray.dir, v);
double c = dot(v, v) - radius * radius;
double d = b * b - 4 * c;
if(d > 0)
{
double x1 = (-b - sqrt(d)) / 2;
double x2 = (-b + sqrt(d)) / 2;
if(x1 >= 0 && x2 >= 0) return x1;
if(x1 < 0 && x2 >= 0) return x2;
}
return -1;
}
and it worked well.

When I run that code with your "ray with origin (0,0,0) and direction (5,0,1).unit" example, it returns 3.15979 for me. From what I can tell all the code you posted is correct. I imagine it's one of your other implementations that's causing the failure. It could be your unit vector calculation, your Vector.sub() method, Vector.dot() method, etc.
Try adding print statements throughout to see where it goes wrong. That's how I usually debug something like this.
Also I did a quick translation of your code into C++ (because I don't know java) that you can compare with if you like. It seems to function fine, which means that it's likely not your intersection function that is the problem.
My code is here: http://codepad.org/xldbJRKo
I hope this helps a little bit!

Related

Is there a way to "transfer" data from one function to an other one without static variable JAVA?

The following code got 2 functions . (credit to #AlexRudenko who helped me)
They both get a segment, a line(not necessary an infinitive one). In addition to the one they already have.
The first function checks whether there is an intersection point(only one), and returns a boolean value.
The second one should check the same and return the point itself.
// Returns true if the lines intersect, false otherwise
public boolean isIntersecting(Line other) {
if (equals(other)){
return false;
}
double x11 = this.start.getX();
double y11 = this.start.getY();
double x12 = this.end.getX();
double y12 = this.end.getY();
double x21 = other.start.getX();
double y21 = other.start.getY();
double x22 = other.end.getX();
double y22 = other.end.getY();
if (x11 == x12 && x21 == x22) { // both lines are constant x
// no intersection point
return false;
} else if (x11 == x12 || x21 == x22) { // either line is constant x
double x;
double m;
double b;
if (x11 == x12) { // first line is constant x, second is sloped
x = x11;
m = (y22 - y21) / (x22 - x21);
b = (x22 * y21 - x21 * y22) / (x22 - x21);
} else { // second line is constant x, first is sloped
x = x21;
m = (y12 - y11) / (x12 - x11);
b = (x12 * y11 - x11 * y12) / (x12 - x11);
}
double y = m * x + b;
Point.intersection = new Point (x,y);
return true;
} else { // both lines are sloped
double m1 = (y12 - y11) / (x12 - x11);
double b1 = (x12 * y11 - x11 * y12) / (x12 - x11);
double m2 = (y22 - y21) / (x22 - x21);
double b2 = (x22 * y21 - x21 * y22) / (x22 - x21);
if ((long) m1 == (long) m2) {
// no intersection point
return false;
}
// calculating intersection coordinates
double x = (b2 - b1)/(m1 - m2);
double y = m1 * x + b1; // or m2 * x + b2
Point.intersection = new Point (x,y);
return true;
}
}
// Returns the intersection point if the lines intersect,
// and null otherwise.
public Point intersectionWith(Line other) {
if (isIntersecting(other)) {
return Point.intersection;
}
return null;
}
I thought about "saving" the point in a static variable in the first function before returning a "true" value, and then using this static variable to "pull" the point in the second function and return the values of it.
Also, do you think that regarding segments and not infinitive lines, I should add more conditions to the first function?
I mean yes, lets say the both have the same slope, but it does not mean they can't have only one intersection point. Because if we are talking about
Line1: (1,0) (1,2)
and Line2: (1,2) (1,3)
They both have the same slope, but they still have an intersection point at (1,2).
So what do you think I should add to the code to eliminate all these cases?
I thought about something like:
if(!this.start().equals(line.start())
||!this.start().equals(line.end())
||!this.end().equals(line.start())
||!this.end().equals(line.end()))
return false;
I think I should also add a small check that checks whether the intersection point is on both of the segments. I should take the X & Y values of the intersection point and check whether the X value is between the X value of the starting point and the X value of the ending point (on both segments) and do the same check for the Y value.
What do you think?
Thanks.
wouldn't it be easier to de it the other way around tho ?
public Point isIntersecting(Line other) {
//your calculation here
return Point
}
public Boolean intersectionWith(Line other) {
Point p=isIntersecting(other)
if(p==null)
return false
return true
}

Having trouble solving cubic equations in Java

I'm attempting to follow some psuedo code for solving cubic equations in Mathematics and Physics for Programmers, Chapter 3, as far as I can see I've followed it accurately, but I don't seem to be getting correct outputs.
For example: According to Wolfram Alpha 5x^3 + 4x^2 + 3x + 2 = 0 should give a root of x≈-0.72932, but I'm getting back -1.8580943294965526 from my script.
Can someone help me to work out what the hell I'm doing? I'm following the scripts to get a better understanding of maths and converting equations to code. But this is at the brink of my understanding so I'm finding it troublesome to debug. Coupled with the fact the book has no errata and many online reviews state the book has many errors, I'm struggling to see whether the issue is with my code, the books explanation or both.
The equation given in the book is:
Then if the discriminant > 0 then root has value of r+s:
if discriminant == 0 then there are two roots:
if discriminant < 0 you can find three roots as follows:
After finding t you can transform it to x by taking:
package com.oacc.maths;
public class SolveCubic {
public static double[] solveCubic(double a, double b, double c, double d) {
double[] result;
if (d != 1) {
a = a / d;
b = b / d;
c = c / d;
}
double p = b / 3 - a * a / 9;
double q = a * a * a / 27 - a * b / 6 + c / 2;
double D = p * p * p + q * q;
if (Double.compare(D, 0) >= 0) {
if (Double.compare(D, 0) == 0) {
double r = Math.cbrt(-q);
result = new double[2];
result[0] = 2 * r;
result[1] = -r;
} else {
double r = Math.cbrt(-q + Math.sqrt(D));
double s = Math.cbrt(-q - Math.sqrt(D));
result = new double[1];
result[0] = r + s;
}
} else {
double ang = Math.acos(-q / Math.sqrt(-p * p * p));
double r = 2 * Math.sqrt(-p);
result = new double[3];
for (int k = -1; k <= 1; k++) {
double theta = (ang - 2 * Math.PI * k) / 3;
result[k + 1] = r * Math.cos(theta);
}
}
for (int i = 0; i < result.length; i++) {
result[i] = result[i] - a / 3;
}
return result;
}
public static double[] solveCubic(double a, double b, double c) {
double d = 1;
return solveCubic(a, b, c, d);
}
public static void main(String args[]) {
double[] result = solveCubic(5, 4, 3, 2);
for (double aResult : result) {
System.out.println(aResult);
}
}
}
I also found this code example from the book site(n.b. this is not the psuedo code from the book): http://www.delmarlearning.com/companions/content/1435457331/files/index.asp?isbn=1435457331
on solveCubic(a,b,c,d)
--! ARGUMENTS:
a, b, c, d (all numbers). d is optional (default is 1)
--!
RETURNS: the list of solutions to dx^3+ax^2+bx+c=0
-- if d is defined then divide a, b and c by d
if not voidp(d)
then
if d=0 then return solveQuadratic(b,c,a)
set d to float(d)
set a to a/d
set b to b/d
set
c to c/d
else
set a to float(a)
set b to float(b)
set c to float(c)
end if
set p to b/3 - a*a/9
set q to a*a*a/27 - a*b/6 + c/2
set disc to p*p*p + q*q
if abs(disc)<0.001 then
set r to cuberoot(-q)
set ret to [2*r, -r]
else if disc>0 then
set r to cuberoot(-q+sqrt(disc))
set s to cuberoot(-q-sqrt(disc))
set ret to [r+s]
else
set ang to acos(-q/sqrt(-p*p*p))
set r to 2*sqrt(-p)
set ret to []
repeat with k=-1 to 1
set theta
to (ang - 2*pi*k)/3
ret.add(r*cos(theta))
end repeat
end if
set ret to ret-a/3 --NB: this adds the value to each
element
return ret
end
The error appears to be with the names of the parameters of your solveCubic method.
It seems your book is explaining how to solve the equation x3 + ax2 + bx + c = 0. You are calling your method thinking that the parameters a, b, c and d are for the equation ax3 + bx2 + cx + d = 0. However, it turns out that the body of your method is actually finding solutions to the equation dx3 + ax2 + bx + c = 0.
Aside from this naming error, the calculations appear to be correct. Try plugging your value ≈-1.858 into 2x3 + 5x2 + 4x + 3 if you don't believe me.
If you instead declare your solveCubic method as
public static double[] solveCubic(double d, double a, double b, double c) {
the parameters then correspond to the equation dx3 + ax2 + bx + c. You should then find that your method gives you the answer you expect.
Okay. So, first off the equations from the book seem to be referring to this idea: If you have an equation of the form:
Then by defining t as x - (a/3) you can transform this into an equation with no quadratic term, as verified by a bit of Mathematica:
Once you have no quadratic term, you can then apply the method given; let q be half the constant term, let p be one third the coefficient on the first power term, and define D (discriminant) as p*p*p - q*q.
All else then follows.
So why does your code not work? Because you've mislabeled the variables. a is the coefficient on x^2, not on x^3. If you call your method with the arguments (0.8, 0.6, 0.4, 1) or equivalently with (4, 3, 2, 5), you'll get the right answer.
(Or do as the other answer suggests and more around the variable names in the method declaration)
This works 100%. It has been tried and tested for all combinations.
public void solveCubicEquation(int A, int B, int C, int D) {
double a = (double) B / A;
double b = (double) C / A;
double c = (double) D / A;
System.out.println("Double values: ");
System.out.println(a + " " + b + " " + c);
double p = b - ((a * a) / 3.0);
double q = (2 * Math.pow(a, 3) / 27.0) - (a * b / 3.0) + c;
double delta = (Math.pow(q, 2) / 4) + (Math.pow(p, 3) / 27);
if (delta > 0.001) {
double mt1, mt2;
double t1 = (-q / 2.0) + Math.sqrt(delta);
double t2 = (-q / 2.0) - Math.sqrt(delta);
if (t1 < 0) {
mt1 = (-1) * (Math.pow(-t1, (double) 1 / 3));
} else {
mt1 = (Math.pow(t1, (double) 1 / 3));
}
if (t2 < 0) {
mt2 = (-1) * (Math.pow(-t2, (double) 1 / 3));
} else {
mt2 = (Math.pow(t2, (double) 1 / 3));
}
x1 = mt1 + mt2 - (a / 3.0);
} else if (delta < 0.001 && delta > -0.001) {
if (q < 0) {
x1 = 2 * Math.pow(-q / 2, (double) 1 / 3) - (a / 3);
x2 = -1 * Math.pow(-q / 2, (double) 1 / 3) - (a / 3);
} else {
x1 = -2 * Math.pow(q / 2, (double) 1 / 3) - (a / 3);
x2 = Math.pow(q / 2, (double) 1 / 3) - (a / 3);
}
} else {
System.out.println("here");
x1 = (2.0 / Math.sqrt(3)) * (Math.sqrt(-p) * Math.sin((1 / 3.0) * Math.asin(((3 * Math.sqrt(3) * q) / (2 * Math.pow(Math.pow(-p, (double) 1 / 2), 3)))))) - (a / 3.0);
x2 = (-2.0 / Math.sqrt(3)) * (Math.sqrt(-p) * Math.sin((1 / 3.0) * Math.asin(((3 * Math.sqrt(3) * q) / (2 * Math.pow(Math.pow(-p, (double) 1 / 2), 3)))) + (Math.PI / 3))) - (a / 3.0);
x3 = (2.0 / Math.sqrt(3)) * (Math.sqrt(-p) * Math.cos((1 / 3.0) * Math.asin(((3 * Math.sqrt(3) * q) / (2 * Math.pow(Math.pow(-p, (double) 1 / 2), 3)))) + (Math.PI / 6))) - (a / 3.0);
}
}
Note: will not work for imaginary values

How to find whether a 2D array (rectangular matrix) have a straight line

I stumbled upon this problem while solving an algorithm question. There is a rectangular matrix.
I get a pair of positions as input, then how can I calculate if they fall in a straight line, for eg. in this case I get input as (d,B)(c,D)(b,F)(a,H) which infact is straight line.
If we see it carefully, the counter on the short side is jumping by 1 and counter on the longer side is jumping by 2. If I write my code based on this logic then, will it be a safe assumption for a bigger rectangles or I can face issue with that logic ?
I'm assuming here that inclined straight line can only be of two types
1) Where both the counters jump by 1, like the diagonal of a square matrix.
2) The case discussed above where counter is jumping by 1 on shorter side and by 2 on longer side.
Can there be a group of points that may fall in straight line but does not fit either of the above conditions and neither they are in a single row or a single column ?
Well, let's start from two degenerated cases:
if you have 0 points, answer as you wish (either there's a line, or there's no such line)
if you have 1 or 2 points, the answer is always yes
Suppose, you have 3+ points and you want to check if they are all on the same line. Take two arbitrary points (x1, y1) and (x2, y2). Once again you have two cases:
x1 == x2; check that all the points are such that xi == x1
x1 != x2; check that all points are such that two conditions are met:
(y1 - y2) * (xi - x2) == (yi - y2) * (x1 - x2)
(y2*x1 - y1*x2) * (xi - x2) == (y2*xi - yi*x2) * (x1 - x2)
I.e. all points are on the same y = kx + b line where k and b are derived from (x1, y1) and (x2, y2)
In you case, when A = 1, B = 2, ..., H = 8; a = 1, b = 2, ..., d = 4 we have
(2, 4)
(4, 3)
(6, 2)
(8, 1)
points which are on the same line. Possible (C#) implementation:
private static bool SameLine(IEnumerable<Tuple<int, int>> points) {
if (null == points)
return true;
Tuple<int, int>[] data = points.ToArray();
// i = 2 - first two points are always on the same line
for (int i = 2; i < data.Length; ++i) {
int x1 = data[0].Item1;
int y1 = data[0].Item2;
int x2 = data[1].Item1;
int y2 = data[1].Item2;
int xi = data[i].Item1;
int yi = data[i].Item2;
// y = k * x + b where k = infinity
if (x1 == x2) {
if (xi != x1)
return false;
continue;
}
// Same k in y = k * x + b
if (!((y1 - y2) * (xi - x2) == (yi - y2) * (x1 - x2)))
return false;
// Same b in y = k * x + b
if (!((y2 * x1 - y1 * x2) * (xi - x2) == (y2 * xi - yi * x2) * (x1 - x2)))
return false;
}
return true;
}
Test
Tuple<int, int>[] test = new Tuple<int, int>[] {
new Tuple<int, int>(2, 4),
new Tuple<int, int>(4, 3),
new Tuple<int, int>(6, 2),
new Tuple<int, int>(8, 1),
};
// Same line
Console.Write(SameLine(test) ? "Same line" : "different lines");

Floating point accuracy [duplicate]

This question already has answers here:
Floating point arithmetic not producing exact results [duplicate]
(7 answers)
Closed 9 years ago.
I am writing a method which calculates the equation of a 2D Line in the form a*x+b*y=1
//Given two points, find the equation of the line by solving two linear equations and then test the result. (For simplicity, assume that delta !=0 here)
private boolean solveAndRetry(float x1,float y1, float x2,float y2) {
float delta = x1 * y2 - x2 * y1;
float deltaA = y2 - y1;
float deltaB = x1 - x2;
float a = deltaA / delta;
float b = deltaB / delta;
float c = 1;
//test
if (a * x2 + b * y2 == c) {
System.out.println("ok");
return true;
}
else {
System.out.println(a * x2 + b * y2-c);
return false;
}
}
When I ran it, I was expecting there would be all "ok"s, but that's not the case and I don't know why
public static void main(String[] args) {
for (float x = 0; x < 10; x += 0.01f) {
solveAndRetry(1, -1, x, 2);
}
}
Here are some lines in the result
ok
ok
ok
ok
ok
ok
ok
ok
-5.9604645E-8
ok
-5.9604645E-8
ok
ok
ok
1.1920929E-7
ok
ok
-5.9604645E-8
ok
A float has an accuracy of 6 to 7 decimal digits. Since rounding errors cannot be avoided, your results are as good as it can get.
Normally, you would never compare floating point numbers for equality. Instead of x == y always use a comparison with an interval:
Math.abs(x - y) < eps
for a suitably chosen eps.

Verify if a point is part of quadratic Bezier curve in Java

I want to verify if a Point is part of a quadratic Bezier curve defined by points p0, p1 and p2..
This is my function to obtain a Point in the curve with a certain t:
public static final Point quadratic (Point p0, Point p1, Point p2, double t) {
double x = Math.pow(1-t, 2) * p0.x + 2 * (1-t) * t * p1.x + Math.pow(t, 2) * p2.x;
double y = Math.pow(1-t, 2) * p0.y + 2 * (1-t) * t * p1.y + Math.pow(t, 2) * p2.y;
return new Point((int)x, (int)y);
}
Considering that the point B(t) in a quadratic curve is obtained as follows:
B(t) = (1 - t)^2 * p0 + 2 * t * (1 - t) * p1 + t^2 * p2
I should verify if a point P belongs to a curve by getting the t value for that point and comparing it to the Point obtained using that t param, but in Java I'm having problems with the precision of the variables.
My function to verify a point is the following:
public static final boolean belongsQuadratic (Point p, Point p0, Point p1, Point p2) {
double[] tx = obtainTs(p.x, p0, p1, p2);
double[] ty = obtainTs(p.y, p0, p1, p2);
if (tx[0] >= 0) {
if ((tx[0] >= ty[0] - ERROR && tx[0] <= ty[0] + ERROR) || (tx[0] >= ty[1] - ERROR && tx[0] <= ty[1] + ERROR)) {
return true;
}
}
if (tx[1] >= 0) {
if ((tx[1] >= ty[0] - ERROR && tx[1] <= ty[0] + ERROR) || (tx[1] >= ty[1] - ERROR && tx[1] <= ty[1] + ERROR)) {
return true;
}
}
return false;
}
public static double[] obtainTs (int comp, Point p0, Point p1, Point p2) {
double a = p0.x - 2*p1.x + p2.x;
double b = 2*p1.x - 2*p0.x ;
double c = p0.x - comp;
double t1 = (-b + Math.sqrt(b*b - 4*a*c)) / (2*a);
double t2 = (-b - Math.sqrt(b*b - 4*a*c)) / (2*a);
return new double[] {t1, t2};
}
So if I run the code with this values:
Point p0 = new Point(320, 480);
Point p1 = new Point(320, 240);
Point p2 = new Point(0, 240);
double t = 0.10f;
Point p = Bezier.quadratic(p0, p1, p2, t);
double[] ts = Bezier.obtainTs(p.x, p0, p1, p2);
I obtain the following output:
For t=0.10000000149011612, java.awt.Point[x=316,y=434]
For t1: -0.1118033988749895, java.awt.Point[x=316,y=536]
For t2: 0.1118033988749895, java.awt.Point[x=316,y=429]
java.awt.Point[x=316,y=434] belongs?: false
Should I use BigDecimal to perform the operations? Is there another way to verify this? Thanks
As an alternative answer, to circumvent the problem noted by Martin R, you can simply build a lookup table and resolve coordinates to on-curve-ness that way. When you build the curve, generate an N-point array of coordinates for incremental t values, and then when you need to test whether a coordinate lies on the curve, find the nearest t value to that coordinate by checking whether that coordinate is in the lookup table, or "near enough to" a coordinate in the lookup table. In code:
point[] points = new point[100];
for(i=0; i<100; i++) {
t = i/100;
points[i] = new point(computeX(t), computeY(t));
}
and then when you need on-curve-testing:
for(i=0; i<points.length; i++) {
point = points[i];
if(abs(point-coordinate)<3) {
// close enough to the curve to count,
// so we can use t value map(i,0,100,0,1)
}
}
building the LUT costs virtually nothing since we're generating those coordinate-for-t values already in order to draw the curve, and you're not going to run an on-curve test without first making sure the coordinate is even in the curve's bounding box.
There is an error here:
double[] ty = obtainTs(p.y, p0, p1, p2);
because obtainTs() uses the x-coordinates of p0, p1, p2 to find the t-parameter for
the y-coordinate of p.
If you change the method parameters to int (which can be the x- or y-coordinates of the point):
public static double[] obtainTs (int comp, int p0, int p1, int p2) {
double a = p0 - 2*p1 + p2;
double b = 2*p1 - 2*p0 ;
double c = p0 - comp;
double t1 = (-b + Math.sqrt(b*b - 4*a*c)) / (2*a);
double t2 = (-b - Math.sqrt(b*b - 4*a*c)) / (2*a);
return new double[] {t1, t2};
}
and call it
double[] tx = obtainTs(p.x, p0.x, p1.x, p2.x);
double[] ty = obtainTs(p.y, p0.y, p1.y, p2.y);
then your test code will return "true" (tested with ERROR = 0.02).
Note that if you write down the equation
B(t) = (1 - t)^2 * p0 + 2 * t * (1 - t) * p1 + t^2 * p2
for both x- and y-coordinate, then you can eliminate the t^2-Term and get a single
linear equation for t. This gives the following method, which might be slightly simpler
and does not use square roots:
public static final boolean belongsQuadratic2 (Point p, Point p0, Point p1, Point p2) {
double ax = p0.x - 2*p1.x + p2.x;
double bx = 2*p1.x - 2*p0.x ;
double cx = p0.x - p.x;
double ay = p0.y - 2*p1.y + p2.y;
double by = 2*p1.y - 2*p0.y ;
double cy = p0.y - p.y;
// "Candidate" for t:
double t = -(cx*ay - cy*ax)/(bx*ay - by*ax);
if (t < 0 || t > 1)
return false;
// Compute the point corresponding to this candidate value ...
Point q = Bezier.quadratic(p0, p1, p2, t);
// ... and check if it is near the given point p:
return Math.abs(q.x - p.x) <= 1 && Math.abs(q.y - p.y) <= 1;
}
Of course, one would have to check for special cases, such as bx*ay - by*ax == 0.
Note also that it is difficult do decide exactly if a point lies on the curve because
the point coordinates are rounded to integers.

Categories