Check if if two line segments intersect in 3D space - c++

I'm trying to find if two line segments intersects in 3D space. Both segments are given by points (x1, y1, z1) and (x2, y2, z2). The following artcile describes how to do it in 2D space: https://www.geeksforgeeks.org/check-if-two-given-line-segments-intersect/ but I have no clue how to distinguish points orientation in 3D space (if they are clockwise or counter-clockwise). Thanks in advance.

Calculate direction vectors d1 and d2 for both segments (d1.x = x12-x11 and so on, where x11 is starting point of the first segment, x12 is ending point)
Calculate starting points difference (b.x = x21 - x11 and so on)
Find
p = (d1 x d2) (vector product)
If p is zero vector, then lines are parallel.
If lines do intersect, (b.dot.p) should be = 0 (scalar product), else these lines are skew.
Intersection point:
ip = point11 + d1 * [ (b x d2).dot.p / (p.dot.p) ]
Note that value in [] should be in range 0..1, otherwise lines intersect outside of segments.
Seems similar approach is described in wiki

Related

Finding point perpendicular to two intersecting lines in 3D

I have two lines (L1, L2) in 3D.
L has the following starting and ending coordinates : P1(x1, y1, z1) and P2(x2, y2, z2).
L2 has P3(x3, y3, z3) and P2(x2, y2, z2). Notice how both L1's and L2's P2 are same: meaning they intersect at this specific point.
Now I want to find a point P(x,y,z) at any distance from P2 that line (P,P2) is perpendicular to plane on which points (P1,P2,P3) are placed.
The cross product is the way of calculating perpendicularity relative to your two lines. You need to make vectors of your line parameters, simples way would be this:
vecL1 = (x1-x2, y1-y2, z1-z2) and
vecL2 = (x3-x2, y3-y2, z3-z2)
Cross product you can google how to calculate, but in this scenario:
//Replacing the new x,y,z's with i, j, k to avoid naming confusion.
vecL3 = vecL1 x vecL2 = (j1*k2 - j2*k1, k1*i2 - k2*i1, i1*j2 - j1*i2)
Now the cross product per definition is a new vector (line) that is strictly perpendicular to the two lines/vectors you used to calculate this with. But vectors lack position, so you need to add the intersection point to this vector in order to find some point.
//i3, j3, k3 being the third vector's parameters
P3(i3+x2, j3+y2, k3+z2)
PS: The distance from your P2 to P3 is per definition (how cross products work) to the area of a parallelogram that the two lines are sides of, I found a link to illustrate:
Normalizing the 3rd vector will make the distance equal to 1 from P2.
The cross product will give you a perpendicular vector to the plane described by two other vectors, in pseudo-code:
normal = cross(normalize(P1-P2), normalize(P3-P2))
Since you've defined P2 as the point of intersection, you can simply add this normal vector to P2 to get your perpendicular point.
If you have any line AB then an arbitrary point C will always be perpendicular to AB IFF triangle ABC has no angle larger than π/2
That means there will always be a point D on line AB such that CD is perpendicular to AB

Shift set of points in 3d

I have a set of points in 3d. I form a line by joining these points. I have to obtain another line which is a shifted version of this line, such that the resultant shift is always to the right of the original line. What would be an approach to solve this problem? How to get the up vectors in the right direction each time?
Assume these points to lie on a sphere. Looking at the top view of the sphere i would want something like this
/\/\/\/\
/\/\/\/\
The first line is the original set of points and the second one the shifted set
Ok i am adding the code
std::vector<osg::Vec3> vertArray; // The array containig the 3d points
std::vector<osg::Vec3> shiftVec; // The array to store the shifted vectors
osg::Vec3 line1, line2, result, upVec, p1, p2, cross1, cross2;
result = vertArray[1] - vertArray[0];
result.normalise();
result = result X upVec; // i am not sure how to get this upvec for general set of points
shiftVec.push_back(result);
for(unsigned int i = 1; i < vertArray.size() - 1; i++)
{
line 1 = vertArray[i-1] - vertArray[i];
line 2 = vertArray[i+1] - vertArray[i];
line1.normalise();
line2.normalise();
upVec = line1 X line2;
line 1 = line1 X upVec;
p1 = vertArray[i-1] + line1;
line 2 = line2 X upVec;
p2 = vertArray[i+1] + line2;
cross1 = upVec;
cross2 = (p2-p1)X line2
float factor = cross2.lenght()/cross1.length();
result = p1+line1*factor;
result = result - vertArray[i];
shiftVec.push_back(result);
}
result = vertArray[i] - vertArray[i-1];
result.normalise();
result = result X upVec; // i am not sure how to get this upvec for general set of points
shiftVec.push_back(result);
look here: ECEF <-> ENU coordinates this might help
I rather use NEH local North,East,Height(or altitude) coordinate system
it is similar to compass + altimeter
if you are not looking in rotation axis direction (ECEF Z-axis) ... on poles
then North vector is just (0,0,6356754.7)-viewer_position (all in ECEF)
East,West vectors can be obtained as North x (0,0,6356754.7)
don`t remember if it is east or west (depends on your coordinate system and cross multiplicants order)
just check it visually and if wrong reverse the order or negate result
Up vector (Height or Altitude) is easy from this just Up=North x East or Up=North x West
again if wrong direction reverse order or negate result ...
[Notes]
6356754.7 [m] is earths polar radius
if you viewing from poles (ECEF Z-axis)
then North vector and Up vector lies on the same axis (in opposite direction)
which means there is no east or west (singularity)
on south hemisphere is usually used South instead of North
in that case South = (0,0,-6356754.7)-viewer_position
If you three points are A, B and C. Then the three point define a plane. In general the points will not lie on a (straight) line. If they do then it becomes ambiguous what "right" means. If everything is on a sphere, then the three points will define a curve formed by the intersection of the sphere and the plane. You could form another line my finding the intersection of the sphere with a parallel plane.
I'm not quite sure what you want but I'm guessing you want the second line to lie in a parallel plane. You can find the normal to the plane by taking the cross product N=(A-B) X (C-B). It looks like you are doing something like this but you need the ^ operator. See https://www8.cs.umu.se/kurser/TDBD12/VT04/lab/osg/html/doc++/osg/Vec3.html#DOC.2.224.21
upVec = line1 ^ line2;

Logical Explanation to the below c/c++ solution

Can someone please explain to me why is GCD used in the following Solution:
http://www.codechef.com/viewsolution/2849602 (for c)
http://www.codechef.com/viewsolution/2849324 (for c++)
for the question: http://www.codechef.com/ACMKAN13/problems/LINEPROB
A Sniper is standing at the point (x1, y1) on the 2D XY Plane. He shoots from his position towards the point (x2, y2). You may assume that all points are integers.
Consider the 2D grid formed by integer points on the XY Plane. The position of the Sniper and the Target are lattice points in this grid. The bullet shot by the Sniper will follow a straight line trajectory from (x1, y1) to (x2, y2). The bullet goes no further than (x2, y2).
Consider the trajectory of the bullet when the Sniper is standing at (1, 1) and the Target lies at (4, 3).
Notice how the trajectory of the bullet touches 4 cells. A cell is considered touched by the trajectory if and only if the bullet will enter the cell. How many cells are touched by the trajectory of the bullet?

Input
The first line contains a single integer T, the number of test cases. Each of the following T lines contain one test case each. Each test case contains 4 integers x1, y1, x2 and y2. The integers are separated by single space characters.
Output
For each test case, output a single line, containing the number of cells touched by the trajectory of the bullet shot from (x1, y1) to (x2, y2). Remember that a cell is considered touched by the trejectory if and only if the bullet enters the cell - only touching a side is not enough.
Constraints
0 < T < 10100
0 ≤ x1, y1, x2, y2 ≤ 1000000000
Sample Input
3
0 0 3 2
0 0 2 2
0 0 1 0
Sample Output
4
2
0
The logic behind the solution is:
number of blocks the bullet traverses going along the X-axis, plus
number of blocks the bullet traverses going along the Y-axis, minus
number of blocks that got over-counted
Given the points (X1, Y1) and (X2, Y2), let A1 = abs(X1 - X2) and A2 = abs(Y1 - Y2). Then, without loss of generality, we can consider the points (0, 0) and (A1, A2).
Notice that A1 represents the number of blocks the bullet traverses along the X-axis. But, this is also the number of vertical lines touched by the bullet on the interval (0, A1] on the grid. Similarly, A2 represents the number of blocks the bullet traverses along the Y-axis, and also the number of horizontal lines touched by the bullet on the interval (0, A2].
When viewed as the number of touched lines, it is easier to see why you need to subtract some number. The number that needs to be subtracted is the number that corresponds to the touches that occur at the intersections of vertical and horizontal lines. The number of occurrences of that are counted by the GCD of A1 and A2. In particular, it occurs at the points (k * A1/GCD(A1,A2), k * A2/GCD(A1,A2)) for k in 1 .. GCD(A1,A2).
Before explaining the logic behind using gcd I want to clarify that this may not be the exact way the author of this code had thought.
First, I do not see any use of max function in that code.
Explanation:
Let's assume that the
sniper is at (0,0) and the target is at (3,3). It is clear that the trajectory of bullet will touch 2 cells.
Remarks: When sniper and target is on the line x = y or parallel to it, cells touched are one less than that of ordinate (or abscissa). (This is equal to the sum of the x and y component of distance traveled by the bullet and subtracting the GCD of the x and y component of distance traveled by the bullet.)
sniper is at (0,0) and the target is at (0,3) or (3,0). Again it is clear, by seeing the picture, that the trajectory of bullet will touch 0 cells.
Remarks: When sniper and target is on the line x or y or parallel to x or y, cells touched are one 0. (This is equal to the sum of the x and y component of distance traveled by the bullet and subtracting the GCD of the x and y component of distance traveled by the bullet.)
sniper is at (1,1) and the target is at (4,3). It is clear that the trajectory of bullet will touch 4 cells.
Remarks: In all other cases, number of cells are equal to the sum of the x and y component of distance traveled by the bullet and subtracting the GCD of the x and y component of distance traveled by the bullet.

How to compute the center of a polygon in 2D and 3D space

Consider a simple convex polygon in 2D Cartesian space. If given a list of vertex coordinates sorted in a counter-clockwise orientation like this [[x0, y0], ..., [xn, yn]]. How could you compute the center of the polygon (the point inside the polygon that is equidistant to all vertices)?
Also consider a second case where the polygon is placed in 3D Cartesian space and its normal vector is not parallel to any of the Cartesian axes. How could you compute the center, without rotating the polygon?
I can read C/C++, Fortran, MATLAB and Python, however any pseudo-code is also well appreciated.
EDIT
I now realise that my question was not well-posed. I am sorry for that. It appears that what I was looking for is the centroid of the polygon (i.e. the point on which a cardboard cut-out would balance while assuming uniform density and a uniform gravity field).
You definition of center doesn't make sense in general.
To see this just draw three non-aligned points on a plane and compute the one an only circle that passes for all three points. Clearly your center of the triangle must be the center of this circle.
Now draw a fourth point that doesn't lie on the circle and form the four sided polygon. What is the center? There is no point in the plane that is equidistant from all vertices.
Note also that even in case of triangles using the point equidistant from the vertices can give you points outside and far away from the polygon and is also numerically unstable (given any ε>0 and M>0 you can always build a triangle in which a specific movement of a vertex by a distance of less than ε moves the center by a distance greater than M).
Commonly used "centers" that are simple to compute are the average of all vertices, the average of the boundary, the center of mass or even just the center of the axis-aligned bounding box. All of them can however fall outside the polygon if the polygon is not convex, but in your case they may work.
The simplest reasonable one (because it doesn't depends on the coordinate system) is the barycenter of the vertices (code in Python):
xc = sum(x for (x, y) in points) / len(points)
yc = sum(y for (x, y) in points) / len(points)
something bad about it it's that just splitting one side of the polygon gives you a different center (in other words it depends on the vertices and not on the set of points bounded by the polygon). The simplest that depends on the polygon is IMO the barycenter of the boundary:
sx = sy = sL = 0
for i in range(len(points)): # counts from 0 to len(points)-1
x0, y0 = points[i - 1] # in Python points[-1] is last element of points
x1, y1 = points[i]
L = ((x1 - x0)**2 + (y1 - y0)**2) ** 0.5
sx += (x0 + x1)/2 * L
sy += (y0 + y1)/2 * L
sL += L
xc = sx / sL
yc = sy / sL
For both of them the extension to 3d is trivial... just add z using the same formulas.
In the case of a general (not necessarily convex, not necessarily simply connected) polygon a "center" that I found useful but that is not trivial to compute is the (an) inner point that is at a maximum distance from the boundary (in other words a "most inner" point).
In this case I resorted to use a discrete (bitmap) representation and a gaussian distance transform.
First of all for a polygon, the centroid may not always imply equidistant lengths from the centroid to the vertices. In most cases this is probably NOT true. That being said, you can find the centroid simply by finding the mean of your x coordinates and the mean of your y coordinates. In Matlab: centroidx = mean(xcoords) and centroidy = mean(ycoords) are the coordinates of the centroid. See this if you really need more.

calculating perpendicular and angular distance between line segments in 3d

I am working on implementing a clustering algorithm in C++. Specifically, this algorithm: http://www.cs.uiuc.edu/~hanj/pdf/sigmod07_jglee.pdf
At one point in the algorithm (sec 3.2 p4-5), I am to calculate perpendicular and angular distance (d┴ and dθ) between two line segments: p1 to p2, p1 to p3.
It has been a while since I had a math class, I am kinda shaky on what these actually are conceptually and how to calculate them. Can anyone help?
To get the perpendicular distance of a point Q to a line defined by two points P_1 and P_2 calculate this:
d = DOT(Q, CROSS(P_1, P_2) )/MAG(P_2 - P_1)
where DOT is the dot product, CROSS is the vector cross product, and MAG is the magnitude (sqrt(X*X+Y*Y+..))
Using Fig 5. You calculate d_1 the distance from sj to line (si->ei) and d_2 the distance from ej to the same line.
I would establish a coordinate system based on three points, two (P_1, P_2) for a line and the third Q for either the start or the end of the other line segment. The three axis of the coordinate system can be defined as such:
e = UNIT(P_2 - P_1) // axis along the line from P_1 to P_2
k = UNIT( CROSS(e, Q) ) // axis normal to plane defined by P_1, P_2, Q
n = UNIT( CROSS(k, e) ) // axis normal to line towards Q
where UNIT() is function to return a unit vector (with magnitude=1).
Then you can establish all your projected lengths with simple dot products. So considering the line si-ei and the point sj in Fig 5, the lengths are:
(l || 1) = DOT(e, sj-si);
(l |_ 1) = DOT(n, sj-si);
ps = si + e * (l || 1) //projected point
And with the end of the second segment ej, new coordinate axes (e,k,n) need to be computed
(l || 2) = DOT(e, ei-ej);
(l |_ 1) = DOT(n, ej-ei);
pe = ei - e * (l || 1) //projected point
Eventually the angle distance is
(d th) = ATAN( ((l |_ 2)-(L |_ 1))/MAG(pe-ps) )
PS. You might want to post this at Math.SO where you can get better answers.
Look at figure 5 on page 3. It draws out what d┴ and dθ are.
EDIT: The "Lehmer mean" is defined using Lp-space conventions. So in 3 dimensions, you would use p = 3. Let's say that the (Euclidean) distance between the two start points is d1, and between the ends is d2. Then d┴(L1, L2) = (d1^3 + d2^3) / (d1^2 + d2^2).
To find the angle between two vectors, you can use their dot product. The norm (denoted ||x||) is computed like this.