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.
Related
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
}
I have recently been playing around with Bicubic Interpolation, as I am wanting to generate the earth, based on real heightmaps inside Minecraft. The reason I am using the interpolation, is because I would like to make the world have more detail. After a lot of research, and a lot of trial and error, I decided to come ask here. :)
Because of limited memory, I can't scale the image on startup, and keep that loaded, I have to do the interpolation on the fly.
I seem to have gotten Cubic Interpolation to work, as seen here:
Visualisation of the interpolation
However, I can not get Bicubic Interpolation to work. For testing purposes, I am using a small image, and scaling it by 4. This is what the code does: Input -> Output
This is my current code:
public static double cubicInterpolate(double[] points, double x, double scale)
{
x /= scale;
double inBetweenPoint = x;
int xInHeightmap = (int) x;
inBetweenPoint -= xInHeightmap;
double beforePoint1 = safe(points, xInHeightmap - 1);
double point1 = safe(points, xInHeightmap);
double point2 = safe(points, xInHeightmap + 1);
double afterPoint2 = safe(points, xInHeightmap + 2);
double p = (afterPoint2 - point2) - (beforePoint1 - point1);
double q = (beforePoint1 - point1) - p;
double r = point2 - beforePoint1;
double s = point1;
return (p * Math.pow(inBetweenPoint, 3)) + (q * Math.pow(inBetweenPoint, 2)) + (r * inBetweenPoint) + s;
}
public static double bicubicInterpolate(double[][] points, double x, double y, double scale)
{
x /= scale;
double inBetweenPoint = x;
int xInHeightmap = (int) x;
inBetweenPoint -= xInHeightmap;
double beforePoint1 = cubicInterpolate(safe(points, xInHeightmap - 1), y, scale);
double point1 = cubicInterpolate(safe(points, xInHeightmap), y, scale);
double point2 = cubicInterpolate(safe(points, xInHeightmap + 1), y, scale);
double afterPoint2 = cubicInterpolate(safe(points, xInHeightmap + 2), y, scale);
return cubicInterpolate(new double[]{beforePoint1, point1, point2, afterPoint2}, inBetweenPoint + 1, scale);
}
public static double[] safe(double[][] p, int i)
{
return p[Math.max(0, Math.min(i, p.length - 1))];
}
public static double safe(double[] p, int i)
{
return p[Math.max(0, Math.min(i, p.length - 1))];
}
Thank you for your help :)
To my understanding, your implementaion treats the given x and y coordinates in a totally different way, which does not lead to the desired result. You should basically do the following.
First, you need to identify the four points (coordinate pairs) in the grid which will form the basis of the interpolation, as well as the distances in both directions from the grid to to the interpolation. This can be done as follows.
int xfloor = (int)x;
int yfloor = (int)y;
int xdelta = x - (double)xfloor;
int ydelta = y - (double)yfloor;
The desired coordinate pairs are then (depending on the orientation of the axes)
P1 = (xfloor, yfloor ) // left upper corner
P2 = (xfloor, yfloor + 1) // left lower corner
P3 = (xfloor + 1 ,yfloor + 1) // right lower corner
P4 = (xfloor + 1, yfloor ) // left upper corner
and finally you wouöd first interpolate along parallel axes and then in the middle, which can be done as follows using intermediate values.
val1 = cubic(value(P1), value(P2), deltay) // interpolate cubically on the left edge
val2 = cubic(value(P4), value(P3), deltay) // interpolate cubically on the right edge
val = cubic (val1, val2, deltax) // interpolate cubically between the intermediates
Interpolation methods are also discussed here.
In your bicubic-interpolation method you write:
//double inBetweenPoint = x;
//int xInHeightmap = (int) x;
//inBetweenPoint -= xInHeightmap;
return cubicInterpolate(new double[]{beforePoint1, point1, point2, afterPoint2}, inBetweenPoint, scale);
As you can easily see, inBetweenPoint will be in the interval [0, 1) at the call to cubicInterpolate. This means that interpolation will be between beforePoint1 and point1. not, as wanted, between point1 and point2.
The simple fix is writing
return cubicInterpolate(new double[]{beforePoint1, point1, point2, afterPoint2}, inBetweenPoint + 1, scale);
I am working through the solution to the following problem:
Given two squares on a two-dimensional plane, find a line that would
cut these two squares in half. Assume that the top and the bottom
sides of the square run parallel to the x-axis.
This is the solution in the book:
public class Square {
public double left;
public double top;
public double bottom;
public double right;
public double size;
public Square(double left, double top, double size) {
this.left = left;
this.top = top;
this.bottom = top + size;
this.right = left + size;
this.size = size;
}
public Point middle() {
return new Point((this.left + this.right)/2.0, (this.top + this.bottom)/2.0);
}
public boolean contains(Square other) {
if (this.left <= other.left && this.right >= other.right && this.top <= other.top && this.bottom >= other.bottom) {
return true;
}
return false;
}
/* Return the point where the line segment connecting mid1 and
* mid2 intercepts the edge of square 1. That is, draw a line
* from mid2 to mid1, and continue it out until the edge of
* the square. */
public Point extend(Point mid1, Point mid2, double size) {
/* Find what direction the line mid2 -> mid1 goes */
double xdir = mid1.x < mid2.x ? -1 : 1;
double ydir = mid1.y < mid2.y ? -1 : 1;
/* If mid1 and mid2 have the same x value, then the slope
* calculation will throw a divide by 0 exception. So, we
* compute this specially. */
if (mid1.x == mid2.x) {
return new Point(mid1.x, mid1.y + ydir * size / 2.0);
}
double slope = (mid1.y - mid2.y) / (mid1.x - mid2.x);
double x1 = 0;
double y1 = 0;
/* Calculate slope using the equation (y1 - y2) / (x1 - x2).
* Note: if the slope is “steep” (>1) then the end of the
* line segment will hit size / 2 units away from the middle
* on the y axis. If the slope is “shallow” (<1) the end of
* the line segment will hit size / 2 units away from the
* middle on the x axis. */
if (Math.abs(slope) == 1) {
x1 = mid1.x + xdir * size / 2.0;
y1 = mid1.y + ydir * size / 2.0;
} else if (Math.abs(slope) < 1) {
x1 = mid1.x + xdir * size / 2.0;
y1 = slope * (x1 - mid1.x) + mid1.y;
} else {
y1 = mid1.y + ydir * size / 2.0;
x1 = (y1 - mid1.y) / slope + mid1.x;
}
return new Point(x1, y1);
}
public Line cut(Square other) {
/* Calculate where a line between each middle would collide with the edges of the squares */
Point p1 = extend(this.middle(), other.middle(), this.size);
Point p2 = extend(this.middle(), other.middle(), -1 * this.size);
Point p3 = extend(other.middle(), this.middle(), other.size);
Point p4 = extend(other.middle(), this.middle(), -1 * other.size);
/* Of above points, find start and end of lines. Start is farthest left (with top most as a tie breaker)
* and end is farthest right (with bottom most as a tie breaker */
Point start = p1;
Point end = p1;
Point[] points = {p2, p3, p4};
for (int i = 0; i < points.length; i++) {
if (points[i].x < start.x || (points[i].x == start.x && points[i].y < start.y)) {
start = points[i];
} else if (points[i].x > end.x || (points[i].x == end.x && points[i].y > end.y)) {
end = points[i];
}
}
return new Line(start, end);
}
public String toString() {
return "(" + left + ", " + top + ")|(" + right + "," + bottom + ")";
}
}
I have the following questions:
What is the size parameter in the extend function? The size of what? A side? The entire square (how can we quantify the size of the entire square?) Area?
In the cut() function, what are p1 through p4 ultimately doing? And why do we need 4 points to do it?
As I understand the problem, it's necessary and sufficient for the line to connect the two centres.
Assume that the top and the bottom sides of the square run parallel to the x-axis.
I'm not aware of why this assumption is necessary.
This answer might not be relevant to the TS. But in case if you are also wondering about this answer, the parameter "size" is misleading - it is referring to the length of the square (Eg. 3x3 square has the length of 3.) Why four points? Because the question assumes that the line here represents the two "farest" points that cut across two squares. Each square has two points that has the potential to become start/end of the line.
I have created a function to calculate the intersection point of two line segment .
Unfortunantly the code below dosen't work if one of the segment is verticale
public static Point intersection(Segment s1, Segment s2) {
double x1 = s1.getP1().getX();
double y1 = s1.getP1().getY() ;
double x2 = s1.getP2().getX();
double y2 = s1.getP2().getY() ;
double x3 = s2.getP1().getX();
double y3 = s2.getP1().getY();
double x4 = s2.getP2().getX();
double y4 = s2.getP2().getY();
double d = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);
if (d == 0) {
return null;
}
double xi = ((x3 - x4) * (x1 * y2 - y1 * x2) - (x1 - x2) * (x3 * y4 - y3 * x4)) / d;
double yi = ((y3 - y4) * (x1 * y2 - y1 * x2) - (y1 - y2) * (x3 * y4 - y3 * x4)) / d;
Point p = new Point(xi, yi);
if (xi < Math.min(x1, x2) || xi > Math.max(x1, x2)) {
return null;
}
if (xi < Math.min(x3, x4) || xi > Math.max(x3, x4)) {
return null;
}
return p;
}
the problem when i have a vertical line segment , this formula
double d = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);
is equal to 0 and the method return null.
How can I handle this exception.
Thank you
Line intersection without special cases
Coming from a background of projective geometry, I'd write the points in homogeneous coordinates:
v1 = [x1, y1, 1]
v2 = [x2, y2, 1]
v3 = [x3, y3, 1]
v4 = [x4, y4, 1]
Then both the line joining two points and the intersection of two lines can be expressed using the cross product:
[x5, y5, z5] = (v1 × v2) × (v3 × v4)
which you can dehomogenize to find the resulting point as
[x5/z5, y5/z5]
without having to deal with any special cases. If your lines are parallel, the last point would lead to a division by zero, though, so you might want to catch that case.
Restriction to segments
The above is for infinite lines, though. You might want to keep the code which returns null if the point of intersection falls outside the bounding box. But if you want real segments, that code is incorrect: you could have a point of intersection which lies outside one of the segments but still inside the bounding box.
A proper check can be implemented using an orientation-checking predicate. The determinant of three of the vectors vi given above will have positive sign if the triangle they form has one orientation, and negative sign for the opposite orientation. So the points v3 and v4 lie on different sides of s1 if
det(v1, v2, v3) * det(v1, v2, v4) < 0
and in a similar way v1 and v2 lie on different sides of s2 if
det(v3, v4, v1) * det(v3, v4, v2) < 0
so if both of these are satisfied, you have an intersection between the segments. If you want to include the segment endpoints, change the < to a ≤ in these inequalities.
I have tried code it without testing... I hope it works! ^^
public static Point intersection(Segment s1, Segment s2) {
// Components of the first segment's rect.
Point v1 = new Point(s1.p2.x - s1.p1.x, s1.p2.y - s1.p1.y); // Directional vector
double a1 = v.y;
double b1 = -v.x;
double c1 = v1.x * s1.p1.y - s1.v.y * s1.p1.x;
// Components of the second segment's rect.
Point v2 = new Point(s2.p2.x - s2.p1.x, s2.p2.y - s2.p1.y);
double a2 = v2.y;
double b2 = -v2.x;
double c2 = v2.x * s2.p1.y - s2.v.y * s2.p1.x;
// Calc intersection between RECTS.
Point intersection = null;
double det = a1 * b2 - b1 * a2;
if (det != 0) {
intersection = new Point(
(b2 * (-c1) - b1 * (-c2)) / det;
(a1 * (-c2) - a2 * (-c1)) / det;
);
}
// Checks ff segments collides.
if (
intersection != null &&
(
(s1.p1.x <= intersection.x && intersection.x <= s1.p2.x) ||
(s1.p2.x <= intersection.x && intersection.x <= s1.p1.x)
) &&
(
(s1.p1.y <= intersection.y && intersection.y <= s1.p2.y) ||
(s1.p2.y <= intersection.y && intersection.y <= s1.p1.y)
) &&
(
(s2.p1.x <= intersection.x && intersection.x <= s2.p2.x) ||
(s2.p2.x <= intersection.x && intersection.x <= s2.p1.x)
) &&
(
(s2.p1.y <= intersection.y && intersection.y <= s2.p2.y) ||
(s2.p2.y <= intersection.y && intersection.y <= s2.p1.y)
)
)
return intersection;
return null;
};
Here's my answer. I have tested it to be accurate by making a loop that checks that the answer it gives is the same as the one given by the Boost geometry library and they agree on each test, though the one that I have written below is much much faster than the one in Boost. The test makes every possible line segment where x is and integer in [-3,2] and y is an integer in [-3,2], for all possible pairs of line segments.
The code below considers line segments that touch at endpoints to be intersecting.
T-shaped intersections are also considered intersecting. The code is in c++ but would be easily adaptable to any language. It's based on a different stackoverflow answer but that answer did not handle endpoints correctly.
It uses the cross product method, which can report if a point is to the left or to the right of a given ray.
There are some optimizations to be made in the math but doing them showed no performance improvement over compilation with g++ -O2 and sometime the performance even decreased! The compiler is able to do those optimizations so I prefered to leave the code readable.
// is_left(): tests if a point is Left|On|Right of an infinite line.
// Input: three points p0, p1, and p2
// Return: >0 for p2 left of the line through p0 and p1
// =0 for p2 on the line
// <0 for p2 right of the line
// See: Algorithm 1 "Area of Triangles and Polygons"
// This is p0p1 cross p0p2.
extern inline coordinate_type_fp is_left(point_type_fp p0, point_type_fp p1, point_type_fp p2) {
return ((p1.x() - p0.x()) * (p2.y() - p0.y()) -
(p2.x() - p0.x()) * (p1.y() - p0.y()));
}
// Is x between a and b, where a can be lesser or greater than b. If
// x == a or x == b, also returns true. */
extern inline coordinate_type_fp is_between(coordinate_type_fp a,
coordinate_type_fp x,
coordinate_type_fp b) {
return x == a || x == b || (a-x>0) == (x-b>0);
}
// https://stackoverflow.com/questions/563198/how-do-you-detect-where-two-line-segments-intersect
extern inline bool is_intersecting(const point_type_fp& p0, const point_type_fp& p1,
const point_type_fp& p2, const point_type_fp& p3) {
const coordinate_type_fp left012 = is_left(p0, p1, p2);
const coordinate_type_fp left013 = is_left(p0, p1, p3);
const coordinate_type_fp left230 = is_left(p2, p3, p0);
const coordinate_type_fp left231 = is_left(p2, p3, p1);
if (p0 != p1) {
if (left012 == 0) {
if (is_between(p0.x(), p2.x(), p1.x()) &&
is_between(p0.y(), p2.y(), p1.y())) {
return true; // p2 is on the line p0 to p1
}
}
if (left013 == 0) {
if (is_between(p0.x(), p3.x(), p1.x()) &&
is_between(p0.y(), p3.y(), p1.y())) {
return true; // p3 is on the line p0 to p1
}
}
}
if (p2 != p3) {
if (left230 == 0) {
if (is_between(p2.x(), p0.x(), p3.x()) &&
is_between(p2.y(), p0.y(), p3.y())) {
return true; // p0 is on the line p2 to p3
}
}
if (left231 == 0) {
if (is_between(p2.x(), p1.x(), p3.x()) &&
is_between(p2.y(), p1.y(), p3.y())) {
return true; // p1 is on the line p2 to p3
}
}
}
if ((left012 > 0) == (left013 > 0) ||
(left230 > 0) == (left231 > 0)) {
if (p1 == p2) {
return true;
}
return false;
} else {
return true;
}
}
The test code:
BOOST_AUTO_TEST_CASE(small, *boost::unit_test::disabled()) {
for (double x0 = -3; x0 < 3; x0++) {
for (double y0 = -3; y0 < 3; y0++) {
for (double x1 = -3; x1 < 3; x1++) {
for (double y1 = -3; y1 < 3; y1++) {
for (double x2 = -3; x2 < 3; x2++) {
for (double y2 = -3; y2 < 3; y2++) {
for (double x3 = -3; x3 < 3; x3++) {
for (double y3 = -3; y3 < 3; y3++) {
point_type_fp p0{x0, y0};
point_type_fp p1{x1, y1};
point_type_fp p2{x2, y2};
point_type_fp p3{x3, y3};
linestring_type_fp ls0{p0,p1};
linestring_type_fp ls1{p2,p3};
BOOST_TEST_INFO("intersection: " << bg::wkt(ls0) << " " << bg::wkt(ls1));
BOOST_CHECK_EQUAL(
path_finding::is_intersecting(p0, p1, p2, p3),
bg::intersects(ls0, ls1));
}
}
}
}
}
}
}
}
}
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!