Determining the intersection of a triangle and a plane - language-agnostic

I have a single triangle and a plane (in 3 dimensional space), How would I calculate the line segment where the two cross, if there is no crossing then I need to detect this case.
The end result I'm looking for is two 3 dimensional vectors, which define the start and end points of the line segment.
To help you out a little, I have already calculated the intersection ray between the plane of the face, and the plane, I simply need to find the endpoints to clip that ray into a line segment.
For those who like reading things in code:
Face face; //a face, defined by 3 points
Plane plane; //a plane, defined by a normal vector and a distance
Ray intersection; //a ray, defined by a point and a direction, initialised to the intersection of the face plane and the face
Segment s = CalculateSegment(face, plane, intersection); //this method needs defining

Here's some suggested pseudo code. Simple version first, more robust version later (just to help separate the principle from the neuances).
Simple version:
// Assume the plane is given as the equation dot(N,X) + d = 0, where N is a (not
// neccessarily normalized) plane normal, and d is a scalar. Any way the plane is given -
// DistFromPlane should just let the input vector into the plane equation.
vector3d planeN;
float planeD;
float DistFromPlane( vector3d P)
{
// if N is not normalized this is *not* really the distance,
// but the computations work just the same.
return dot(planeN,P) + planeD;
}
bool GetSegmentPlaneIntersection( vector3d P1, vector3d P2, vector3d& outP)
{
float d1 = DistFromPlane(P1),
d2 = DistFromPlane(P2);
if (d1*d2 > 0) // points on the same side of plane
return false;
float t = d1 / (d1 - d2); // 'time' of intersection point on the segment
outP = P1 + t * (P2 - P1);
return true;
}
void TrianglePlaneIntersection(vector3d triA, vector3d triB, vector3d triC,
vector3dArray& outSegTips)
{
vector3d IntersectionPoint;
if( GetSegmentPlaneIntersection( triA, triB, IntersectionPoint))
outSegTips.Add(IntersectionPoint);
if( GetSegmentPlaneIntersection( triB, triC, IntersectionPoint))
outSegTips.Add(IntersectionPoint);
if( GetSegmentPlaneIntersection( triC, triA, IntersectionPoint))
outSegTips.Add(IntersectionPoint);
}
Now adding some robustness:
[Edit: Added explicit consideration for the case of a single vertex on the plane]
vector3d planeN;
float planeD;
float DistFromPlane( vector3d P)
{
return dot(planeN,P) + planeD;
}
void GetSegmentPlaneIntersection( vector3d P1, vector3d P2, vector3dArray& outSegTips)
{
float d1 = DistFromPlane(P1),
d2 = DistFromPlane(P2);
bool bP1OnPlane = (abs(d1) < eps),
bP2OnPlane = (abs(d2) < eps);
if (bP1OnPlane)
outSegTips.Add(P1);
if (bP2OnPlane)
outSegTips.Add(P2);
if (bP1OnPlane && bP2OnPlane)
return;
if (d1*d2 > eps) // points on the same side of plane
return;
float t = d1 / (d1 - d2); // 'time' of intersection point on the segment
outSegTips.Add( P1 + t * (P2 - P1) );
}
void TrianglePlaneIntersection(vector3d triA, vector3d triB, vector3d triC,
vector3dArray& outSegTips)
{
GetSegmentPlaneIntersection( triA, triB, outSegTips));
GetSegmentPlaneIntersection( triB, triC, outSegTips));
GetSegmentPlaneIntersection( triC, triA, outSegTips));
RemoveDuplicates(outSegTips); // not listed here - obvious functionality
}
Hopefully that gives an idea, but there are still quite a few potential optimizations. If, for example, you're calculating these intersections for every triangle in a large mesh, you might calculate and cache the DistanceFromPlane once per vertex, and just retrieve it for every edge the vertex participates in. There can be more advanced caching too, depending on your scenario and data representation.

Plug the 3 points into the plane equation (defined by the 4 parameters you listed a,b,c,d) and determine which pairs are on opposite sides of the plane.
Given the plane equation:
Ax + By + Cz + D = 0
where A,B,C is the normal (unit length) and D is the distance to origin IIRC, you plug in points (x,y,z) and see if this result is positive or negative. It will be zero for points on the plane, and the sign will tell you which side a point is on when the result is not 0. So pick pairs of points on opposite sides (there will be at most 2) and compute the intersection of those 2 segments with the plane using a standard ray/plane intersection formula which escapes me right now. Those will be the 2 points that form the segment you seek.
EDIT
Come to think of it, the values you get from plugging the points into the plane equation should be useful for interpolating between pairs of points to get the intersection of segments with the plane.
Len Fn = Axn + Byn + C*zn + D be the result of plugging in point n.
Then suppose F1 = -4 and F2 = 8. So points P1 and P2 are on opposite sides of the plane. We will also have P = P1*2/3 + P2*1/3 is the point of intersection of the segment from P1 to P2 with the plane. Generalizing this into a proper formula is left as an exorcise.

Find the intersection of each line segment bounding the triangle with the plane. Merge identical points, then
if 0 intersections exist, there is no intersection
if 1 intersection exists (i.e. you found two but they were identical to within tolerance) you have a point of the triangle just touching the plane
if 2 points then the line segment between them is the intersection
next step, search SO for line-segment to plane intersection algorithms (or just use the one provided by your framework)...

It depends a bit on what libraries you have. I have created my own geometry library which can calculate the intersection of a line with a plane. In this case calculate the three points of intersection of the three edges of the triangle and then calculate which of them lie between vertices. This could be 0 (no intersection), or 2 which is the case you want. (There are special cases where the two points are coincident - a point of the triangle).

Related

Overlapping line segments in 2D space

I need to find whether two lines overlap each other. I have the intersection code which returns 0, if two lines are parallel. But then I need to know if these two parallel lines overlap.
Edit:
A C B D
-----------------------------------------------
Line 1: A-B
Line 2: C-D
I need to find if Line 1 overlaps Line 2, but both lines can have a slope > 0.
You can compare to find if there is no over lap. you will have less comparisons in this way thus very efficient. Do following comparisons
D < A
B < C
If either case is true the lines are not overlapping. otherwise there must an overlap.
You will make least number of comparisons to determine if they are not overlapping. Otherwise there will be more comparisons to make.
Since you know they're both parallel, then just check whether line segment CD contains either of the endpoints of the first line (point A and point B).
For two co-linear line segments that are not necessarily axis-aligned:
Sort the vertices in clockwise order around the origin.
The lines overlap if the ordered vertices alternate between the two segments, e.g. Line1.Point1, Line2.Point1, Line1.Point2, Line2.Point2.
It is sufficient to calculate the areas of triangles ACB and CBD. If the area is 0, then the points are collinear, and if both areas are zero then the lines are overlapping.
You can calculate the area of a triangle ABC by this formula:
2*Area(ABC)= (bx – ax)(cy – ay) – (cx – ax)(by – ay);
Line equation is direction of line in infinite, by finding slope or intercept you wont be able do anything with them(even though horizontal line doesn't have slope), i suggest use point on line.
so AB is your line [(x,y),(x,y)] and C is on of the point (x,y) and then you need to check if point on the line.
Check Point On a Line
We are given two line segments
AB = line segment from (Ax,Ay) to (Bx,By)
CD = line segment from (Cx,Cy) to (Dx,Dy)
with the same slope.
Order the endpoints E1 < E2 < E3 < E4 such that Ei,x ≤ Ei+1,x and Ei,y ≤ Ei+1,y if Ei,x = Ei+1,x
If E1 and E2 are from different segments, the overlap is the segment from E2 to E3.
There are some degenerate cases:
A < B = C < D
A < C = D < B
A < B = C = D
A = B = C = D
These result in a single point of intersection. I'm not sure if any of those can occur in your system, but if so you'll have to decide whether or not you consider that "overlap" and add special case checks.
The characteristic of two segments of being in the same lines is called collinearity and can be tested calculating the area of the 2 triangles formed by one segment endpoints and, respectively, the endpoints of the other segment. If the area is zero or close to zero below a threshold the segments are collinear.
public static bool AreSegmentsCollinear(Segment a, Segment b, double epsilon)
{
return IsPointCollinear(a, b.Left, epsilon) && IsPointCollinear(a, b.Right, epsilon);
}
public static bool IsPointCollinear(Segment a, Vector2D p, double epsilon)
{
return Math.Abs(GetSignedTriangleArea2(a, p)) <= epsilon;
}
/// <returns>The squared signed area of the triangle formed with endpoints
/// of segment a and point p</returns>
public static double GetSignedTriangleArea2(Segment a, Vector2D p)
{
return (p - a.Left) ^ (a.Right - a.Left);
}
/// <summary>Cross product of vectors in 2D space. NB: it returns a
/// magnitude (scalar), not a vector</summary>
/// <returns>The area of the parallelogram formed with u, v as the edges</returns>
public static Double operator ^(Vector2D u, Vector2D v)
{
return u.X * v.Y - u.Y * v.X;
}
Just to be clear since there seems to be some confusion in the answers, the question being asked is as follows. Given two 2D line segments A and B how do I determine if both of the following are true:
A and B are colinear.
A and B intersect.
Note that there are tolerances involved in both questions i.e. how close to parallel and how near each other do A and B need to be to be considered colinear? How much do they need to overlap to be considered overlapping?
I think to handle such tolerances robustly the best algorithm is to treat the line segments as thin rectangles, where the thickness of the rectangles is a tolerance parameter t1. Let t2 be another tolerance parameter on slopes that are considered parallel. Then the algorithm becomes
If the slope of A and the slope of B are not within t2 of each other return false. To handle vertical lines cleanly, slope can be represented as a unit vector and the test can be on whether the Euclidean distance between the two unit vectors is smaller than t2.
Represent A and B as (non-axis-aligned) rectangles R1 and R2. Where R1 encloses A in the obvious way, i.e. it is length(A) + t1 units long and is t1 units wide with A centered inside it, and R2 analogously encloses B.
Determine if R1 and R2 intersect each other. This can be done relatively efficiently by treating each rectangle as the union of two triangles and testing for triangle-triangle intersections across all combinations of A triangles and B triangles. If there is an intersection return true; otherwise return false.
With lines l1 and l2 given in the following form [x1, y1, x2, y2] the following python code will give the intersection for collinear line segments with any slope.
intersection = line_intersect(l1, l2)
def line_intersect(l1, l2):
"""Find the intersection of two line segments"""
x1, y1, x2, y2 = l1
x3, y3, x4, y4 = l2
x_inter = component_intersect(x1, x2, x3, x4)
y_inter = component_intersect(y1, y2, y3, y4)
return math.sqrt(x_inter**2 + y_inter**2)
def component_intersect(c1, c2, c3, c4):
"""Calculate intersection in one dimension/component"""
# find left endpoints
cl1 = min(c1, c2)
cl2 = min(c3, c4)
# find right endpoints
cr1 = max(c1, c2)
cr2 = max(c3, c4)
# find endpoints of intersection
c_inter_l = max(cl1, cl2)
c_inter_r = min(cr1, cr2)
# calcuate intersection
c_inter = c_inter_r - c_inter_l
c_inter = max(c_inter, 0)
return c_inter

How can I determine whether it's faster to face an object rotating clockwise or counter clockwise?

I've been trying this to no avail for some days now, but basically I have some creatures and the player on the screen. What I want to happen is for the enemies to turn to face the player at a variable speed, rather than 'lock' into position and face the player immediately.
What I am trying to do is work out whether it is faster for a given enemy to rotate clockwise or counter clockwise to face the player, but it's proving to be beyond my capabilities with trigonometry.
Example:
x in these figures represents the 'shorter' path and the direction I want to rotate in each situation.
What is the simplest way to work out either 'clockwise' or 'counter-clockwise' in this situation, using any of the following:
The direction the enemy is facing.
The angle between the enemy to the player, and player to the enemy.
There is no need to calculate angles or use trigonometric functions here, assuming you have a direction vector.
var pos_x, pos_y, dir_x, dir_y, target_x, target_y;
if ((pos_x - target_x) * dir_y > (pos_y - target_y) * dir_x) {
// Target lies clockwise
} else {
// Target lies anticlockwise
}
This simply draws an imaginary line through the object in the direction it's facing, and figures out which side of that line the target is on. This is basic linear algebra, so you should not need to use sin() or cos() etc. anywhere in this function, unless you need to calculate the direction vector from the angle.
This also uses a right-handed coordinate system, it will be backwards if you are using a left-handed coordinate system -- the formulas will be the same, but "clockwise" and "anticlockwise" will be swapped.
Deeper explanation: The function computes the outer product of the forward vector (dir_x, dir_y) and the vector to the target, (target_x - pos_x, target_y - pos_y). The resulting outer product is a pseudoscalar which is positive or negative, depending on whether the target is clockwise or anticlockwise.
Crash course on vectors
A vector is a magnitude and direction, e.g., 3 km north, or 6 centimeters down. You can represent a vector using cartesian coordinates (x, y), or you can represent it using polar coordinates (r,θ). Both representations give you the same vectors, but they use different numbers and different formulas. In general, you should stick with cartesian coordinates instead of polar coordinates. If you're writing a game, polar coordinates suck royally — they litter your code with sin() and cos() everywhere.
The code has three vectors in it:
The vector (pos_x, pos_y) is the position of the object, relative to the origin.
The vector (target_x, target_y) is the position of the target, relative to the origin.
The vector (dir_x, dir_y) is the direction that the object is facing.
const CLOCKWISE:int = 0;
const COUNTER_CLOCKWISE:int = 1;
const PI2:Number = Math.PI * 2
function determineSmallestAngle(from:Sprite, to:Sprite):int
{
var a1:Number = Math.atan2(to.y - from.y, to.x - from.x);
var a2:Number = from.rotation * Math.PI / 180;
a2 -= Math.floor(a2 / PI2) * PI2;
if(a2 > Math.PI) a2 -= PI2;
a2 -= a1;
if (a2 > Math.PI) a2 -= PI2;
if (a2 < -1 * Math.PI) a2 += PI2;
if (a2 > 0) return CLOCKWISE;
return COUNTER_CLOCKWISE;
}

Create a function to generate random points in a parallelogram

I hope someone can help me here, I have been asked to write some code for an Lua script for a game. Firstly i am not an Lua Scripter and I am defiantly no mathematician.
What i need to do is generate random points within a parallelogram, so over time the entire parallelogram becomes filled. I have played with the scripting and had some success with the parallelogram (rectangle) positioned on a straight up and down or at 90 degrees. My problem comes when the parallelogram is rotated.
As you can see in the image, things are made even worse by the coordinates originating at the centre of the map area, and the parallelogram can be positioned anywhere within the map area. The parallelogram itself is defined by 3 pairs of coordinates, start_X and Start_Y, Height_X and Height_Y and finally Width_X and Width_Y. The random points generated need to be within the bounds of these coordinates regardless of position or orientation.
Map coordinates and example parallelogram
An example of coordinates are...
Start_X = 122.226
Start_Y = -523.541
Height_X = 144.113
Height_Y = -536.169
Width_X = 128.089
Width_Y = -513.825
In my script testing i have eliminated the decimals down to .5 as any smaller seems to have no effect on the final outcome. Also in real terms the start width and height could be in any orientation when in final use.
Is there anyone out there with the patients to explain what i need to do to get this working, my maths is pretty basic, so please be gentle.
Thanks for reading and in anticipation of a reply.
Ian
In Pseudocode
a= random number with 0<=a<=1
b= random number with 0<=b<=1
x= Start_X + a*(Width_X-Start_X) + b*(Height_X-Start_X)
y= Start_Y + a*(Width_Y-Start_Y) + b*(Height_Y-Start_Y)
this should make a random point at coordinates x,y within the parallelogram
The idea is that each point inside the parallelogram can be specified by saying how far you go from Start in the direction of the first edge (a) and how far you go in the direction of the second edge (b).
For example, if you have a=0, and b=0, then you do not move at all and are still at Start.
If you have a=1, and b=0, then you move to Width.
If you have a=1, and b=1, then you move to the opposite corner.
You can use something like "texture coordinates", which are in the range [0,1], to generate X,Y for a point inside your parallelogram. Then, you could generate random numbers (u,v) from range [0,1] and get a random point you want.
To explain this better, here is a picture:
The base is formed by vectors v1 and v2. The four points A,B,C,D represent the corners of the parallelogram. You can see the "texture coordinates" (which I will call u,v) of the points in parentheses, for example A is (0,0), D is (1,1). Every point inside the parallelogram will have coordinates within (0,0) and (1,1), for example the center of the parallelogram has coordinates (0.5,0.5).
To get the vectors v1,v2, you need to do vector subtraction: v1 = B - A, v2 = C - A. When you generate random coordinates u,v for a random point r, you can get back the X,Y using this vector formula: r = A + u*v1 + v*v2.
In Lua, you can do this as follows:
-- let's say that you have A,B,C,D defined as the four corners as {x=...,y=...}
-- (actually, you do not need D, as it is D=v1+v2)
-- returns the vector a+b
function add(a,b)
return {x = a.x + b.x, y = a.y + b.y} end
end
-- returns the vector a-b
function sub(a,b)
return {x = a.x - b.x, y = a.y - b.y} end
end
-- returns the vector v1*u + v2*v
function combine(v1,u,v2,v)
return {x = v1.x*u + v2.x*v, y = v1.y*u + v2.y*v}
end
-- returns a random point in parallelogram defined by 2 vectors and start
function randomPoint(s,v1,v2)
local u,v = math.random(), math.random() -- these are in range [0,1]
return add(s, combine(v1,u,v2,v))
end
v1 = sub(B,A) -- your basis vectors v1, v2
v2 = sub(C,A)
r = randomPoint(A,v1,v2) -- this will be in your parallelogram defined by A,B,C
Note that this will not work with your current layout - start, width, height. How do you want to handle rotation with these parameters?

Bezier Curve always the same length

I'm working on a game in HTML5 canvas.
I want is draw an S-shaped cubic bezier curve between two points, but I'm looking for a way to calculate the coordinates of the control points so that the curve itself is always the same length no matter how close those points are, until it reaches the point where the curve becomes a straight line.
This is solvable numerically. I assume you have a cubic bezier with 4 control points.
at each step you have the first (P0) and last (P3) points, and you want to calculate P1 and P2 such that the total length is constant.
Adding this constraint removes one degree of freedom so we have 1 left (started with 4, determined the end points (-2) and the constant length is another -1). So you need to decide about that.
The bezier curve is a polynomial defined between 0 and 1, you need to integrate on the square root of the sum of elements (2d?). for a cubic bezier, this means a sqrt of a 6 degree polynomial, which wolfram doesn't know how to solve. But if you have all your other control points known (or known up to a dependency on some other constraint) you can have a save table of precalculated values for that constraint.
Is it really necessary that the curve is a bezier curve? Fitting two circular arcs whose total length is constant is much easier. And you will always get an S-shape.
Fitting of two circular arcs:
Let D be the euclidean distance between the endpoints. Let C be the constant length that we want. I got the following expression for b (drawn in the image):
b = sqrt(D*sin(C/4)/4 - (D^2)/16)
I haven't checked if it is correct so if someone gets something different, leave a comment.
EDIT: You should consider the negative solution too that I obtain when solving the equation and check which one is correct.
b = -sqrt(D*sin(C/4)/4 - (D^2)/16)
Here's a working example in SVG that's close to correct:
http://phrogz.net/svg/constant-length-bezier.xhtml
I experimentally determined that when the endpoints are on top of one another the handles should be
desiredLength × cos(30°)
away from the handles; and (of course) when the end points are at their greatest distance the handles should be on top of one another. Plotting all ideal points looks sort of like an ellipse:
The blue line is the actual ideal equation, while the red line above is an ellipse approximating the ideal. Using the equation for the ellipse (as my example above does) allows the line to get about 9% too long in the middle.
Here's the relevant JavaScript code:
// M is the MoveTo command in SVG (the first point on the path)
// C is the CurveTo command in SVG:
// C.x is the end point of the path
// C.x1 is the first control point
// C.x2 is the second control point
function makeFixedLengthSCurve(path,length){
var dx = C.x - M.x, dy = C.y - M.y;
var len = Math.sqrt(dx*dx+dy*dy);
var angle = Math.atan2(dy,dx);
if (len >= length){
C.x = M.x + 100 * Math.cos(angle);
C.y = M.y + 100 * Math.sin(angle);
C.x1 = M.x; C.y1 = M.y;
C.x2 = C.x; C.y2 = C.y;
}else{
// Ellipse of major axis length and minor axis length*cos(30°)
var a = length, b = length*Math.cos(30*Math.PI/180);
var handleDistance = Math.sqrt( b*b * ( 1 - len*len / (a*a) ) );
C.x1 = M.x + handleDistance * Math.sin(angle);
C.y1 = M.y - handleDistance * Math.cos(angle);
C.x2 = C.x - handleDistance * Math.sin(angle);
C.y2 = C.y + handleDistance * Math.cos(angle);
}
}

How to draw paths specified in terms of straight and curved motion

I have information on paths I would like to draw. The information consists of a sequence of straight sections and curves. For straight sections, I have only the length. For curves, I have the radius, direction and angle. Basically, I have a turtle that can move straight or move in a circular arc from the current position (after which moving straight will be in a different direction).
I would like some way to draw these paths with the following conditions:
Minimal (preferably no) trigonometry.
Ability to center on a canvas and scale to fit any arbitrary size.
From what I can tell, GDI+ gives me number 2, Cairo gives me number 1, but neither one makes it particularly easy to get both. I'm open to suggestions of how to make GDI+ or Cairo (preferably pycairo) work, and I'm also open to any other library (preferably C# or Python).
I'm even open to abstract mathematical explanations of how this would be done that I can convert into code.
For 2D motion, the state is [x, y, a]. Where the angle a is relative to the positive x-axis. Assuming initial state of [0, 0, 0]. 2 routines are needed to update the state according to each type of motion. Each path yields a new state, so the coordinates can be used to configure the canvas accordingly. The routines should be something like:
//by the definition of the state
State followLine(State s, double d) {
State s = new State();
s.x = s0.x + d * cos(s0.a);
s.y = s0.y + d * sin(s0.a);
s.a = s0.a;
return s;
}
State followCircle(State s0, double radius, double arcAngle, boolean clockwise) {
State s1 = new State(s0);
//look at the end point on the arc
if(clockwise) {
s1.a = s0.a - arcAngle / 2;
} else {
s1.a = s0.a + arcAngle / 2;
}
//move to the end point of the arc
State s = followLine(s1, 2 * radius * sin(arcAngle/ 2));
//fix new angle
if(clockwise) {
s.a = s0.a - arcAngle;
} else {
s.a = s0.a + arcAngle;
}
return s;
}