Java: Intersection of two ellipse segments transformed into 3d space - java

I have segments of lines and ellipses (NOT planes and ellipsoids) transformed into 3d space and need to calculate whether two given segments intersect and where.
I'm using Java but haven't yet found a library which solves my problem, nor came across some algorithms that I could use for my own implementation.

For line-line intersection test there are several ways to solve. The classic way is using linear algebra (i.e., solving a linear matrix system) but from software development point of view I like more the Geometric-Algebra way, in the form of Plucker Coordinates, which only requires to implement vector algebra operations (i.e., cross-product and dot-product) which are simpler to code than matrix operations for solving linear systems.
I will show both for the sake of comparison then you decide:
Linear Algebra Way
Given line segment P limited by points P1 and P2 and line segment Q limited by points Q1 and Q2.
The parametric form of the lines is given by:
P(t) = P1 + t (P2 - P1)
Q(t) = Q1 + t (Q2 - Q1)
Where t is a real number in the interval [0 1].
If two lines intersect then the following equation holds:
P(t0) = Q(t1)
Provided that the two unknown numbers t0 and t1 exist. Expanding the above equation we get:
t0 (P2 - P1) - t1 (Q2 - Q1) = Q1 - P1
We can solve for t0 and t1 by expressing the above equation in matrix algebra:
A x = B
Where A is a 3x2 matrix with coordinates of vector (P2 - P1) in the first column and coordinates of the vector (Q2 - Q1) in the second column; x is a 2x1 column vector of unknowns t0 and t1 and B is a 3x1column vector with coordinates of vector (Q1 - P1).
Classically the system can be solved calculating the pseudo-inverse of matrix A, denoted by A^+:
A^+ = (A^T A)^-1 A^T
See:
https://en.m.wikipedia.org/wiki/Generalized_inverse
Fortunately any matrix package in Java should be able to compute the above calculations very easily and perhaps very efficiently too.
If multiplying A with its pseudo-inverse A^+ is equal to the identity matrix I, i.e., (A A^+) == I, then there IS a unique answer (intersection) and you can get it computing the following product:
x = A^+ B
Of course if you cannot compute the pseudo-inverse in the first place, e.g., because (A^T A) is singular (i.e. determinant is zero), then no intersection exists.
Since we are dealing with segments, the intersection point is at point P(x0) or Q(x1) iff x0 and x1 are both in the interval [0 1].
OTHER METHOD OF SOLUTION
To avoid dealing with matrix algebra we can try to solve the system using vector algebra and substitution method:
t0 (P2 - P1) - t1 (Q2 - Q1) = Q1 - P1
t0 = a + t1 b
t1 = C • (Q1 - (1 - a) P1 - a P2) / |C|^2
Where:
a = (P2 - P1) • (Q1 - P1) / |P2 - P1|^2
b = (P2 - P1) • (Q2 - Q1) / |P2 - P1|^2
C = b (P2 - P1) - (Q2 - Q1)
I cannot provide a geometric intuition of the above results yet.
Plucker Coordinates way
Given line segment P limited by points P1 and P2 and line segment Q limited by points Q1 and Q2.
The Plucker Coordinates of line P is given by a pair of 3d vectors (Pd, Pm):
Pd = P2 - P1
Pm = P1 x P2
Where Pm is the cross-product of P1 and P2. The Plucker Coordinates (Qd, Qm) of line Q can be calculated in exactly the same way.
The lines P and Q only can intersect if they are coplanar. Thr lines P and Q are coplnanar iif:
Pd • Qm + Qd • Pm = 0
Where (•) is the dot-product. Since machines have finite precision a robust test should check not for zero but for a small number. If Pd x Qd = 0 then lines are parallel (here 0 is the zero vector). Again a robust test should be for instamce that the squared length of (Pd x Qd) is small.
If the lines are not parallel then they are coplanar and their intersection (called "meet" in Plucker's jargon) will be the point:
x = ((Pm • N) Qd - (Qm • N) Pd - (Pm • Qd) N) / (Pd x Qd) • N
Where N is any coordinate axis vector (i.e., (1,0,0) or (0,1,0) or (0,0,1)), such that (Pd x Qd) • N is non-zero.
If the neither P nor Q pass through the origin, then their Plucker coordinate Pm and Qm respectively will be non-zero and the following sinpler formula will work
x = Pm x Qm / Pd • Qm
For an introduction to Plucker Coordinates see:
https://en.m.wikipedia.org/wiki/Plücker_coordinates
http://www.realtimerendering.com/resources/RTNews/html/rtnv11n1.html#art3
For a general intersection formula see "Corollary 6" of:
http://web.cs.iastate.edu/~cs577/handouts/plucker-coordinates.pdf
Transforming Ellipses to Circles Forth and Back
We can always transform an ellipse into a circle. An ellipse has two "radius", called semi-axes, which you can visualize in your mind as two orthogonal vectors, one big called major semi-axes and one small called minor semi-axes. You can apply a non-uniform scaling transformation to both semi-axes vectors for making them of equal size, so you get a circle.
We define an ellipse "E" by its center O, which is a 3d point and its two semi-axes A1 and A2, which are also 3d vectors. A normal vector N to the ellipse's plane can be computed by the cross product of its semi-axes N = A1 x A2 and then normalizing it to have unit length.
For now suppose there is a linear function M that you can use to transform (scale) your ellipse E into a circle C, with radius equal to the minor semi-axes, by applying it to your ellipse's semi-axes A1 and A2 and to the ellipse's center O.
Notice that the eliipse's center O and ellipse's normal vector N are not changed by M. So M(N) = N and M(O) = O. That means that the circle is in the same plane and has the same position C than the ellipse. The linear function M has a corresponding inverse function M^-1 so we can transform back the vectors of the circle to get the original ellipse E.
Of course we can transform the endpoints of lines P and Q also using function M for sending them to the "circle space" and we can send them back to "ellipse space" using M^-1.
Using M we can compute the intersection of the lines P and Q with the ellipse E in the circle space. So now we can focus on the line-circle intersection.
Line-Plane Intersection
Given a plane with normal vector N and distance D such that:
N • x + D = 0
For every point x in the plane. Then the intersection with line P with Plucker Coordinates (Pd, Pm) is given by:
x = (N x Pm - D Pd) / N • Pd
This works only if the line P is not in the plane i.e.,:
(N • P1 + D) != 0 and (N • P2 + D) != 0
And for our ellipse E we have:
N = (A1 x A2)/|A1 x A2|
D = -N • O
Line-Circle and Point-Circle Intersection
Now having x, the point-in-circle check is easy:
|O - x| <= |A2|
Equality holds only when x is in circle boundary.
If line P is in circle's plane then the following sinple check will give you the answer:
https://stackoverflow.com/a/1079478/9147444
How to Compute the Linear Function M
A simple way to compute M is the following. Use the Ellipse's normal vector N and semi-axes A1 and A2 to create a 3x3 matrix U. Such that U has the vectors A1, A2 and N as columns.
Then scale the major semi-axes A1 to have the same length to the minor semi-axes A2. Then create the matrix V auch that V has the new vector A1 and A2 and N as columns.
Then we define the linear matrix system:
R U = V
Where R is a 3x3 (non-uniform-)scaling-rotation matrix.
We can solve for R by multiplying both sides of the equation by the inverse of U which is denoted by U^-1
R U U^-1 = V U^-1
Since U U^-1 is the identity matrix we get:
R = V U^+
Using R we define the affine transformation
M(x) = R (x - O) + O
We can use M to transform points to circle space, such as O, P1, P2, Q1 and Q2. But if we need to transform vectors such as A1, A2, N, Pd and Qd. We need to use the simpler M:
M(x) = R x
Since A1, A2 and N are eigenvectors of R then R is not singular and has an inverse. We define the inverse M as:
M^-1(x) = R^-1 (x - O) + O
And for vectors:
M^-1(x) = R^-1 x
Update: Ellipse-Ellipse intersection
Two intersecting non-coplanar 3d-ellipses have their intersection points on the line formed by the intersection between their planes. So you first find the line formed by the intersection of the planes containig the ellipses (if planes do not intersect i.e., they are parallel, then neither the ellipses intersect).
The line of intersection belong to both planes, so it is perpendicular to both normals. The direction vector V is the cross product of the plane normals:
V = N1 × N2
To fully determine the line we also need to find a point on the line. We can do that solving the linear equations of the planes. Given the 2x3 matrix N = [N1^T N2^T] with the normals N1 and N2 as rows, and the 2x1 column vector b = [N1 • C1, N2 • C2], where C1 and C2 are the centers of the ellipses, we can form the linear matrix system:
N X = b
Where X is some point satifying both plane equations. The system is underdetermined since there are infinite number of points in the line satifying the system. We can still find a particular solution closer to the origin by using the pseudo-inverse of matrix N, denoted by N^+.
X = N^+ b
The line equation is
L(t) = X + t V
For some scalar t.
Then you can apply the method described earlier to test the line-ellipse intersection i.e., scaling the ellipse to a circle and intersect with the coplanar line. If both ellipses intersect the line in the same points then they intersect.
Now, the case in which the ellipses actually lie on the same plane is more complex. You can ceck the approach taken by Dr Eberly in his excellent book "Geometric Tools" (also available online):
https://www.geometrictools.com/Documentation/IntersectionOfEllipses.pdf
And also you can check the C++ source code which is open source:
https://www.geometrictools.com/GTEngine/Include/Mathematics/GteIntrEllipse2Ellipse2.h

Related

How to know if point is on the right side or on the left side of line [duplicate]

This question already has answers here:
How to tell whether a point is to the right or left side of a line
(16 answers)
Closed 1 year ago.
I talking about 3D
I have 2 points on a plane(A and B) and I made a Vector AB (B-A) from them. I have made a line with point A and Vector AB. On the same plane I have another point. I want to know if the point is on the right side or on the left side of the line I have made.
Thanks for help..
You have got your two points A, B, and a third point C.
You may already know that you can find the cosinus of an angle, using the dot product:
cos(AB, AC) = AB . AC / (||AB|| ||AC||)
Where . denotes the dot product and || || the Euclidean norm.
However, what you want is not the cosinus of the angle, but the sinus of the angle. The sinus sin(AB, AC) will be positive if C is on the left of AB, and negative if C is on the right of AB.
Let's call D the point obtained by taking the image of B by the rotation of centre A and angle +Pi/2 (rotation by a quarter-turn counterclockwise).
Then it turns out that:
sin(AB, AC) = cos(AD, AC)
Furthermore, the coordinates of vector AD are easy to compute: if vector AB has coordinates (x,y), then vector AD has coordinates (-y, x).
Putting all this together, we get the following formula:
sin(AB,AC) = ((yA - yB)*(xC-xA) + (xB-xA)*(yC-yA)) / sqrt(((xB-xA)**2+(yB-yA)**2)*((xC-xA)**2 + (yC-yA)**2))
This is a number between -1 and +1. If all you care about is a "left" or "right" answer, then you only want the sign of sin(AB,AC); and the denominator of this expression is always positive. Hence you only need to compute the numerator:
n = ((yA - yB)*(xC-xA) + (xB-xA)*(yC-yA))
if n > 0:
print("C is on the LEFT of AB")
else if n < 0:
print("C is on the RIGHT of AB")
else if n == 0:
print("C is on AB")

JAVA - return array elements between two points

I've been trying to make a code that goes through all array elements that are between two specified points, but I am stuck.
Let's suppose it's an array like that:
int[][] new_array = new int[100][100];
And how do I get all elements that are in straight line between let's say
new_array[17][2];
and
new_array[5][90];
This is what I want to achieve:
Let's imagine that your array is a first quadrant of a cartesian coordinates system. With a first column lying on axis Y and last row lying on axis X.
Having that assumption you could find a function that describes a straight line between any of two points in your array.
You need to solve the function:
y = ax + b
It's a standard linear function. You have two points, solving that you'll find your equation (values of a and b).
When you know equation you need to evaluate points in the array for each x value. Doing that you'll find all y values that are below/on/above the line.
Following #Marcin Pietraszek's answer the function can be obtained this way:
Given the two points (a,b) and (c,d) the straight line that passes through both points is given by
a + K * (x - a) = c AND b + K (y - b) = d
where K is a scalar number.
And this resolves to:
y = ( (d - b) * x - (d - b) * a + (c - a) * b ) / (c - a)
So any point (x, y) that meets this condition will be on the straight line.
You will need go through the matrix, checking one by one to see which points meet the condition.
If you want only the point inside the segment, then aditionally you need to check the boundaries.

Generating intermediate vertices

I have a path say [vertex4, vertex5, vertex7, vertex8] starting at vertex 4, ending at vertex 8. I have access to the X and Y co-ordinates of each vertex.
How would I go about generating a series of vertices in between each pair of vertices. Say for instance, vertex4 -> vertex5 I want to be able to bisect the edge so that there will be more vertices along the edge to get to 5.
So for example, if I have a max 'step' size or something of 0.1, then the path would then be:
[4, 4.1, 4.2, 4.3 ... and so on up to 5].
I'm assuming each vertex is a point (X,Y) on the 2D plane and the edges are straight lines connecting them. You can easily bisect any edge on K equal parts:
Let A = (X0,Y0) and B = (X1,Y1) be the endpoints of the edge, then we can compute dX = (X1-X0)/K and dY = (Y1-Y0)/K. The intermediate points will be points of the form Pi = (X0+dXi,Y0+dYi) for 0 < i < K.
If you want the edges to have some specified length L, compute the length D of the edge AB using euclidian distance, and the number K of parts will be given by ceil(D/L).

Java - Polar Coordinates. Given a line segment AB and a point P, find the closest point on the polar line AB to point P

I'm given a line segment with two endpoints: (x1,y1) (x2,y2) and a random point: (x3,y3).
If I convert the line segment to polar coordinates, I need to be able to figure out, programmatically, a point on the line segment that is the closest to point (x3,y3).
EDIT:
I made a mistake in my question. The problem isn't trying to find the closest point between the three. The problem is... given a line AB with a start and an end... find ANY point on the line that is closest to point (x3,y3).
I guess this is too late, just in case someone needs it, I just convert back to cartesian coordinates so the formula is easier to look at:
public static double distance(double r1,double t1,double r2,double t2,double r3,double t3)
{
double x1 = (r1*Math.cos(t1));
double x2 = (r1*Math.cos(t2));
double x3 = (r1*Math.cos(t3));
double y1 = (r1*Math.sin(t1));
double y2 = (r1*Math.sin(t2));
double y3 = (r1*Math.sin(t3));
return Math.abs((x2-x1)*(y1-y3)-(x1-x3)*(y2-y1))/Math.sqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1));
}
This question involves elementary school level algebra:
There are only 3 options for the closest point:
one of the points (x1,y1) (x2,y2)
another point between them on the line segment
It's easy to find the distance between (x3,y3) to the two points using Pythagoras:
d1 = d(<x1,y1>,<x3,y3>) = sqrt( (x1-x3)^2 + (y1-y3)^2)
and
d2 = d(<x2,y2>,<x3,y3>) = sqrt( (x2-x3)^2 + (y2-y3)^2)
and now for the "more complicated" part: if there's another point on the segment which is closer to (x3,y3) - if we connect these points - we'll have a new segment that will be diagonal to the original segment - we have to use that together with a first degree equation (for a line) to find that third point and see if it's on the segment or outside (if it's outside of the segment then we'll take the minimum distance between (x3,y3) to the other two points.
The line equation (excuse my English - I learnt it in my native language so bear with me) is:
(I) y = mx + d
where m is easy to calculate:
m = (y1-y2)/(x1-x2)
and now that we know the value of m in order to find d we'll use the values of one of the first two points, for example:
y1 = mx1 +d => d = y1 - mx1
the diagonal line will have m' which is -1/m and if we'll use the values of (x3,y3) we'll find d'. So now we know both the values of (I) as well as the equation for the diagonal line:
(II) y=m'x + d'
and if we'll use these two equations we can find the point on the line through which the original segment goes through which is the closest to (x3,y3) - if this point lies on the segment - we're done, otherwise, like earlier mentioned - we'll take the minimum between d1 and d2
public static Location closest(double x1, double y1, double x2, double y2, double x3, double y3){
Location a = new Location("provider");
Location b = new Location("provider");
Location c = new Location("provider");
//I can't remember which one goes where, you may need to swap all the x/ys around
//Do conversion or something here.
a.setLongitude(x1);
a.setLatitude(y1);
b.setLongitude(x2);
b.setLatitude(y2);
c.setLongitude(x3);
c.setLongitude(y3);
return closest(a, b, c);
}
public static Location closest(Location a, Location b, Location c){
return a.distanceTo(c) < b.distanceTo(c) ? a : b;
}

Rotation of indices of a 2d array by 90 degrees

I know how to rotate an entire 2d array by 90 degrees around the center(My 2d array lengths are always odd numbers), but I need to find an algorithm that rotates specific indices of a 2d array of known length. For example I know that the 2d array is a 17 by 17 grid and I want the method to rotate the indices [4][5] around the center by 90 degrees and return the new indices as two separate ints(y,x); Please point me in the right direction or if your feeling charitable I would very much appreciate some bits of code - preferably in java. Thanks!
Assuming cartesian coordinates (i.e. x points right, and y points up) and that your coordinates are in the form array[y][x] the center [cx, cy] of your 17x17 grid is [8, 8].
Calculate the offset [dx, dy] of your point [px, py] being [4, 5] from there, i.e. [-4, -3]
For a clockwise rotation, the new location will be [cx - dy, cy + dx]
If your array uses the Y axis pointing "downwards" then you will need to reverse some of the signs in the formulae.
For a non-geometric solution, consider that the element [0][16] needs to get mapped to [16][16], and [0][0] mapped to [0][16]. i.e. the first row maps to the last column, the second row maps to the second last column, etc.
If n is one less than the size of the grid (i.e. 16) that just means that point [y][x] will map to [x][n - y]
In theory, the geometric solution should provide the same answer - here's the equivalence:
n = 17 - 1;
c = n / 2;
dx = x - c;
dy = y - c;
nx = c - dy = c - (y - c) = 2 * c - y = n - y
ny = c + dx = c + (x - c) = x
​
If you have a square array with N elements in each row/column a 90deg turn anti-/counter-clockwise sends (x,y) to (N+1-y,x) doesn't it ?
That is, if like me, you think that the top-left element in a square array is (1,1) and row numbers increase down and column numbers to the right. I guess someone who counts from 0 will have to adjust the formula somewhat.
The point in Cartesian space x,y rotated 90 degrees counterclockwise maps to -y,x.
An array with N columns and M rows would map to an array of M columns and N rows. The new "x" index will be non-positive, and will be made zero-based by adding M:
a[x][y] maps to a[M-y][x]

Categories