How to calculate turning direction - c#

I have a three lat-lon coordinates that make up two line segment A to B to C. I also found a function that can return north-bearing of a line segment A-B or B-C in -180 to 180 manner. However, I'm having trouble to determine when a car reaches from A to B, should it turn right or left to continue to C.

EDIT: Previous answer was wrong. now this is the correct
public Direction GetDirection(Point a, Point b, Point c)
{
double theta1 = GetAngle(a, b);
double theta2 = GetAngle(b, c);
double delta = NormalizeAngle(theta2 - theta1);
if ( delta == 0 )
return Direction.Straight;
else if ( delta == Math.PI )
return Direction.Backwards;
else if ( delta < Math.PI )
return Direction.Left;
else return Direction.Right;
}
private Double GetAngle(Point p1, Point p2)
{
Double angleFromXAxis = Math.Atan ((p2.Y - p1.Y ) / (p2.X - p1.X ) ); // where y = m * x + K
return p2.X - p1.X < 0 ? m + Math.PI : m ); // The will go to the correct Quadrant
}
private Double NormalizeAngle(Double angle)
{
return angle < 0 ? angle + 2 * Math.PI : angle; //This will make sure angle is [0..2PI]
}

Edited to fix the over 180 issue, also now supports U-Turns.
const int THRESHOLD = 0;
Direction TurnLeftOrRight(Point A, Point B, Point C)
{
int angle = ToAngle(B,C) - ToAngle(A,B);
if((angle > THRESHOLD && angle < 180 - THREASHOLD) || angle < -180 - THREASHOLD)
return Direction.Right;
else if ((angle < 0 - THREASHOLD && angle > -180 + THREASHOLD) || angle > 180 + THREASHOLD)
return Direction.Left;
else if (angle >= 0 - THREASHOLD && angle <= THREASHOLD)
return Direction.Straight
else
return Direction.UTurn;
}
You could also do tolerances between left right and strait just change the first angle > 0 to angle > 45 and the second one to angle < -45

I think your life will be simpler if you use the vector cross product.
Although strictly speaking cross product is defined only for 3D vectors, for 2D vectors p=(px,py) and q=(qx,qy), you can think of their cross product p×q as as pxqy - pyqx. This latter number will be positive if p is clockwise from q, and negative if p is counter-clockwise from q. It will be zero if p and q are parallel - i.e. point in the same or opposite directions.
In your case, you are using (lat,lon). The equivalent in (x,y) coordinates is (-lon, lat), so if you have two vectors (lat1,lon1) and (lat2,lon2), you want to calculate (-lon1, lat1)×(-lon2, lat2), which comes out to lat1*lon2-lon1*lat2.
If this number is zero, you can use the dot product to tell whether the direction is straight or a U-turn.
So your code could look like this, assuming Points and Vectors are written in (lat, lon) form (the code would be a little different if they are in x and y):
public Direction GetTurnDirection(Point A, Point B, Point C)
{
Vector v1 = B - A ;
Vector v2 = C - B ;
double cross = v1.lat*v2.lon - v1.lon*v2.lat ;
if (cross > 0) { return Direction.Left ; }
if (cross < 0) { return Direction.Right ; }
double dot = v1.lat*v2.lat + v1.lon*v2.lon ;
if (dot > 0) { return Direction.Straight ; }
return Direction.UTurn ;
}

If AB is the bearing of B from A, and BC that of C from B, the
turn angle is remainder( BC-AB, 360.0); (assuming degrees). If this
is positive the turn is to the right. In your example remainder( BC-AB, 360.0)
is remainder(271,360) = -89.

Related

Simple Math for controlling characters movement based on camera rotation

I have a GameObject known as myCamera. I am trying to use the Euler Angle of said GameObject to determine in what direction the character should move. An example of the return values expected are located here:
if 0 Degrees x = 1; z = 0
if 90 Degrees x = 0; z = 1
if 180 Degrees x = -1; z = 0;
if 270 Degrees x = 0; z = -1;
The current code looks like this:
// TempVar[0] = tempX; [2] = finalX; [1] = finalZ;
myCameraRotation = myCamera.transform.rotation.eulerAngles;
if (myCameraRotation.y > 180f)
{
tempVar[0] = (int)myCameraRotation.y - 360f;
Debug.Log("Over 180 - New Value: " + tempVar[0]);
}
else
{
tempVar[0] = (int)myCameraRotation.y;
Debug.Log("Under 180 - Passing Value: " + tempVar[0]);
}
if (tempVar[0] > -90 && tempVar[0] <= 90)
{
tempVar[1] = (1 * Mathf.Abs(1 - (tempVar[0] / 90)));
} // Calculate Z
else if (tempVar[0] <= -90 && tempVar[0] > 90)
{
tempVar[1] = ((0 - 1) * Mathf.Abs(1 - (tempVar[0] / 90)));
} // Calculate Z
if (tempVar[0] > 0 && tempVar[0] <= 180)
{
tempVar[2] = (1 * Mathf.Abs(1 - (tempVar[0] / 90) / 2));
} // Calculate X
else if (tempVar[0] <= 0 && tempVar[0] > 180)
{
tempVar[2] = (-1 * Mathf.Abs(1 - (tempVar[0] / 90) / 2));
} // Calculate X
Debug.Log("Z Value : " + tempVar[1]);
Debug.Log("X Value : " + tempVar[2]);
This is outputting some really funky values and I am not really sure what is going on with it, to be honest. As previously stated I am getting the Euler Angle of the myCamera GameObject, not the rotation.
The more I try to work on this the more horrific numbers start popping up, any help from someone who knows some math would be much appreciated.
If the y-rotation is the only rotation of the object, you can use the first column of the object's matrix as the forward direction.
If there are more transforms, you can calculate the direction you want with:
x = Math.Cos(myCameraRotation.y);
z = Math.Sin(myCameraRotation.y);

Finding the intersect location of two Rays

I have two rays. Each ray has a start location vector (Vector3D) and a direction vector (Vector3D), but continue on to infinity. They are both on the same plane, but in a 3D environment. The Rays are interdependent, which means that they might not mirror each other perfectly. From this i need to calculate the location at which these rays intersect in the 3D environment and output it as a vector. In essence: a rangefinder.
How should i go about doing this? Is there a better way of doing it than using the C# Ray structure, is it even possible?
I am a pretty new coder (read: bad) but any answer is appreciated, I would enjoy it if an explanation was included.
Crude image of the rays
Two lines in 3D space only intersect if they are on the same plane. The probability that two random lines in space intersect is really small.
When you want to find out if two rays intersect, if you are looking for an exact intersection point, the chance is that you are not going to be able to calculate it due to floating point errors.
The next best thing is to find the shortest distance between two rays. Then if that distance is smaller than a certain threshold (defined by you) we could say the rays are intersecting.
Finding shortest distance
Here are two rays in 3D space, with the blue vector representing the shortest distance.
Let's take a frame from that gif:
Legend:
p1 is ray1.Position
p2 is ray2.Position
d1 is ray1.Direction
d2 is ray2.Direction
d3 is the cross product d1 x d2
The cross product of the rays directions will be perpendicular to both rays, so it's the shortest direction from ray to ray. If lines are parallel, the cross product will be zero, but for now lets only deal with non-parallel lines.
From the photo, we get the equation:
p1 + a*d1 + c*d3 = p2 + b*d2
Rearranged so the variables are on the left:
a*d1 - b*d2 + c*d3 = p2 - p1
Since each of the know values (d1, d2, d3, p1 and p2) has three components (x,y,z), this is a system of three linear equations with 3 variables.
a*d1.X - b*d2.X + c*d3.X = p2.X - p1.X
a*d1.Y - b*d2.Y + c*d3.Y = p2.Y - p1.Y
a*d1.Z - b*d2.Z + c*d3.Z = p2.Z - p1.Z
Using Gaussian elimination, we get the values of a, b and c.
If both a and b are positive, the position of the intersection will be
Vector3 position = ray1.Position + a*ray1.Direction;
Vector3 direction = c * d3; //direction.Length() is the distance
You could return these values as a Ray for convenience.
If either a or b are negative, this means that the calculated shortest distance would be behind one (or both) of the rays, so a different method should be used to find the shortest distance. This method is the same for lines that are parallel (cross product d1 x d2 is zero).
Now the calculation becomes finding which ray (positive direction) is closest to the position (p1 or p2) of the other ray. For this we use dot product (projection of a vector onto another vector)
Legend:
dP = p2 - p1
Before calculating dot product of d1 dot dP, make sure that d1 (or d2) are normalized (Vector3.Normalize()) - dot product is meant to work on unit vectors.
Now it's a matter of finding which is the shortest distance, based on the projection factor (result of dot) on ray1 (lets call it a2) and projection factor on ray2 (lets call it b2).
If both a2 and b2 are negative (negative side of the ray), then the shortest distance is from position to position. If one is in the negative direction then the other one is the shortest. Else it's the shorter of the two.
Working code:
public Ray FindShortestDistance(Ray ray1, Ray ray2)
{
if (ray1.Position == ray2.Position) // same position - that is the point of intersection
return new Ray(ray1.Position, Vector3.Zero);
var d3 = Vector3.Cross(ray1.Direction, ray2.Direction);
if (d3 != Vector3.Zero) // lines askew (non - parallel)
{
//d3 is a cross product of ray1.Direction (d1) and ray2.Direction(d2)
// that means d3 is perpendicular to both d1 and d2 (since it's not zero - we checked that)
//
//If we would look at our lines from the direction where they seem parallel
// (such projection must always exist for lines that are askew)
// we would see something like this
//
// p1 a*d1
// +----------->x------
// |
// | c*d3
// p2 b*d2 v
// +------->x----
//
//p1 and p2 are positions ray1.Position and ray2.Position - x marks the points of intersection.
// a, b and c are factors we multiply the direction vectors with (d1, d2, d3)
//
//From the illustration we can the shortest distance equation
// p1 + a*d1 + c*d3 = p2 + b*d2
//
//If we rearrange it so we have a b and c on the left:
// a*d1 - b*d2 + c*d3 = p2 - p1
//
//And since all of the know variables (d1, d2, d3, p2 and p1) have 3 coordinates (x,y,z)
// now we have a set of 3 linear equations with 3 variables.
//
// a * d1.X - b * d2.X + c * d3.X = p2.X - p1.X
// a * d1.Y - b * d2.Y + c * d3.Y = p2.Y - p1.Y
// a * d1.Z - b * d2.Z + c * d3.Z = p2.Z - p1.Z
//
//If we use matrices, it would be
// [d1.X -d2.X d3.X ] [ a ] [p2.X - p1.X]
// [d1.Y -d2.Y d3.Y ] * [ a ] = [p2.Y - p1.Y]
// [d1.Z -d2.Z d3.Z ] [ a ] [p2.Z - p1.Z]
//
//Or in short notation
//
// [d1.X -d2.X d3.X | p2.X - p1.X]
// [d1.Y -d2.Y d3.Y | p2.Y - p1.Y]
// [d1.Z -d2.Z d3.Z | p2.Z - p1.Z]
//
//After Gaussian elimination, the last column will contain values a b and c
float[] matrix = new float[12];
matrix[0] = ray1.Direction.X;
matrix[1] = -ray2.Direction.X;
matrix[2] = d3.X;
matrix[3] = ray2.Position.X - ray1.Position.X;
matrix[4] = ray1.Direction.Y;
matrix[5] = -ray2.Direction.Y;
matrix[6] = d3.Y;
matrix[7] = ray2.Position.Y - ray1.Position.Y;
matrix[8] = ray1.Direction.Z;
matrix[9] = -ray2.Direction.Z;
matrix[10] = d3.Z;
matrix[11] = ray2.Position.Z - ray1.Position.Z;
var result = Solve(matrix, 3, 4);
float a = result[3];
float b = result[7];
float c = result[11];
if (a >= 0 && b >= 0) // normal shortest distance (between positive parts of the ray)
{
Vector3 position = ray1.Position + a * ray1.Direction;
Vector3 direction = d3 * c;
return new Ray(position, direction);
}
//else will fall through below:
// the shortest distance was between a negative part of a ray (or both rays)
// this means the shortest distance is between one of the ray positions and another ray
// (or between the two positions)
}
//We're looking for the distance between a point and a ray, so we use dot products now
//Projecting the difference between positions (dP) onto the direction vectors will
// give us the position of the shortest distance ray.
//The magnitude of the shortest distance ray is the the difference between its
// position and the other rays position
ray1.Direction.Normalize(); //needed for dot product - it works with unit vectors
ray2.Direction.Normalize();
Vector3 dP = ray2.Position - ray1.Position;
//shortest distance ray position would be ray1.Position + a2 * ray1.Direction
// or ray2.Position + b2 * ray2.Direction (if b2 < a2)
// or just distance between points if both (a and b) < 0
//if either a or b (but not both) are negative, then the shortest is with the other one
float a2 = Vector3.Dot(ray1.Direction, dP);
float b2 = Vector3.Dot(ray2.Direction, -dP);
if (a2 < 0 && b2 < 0)
return new Ray(ray1.Position, dP);
Vector3 p3a = ray1.Position + a2 * ray1.Direction;
Vector3 d3a = ray2.Position - p3a;
Vector3 p3b = ray1.Position;
Vector3 d3b = ray2.Position + b2 * ray2.Direction - p3b;
if (b2 < 0)
return new Ray(p3a, d3a);
if (a2 < 0)
return new Ray(p3b, d3b);
if (d3a.Length() <= d3b.Length())
return new Ray(p3a, d3a);
return new Ray(p3b, d3b);
}
//Solves a set of linear equations using Gaussian elimination
float[] Solve(float[] matrix, int rows, int cols)
{
for (int i = 0; i < cols - 1; i++)
for (int j = i; j < rows; j++)
if (matrix[i + j * cols] != 0)
{
if (i != j)
for (int k = i; k < cols; k++)
{
float temp = matrix[k + j * cols];
matrix[k + j * cols] = matrix[k + i * cols];
matrix[k + i * cols] = temp;
}
j = i;
for (int v = 0; v < rows; v++)
if (v == j)
continue;
else
{
float factor = matrix[i + v * cols] / matrix[i + j * cols];
matrix[i + v * cols] = 0;
for (int u = i + 1; u < cols; u++)
{
matrix[u + v * cols] -= factor * matrix[u + j * cols];
matrix[u + j * cols] /= matrix[i + j * cols];
}
matrix[i + j * cols] = 1;
}
break;
}
return matrix;
}
It is so simple. you need to apply AAS Triangle formula.
Consider the above figure as triangle,
Ray1 location as A and Ray2 Location as B and you need to find point c. Using dot product find angle between Ray AB and AC (alpha), BA and BC (theta).
Find the distance between location A and B (D). So finally you have alpha, beta and D, i.e (2 angles and 1 side of the triangle.) Apply AAS method and find C location.
https://www.mathsisfun.com/algebra/trig-solving-aas-triangles.html.

Smallest difference between two angles?

I'm trying to calculate the smallest difference between two angles.
This is my current code (a slight variation of something I found online):
float a1 = MathHelper.ToDegrees(Rot);
float a2 = MathHelper.ToDegrees(m_fTargetRot);
float dif = (float)(Math.Abs(a1 - a2);
if (dif > 180)
dif = 360 - dif;
dif = MathHelper.ToRadians(dif);
It works fine except for in cases at the edge of a circle. For example if the current angle is 355 and the target angle is 5 it calculates the difference is -350 rather than 10 since 365 degrees is equal to 5 degrees.
Any ideas on what I can do to make this work?
You basically had it. Just take the dif modulus 360 before checking to see if greater than 180:
float a1 = MathHelper.ToDegrees(Rot);
float a2 = MathHelper.ToDegrees(m_fTargetRot);
float dif = (float)Math.Abs(a1 - a2) % 360;
if (dif > 180)
dif = 360 - dif;
dif = MathHelper.ToRadians(dif);
Edit: #Andrew Russell made a great point in comments to your question and the solution below takes advantage of the MathHelper.WrapAngle method as he suggested:
diff = Math.Abs(MathHelper.WrapAngle(a2 - a1));
You would expand the check for out of bound angles:
if (dif < 0) dif = dif + 360;
if (dif > 180) dif = 360 - dif;
I never like handling the zero-wrapping with case statements. Instead, I use the definition of the dot product to compute the (unsigned) angle between two angles:
vec(a) . vec(b) = ||a|| ||b|| cos(theta)
We're just going to make a and b unit vectors, so ||a|| == ||b|| == 1.
Since vec(x) = [cos(x),sin(x)], we get:
unsigned_angle_theta(a,b) = acos(cos(a)cos(b) + sin(a)sin(b))
(n.b. all angles in radians)
You can normalize the result to be 0 <= theta < 360:
while(theta < 0) { theta += 360; }
If you want to keep the answer in radians (recommended):
const Double TwoPi = 2 * Math.Pi;
while(theta < 0) { theta += TwoPi; }
We can use Euler's formula: exp(iA) = cos A + i sin A.
In the case of the difference between two angles this becomes:
exp(i(A-B))
Using the laws of exponents:
= exp(iA).exp(-iB).
-iB is the conjugate of iB thus:
= exp(iA).exp(conjugate(iB)).
The complex exponent can be calcuated by Taylors series:
taylor_e(P={cplx,_,_}) ->
taylor_e(
#{sum => to_complex(1),
term => 0,
term_value => to_complex(1),
min_terms => 3,
quadrature => 1,
error_term => 1.0e-4,
domain => P}
);
taylor_e(P=#{sum := Sum,
term := Term,
term_value := TermValue0,
min_terms := MinTerms,
domain := Dom,
quadrature := Q,
error_term := ErrorTerm
})
when ((Term =< MinTerms) or (abs(1-Q) > ErrorTerm)) and
(Term < 20) ->
NewTerm = Term+1,
TermValue1 = scalar_divide(multiply(TermValue0,Dom),NewTerm),
PartialSum = add(Sum,TermValue1),
taylor_e(P#{sum := PartialSum,
term := Term+1,
term_value := TermValue1,
quadrature := quadrance(PartialSum)
});
taylor_e(#{sum := Result}) ->
Result.
The angle difference is the the argument (direction) of the resulting complex number and is retrieved by atan2.
Of course you will need some basic complex number routines. This method does not have a discontinuity around 0/360 degrees, and the sign of the result gives the direction of turning. Where this is a difference between some reference direction (say in an autopilot) it only needs calculating once and then storing until a new course is chosen. The deviations from the course would need to be calculated from every sample however.

C# Point in polygon

I'm trying to determine if a point is inside a polygon. the Polygon is defined by an array of Point objects. I can easily figure out if the point is inside the bounded box of the polygon, but I'm not sure how to tell if it's inside the actual polygon or not. If possible, I'd like to only use C# and WinForms. I'd rather not call on OpenGL or something to do this simple task.
Here's the code I have so far:
private void CalculateOuterBounds()
{
//m_aptVertices is a Point[] which holds the vertices of the polygon.
// and X/Y min/max are just ints
Xmin = Xmax = m_aptVertices[0].X;
Ymin = Ymax = m_aptVertices[0].Y;
foreach(Point pt in m_aptVertices)
{
if(Xmin > pt.X)
Xmin = pt.X;
if(Xmax < pt.X)
Xmax = pt.X;
if(Ymin > pt.Y)
Ymin = pt.Y;
if(Ymax < pt.Y)
Ymax = pt.Y;
}
}
public bool Contains(Point pt)
{
bool bContains = true; //obviously wrong at the moment :)
if(pt.X < Xmin || pt.X > Xmax || pt.Y < Ymin || pt.Y > Ymax)
bContains = false;
else
{
//figure out if the point is in the polygon
}
return bContains;
}
I've checked codes here and all have problems.
The best method is:
/// <summary>
/// Determines if the given point is inside the polygon
/// </summary>
/// <param name="polygon">the vertices of polygon</param>
/// <param name="testPoint">the given point</param>
/// <returns>true if the point is inside the polygon; otherwise, false</returns>
public static bool IsPointInPolygon4(PointF[] polygon, PointF testPoint)
{
bool result = false;
int j = polygon.Count() - 1;
for (int i = 0; i < polygon.Count(); i++)
{
if (polygon[i].Y < testPoint.Y && polygon[j].Y >= testPoint.Y || polygon[j].Y < testPoint.Y && polygon[i].Y >= testPoint.Y)
{
if (polygon[i].X + (testPoint.Y - polygon[i].Y) / (polygon[j].Y - polygon[i].Y) * (polygon[j].X - polygon[i].X) < testPoint.X)
{
result = !result;
}
}
j = i;
}
return result;
}
The accepted answer did not work for me in my project. I ended up using the code found here.
public static bool IsInPolygon(Point[] poly, Point p)
{
Point p1, p2;
bool inside = false;
if (poly.Length < 3)
{
return inside;
}
var oldPoint = new Point(
poly[poly.Length - 1].X, poly[poly.Length - 1].Y);
for (int i = 0; i < poly.Length; i++)
{
var newPoint = new Point(poly[i].X, poly[i].Y);
if (newPoint.X > oldPoint.X)
{
p1 = oldPoint;
p2 = newPoint;
}
else
{
p1 = newPoint;
p2 = oldPoint;
}
if ((newPoint.X < p.X) == (p.X <= oldPoint.X)
&& (p.Y - (long) p1.Y)*(p2.X - p1.X)
< (p2.Y - (long) p1.Y)*(p.X - p1.X))
{
inside = !inside;
}
oldPoint = newPoint;
}
return inside;
}
See this it's in c++ and can be done in c# in a same way.
for convex polygon is too easy:
If the polygon is convex then one can
consider the polygon as a "path" from
the first vertex. A point is on the
interior of this polygons if it is
always on the same side of all the
line segments making up the path.
Given a line segment between P0
(x0,y0) and P1 (x1,y1), another point
P (x,y) has the following relationship
to the line segment. Compute (y - y0)
(x1 - x0) - (x - x0) (y1 - y0)
if it is less than 0 then P is to the
right of the line segment, if greater
than 0 it is to the left, if equal to
0 then it lies on the line segment.
Here is its code in c#, I didn't check edge cases.
public static bool IsInPolygon(Point[] poly, Point point)
{
var coef = poly.Skip(1).Select((p, i) =>
(point.Y - poly[i].Y)*(p.X - poly[i].X)
- (point.X - poly[i].X) * (p.Y - poly[i].Y))
.ToList();
if (coef.Any(p => p == 0))
return true;
for (int i = 1; i < coef.Count(); i++)
{
if (coef[i] * coef[i - 1] < 0)
return false;
}
return true;
}
I test it with simple rectangle works fine:
Point[] pts = new Point[] { new Point { X = 1, Y = 1 },
new Point { X = 1, Y = 3 },
new Point { X = 3, Y = 3 },
new Point { X = 3, Y = 1 } };
IsInPolygon(pts, new Point { X = 2, Y = 2 }); ==> true
IsInPolygon(pts, new Point { X = 1, Y = 2 }); ==> true
IsInPolygon(pts, new Point { X = 0, Y = 2 }); ==> false
Explanation on the linq query:
poly.Skip(1) ==> creates a new list started from position 1 of the poly list and then by
(point.Y - poly[i].Y)*(p.X - poly[i].X) - (point.X - poly[i].X) * (p.Y - poly[i].Y) we'll going to calculate the direction (which mentioned in referenced paragraph).
similar example (with another operation):
lst = 2,4,8,12,7,19
lst.Skip(1) ==> 4,8,12,7,19
lst.Skip(1).Select((p,i)=>p-lst[i]) ==> 2,4,4,-5,12
meowNET anwser does not include Polygon vertices in the polygon and points exactly on horizontal edges. If you need an exact "inclusive" algorithm:
public static bool IsInPolygon(this Point point, IEnumerable<Point> polygon)
{
bool result = false;
var a = polygon.Last();
foreach (var b in polygon)
{
if ((b.X == point.X) && (b.Y == point.Y))
return true;
if ((b.Y == a.Y) && (point.Y == a.Y))
{
if ((a.X <= point.X) && (point.X <= b.X))
return true;
if ((b.X <= point.X) && (point.X <= a.X))
return true;
}
if ((b.Y < point.Y) && (a.Y >= point.Y) || (a.Y < point.Y) && (b.Y >= point.Y))
{
if (b.X + (point.Y - b.Y) / (a.Y - b.Y) * (a.X - b.X) <= point.X)
result = !result;
}
a = b;
}
return result;
}
You can use the ray casting algorithm. It is well-described in the wikipedia page for the Point in polygon problem.
It's as simple as counting the number of times a ray from outside to that point touches the polygon boundaries. If it touches an even number of times, the point is outside the polygon. If it touches an odd number of times, the point is inside.
To count the number of times the ray touches, you check intersections between the ray and all of the polygon sides.
My answer is taken from here:Link
I took the C code and converted it to C# and made it work
static bool pnpoly(PointD[] poly, PointD pnt )
{
int i, j;
int nvert = poly.Length;
bool c = false;
for (i = 0, j = nvert - 1; i < nvert; j = i++)
{
if (((poly[i].Y > pnt.Y) != (poly[j].Y > pnt.Y)) &&
(pnt.X < (poly[j].X - poly[i].X) * (pnt.Y - poly[i].Y) / (poly[j].Y - poly[i].Y) + poly[i].X))
c = !c;
}
return c;
}
You can test it with this example:
PointD[] pts = new PointD[] { new PointD { X = 1, Y = 1 },
new PointD { X = 1, Y = 2 },
new PointD { X = 2, Y = 2 },
new PointD { X = 2, Y = 3 },
new PointD { X = 3, Y = 3 },
new PointD { X = 3, Y = 1 }};
List<bool> lst = new List<bool>();
lst.Add(pnpoly(pts, new PointD { X = 2, Y = 2 }));
lst.Add(pnpoly(pts, new PointD { X = 2, Y = 1.9 }));
lst.Add(pnpoly(pts, new PointD { X = 2.5, Y = 2.5 }));
lst.Add(pnpoly(pts, new PointD { X = 1.5, Y = 2.5 }));
lst.Add(pnpoly(pts, new PointD { X = 5, Y = 5 }));
My business critical implementation of PointInPolygon function working on integers (as OP seems to be using) is unit tested for horizontal, vertical and diagonal lines, points on the line are included in the test (function returns true).
This seems to be an old question but all previous examples of tracing have some flaws: do not consider horizontal or vertical polygon lines, polygon boundary line or the order of edges (clockwise, counterclockwise).
The following function passes the tests I came up with (square, rhombus, diagonal cross, total 124 tests) with points on edges, vertices and just inside and outside edge and vertex.
The code does the following for every consecutive pair of polygon coordinates:
Checks if polygon vertex equals the point
Checks if the point is on a horizontal or vertical line
Calculates (as double) and collects intersects with conversion to integer
Sorts intersects so the order of edges is not affecting the algorithm
Checks if the point is on the even intersect (equals - in polygon)
Checks if the number of intersects before point x coordinate is odd - in polygon
Algorithm can be easily adapted for floats and doubles if necessary.
As a side note - I wonder how much software was created in the past nearly 10 years which check for a point in polygon and fail in some cases.
public static bool IsPointInPolygon(Point point, IList<Point> polygon)
{
var intersects = new List<int>();
var a = polygon.Last();
foreach (var b in polygon)
{
if (b.X == point.X && b.Y == point.Y)
{
return true;
}
if (b.X == a.X && point.X == a.X && point.X >= Math.Min(a.Y, b.Y) && point.Y <= Math.Max(a.Y, b.Y))
{
return true;
}
if (b.Y == a.Y && point.Y == a.Y && point.X >= Math.Min(a.X, b.X) && point.X <= Math.Max(a.X, b.X))
{
return true;
}
if ((b.Y < point.Y && a.Y >= point.Y) || (a.Y < point.Y && b.Y >= point.Y))
{
var px = (int)(b.X + 1.0 * (point.Y - b.Y) / (a.Y - b.Y) * (a.X - b.X));
intersects.Add(px);
}
a = b;
}
intersects.Sort();
return intersects.IndexOf(point.X) % 2 == 0 || intersects.Count(x => x < point.X) % 2 == 1;
}
Complete algorithm along with C code is available at http://alienryderflex.com/polygon/
Converting it to c# / winforms would be trivial.
For those using NET Core, Region.IsVisible is available from NET Core 3.0. After adding package System.Drawing.Common,
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
namespace Example
{
class Program
{
static bool IsPointInsidePolygon(Point[] polygon, Point point)
{
var path = new GraphicsPath();
path.AddPolygon(polygon);
var region = new Region(path);
return region.IsVisible(point);
}
static void Main(string[] args)
{
Point vt1 = new Point(0, 0);
Point vt2 = new Point(100, 0);
Point vt3 = new Point(100, 100);
Point vt4 = new Point(0, 100);
Point[] polygon = { vt1, vt2, vt3, vt4 };
Point pt = new Point(50, 50);
bool isPointInsidePolygon = IsPointInsidePolygon(polygon, pt);
Console.WriteLine(isPointInsidePolygon);
}
}
}
Of lesser importance is that, adding System.Drawing.Common package increased size of publish folder by 400 KB. Maybe compared to custom code, this implementation could also be slower (above function timed to be 18 ms on i7-8665u). But still, I prefer this, for one less thing to worry about.
All you really need are 4 lines to implement the winding number method. But first, reference the System.Numerics to use complex library. The code below assumes that you have translate a loop of points (stored in cpyArr) so that your candidate point stands at 0,0.
For each point pair, create a complex number c1 using the first point and c2 using the 2nd point ( the first 2 lines within the loop)
Now here is some complex number theory. Think of c1 and c2 as complex number representation of vectors. To get from vector c1 to vector c2, you can multiply c1 by a complex number Z (c1Z=c2). Z rotates c1 so that it points at c2. Then it also stretches or squishes c1 so that it matces c2. To get such a magical number Z, you divide c2 by c1 (since c1Z=c2, Z=c2/c1). You can look up your high school notes on dividing complex number or use that method provided by Microsoft. After you get that number, all we really care is the phase angle.
To use the winding method, we add up all the phases and we should +/- 2 pi if the point is within the area. Otherwise, the sum should be 0
I added edge cases, 'literally'. If your phase angle is +/- pi, you're right on the edge between the points pair. In that case, I'd say the point is a part of the area and break out of the loop
/// <param name="cpyArr">An array of 2 coordinates (points)</param>
public static bool IsOriginInPolygon(double[,] cpyArr)
{
var sum = 0.0;
var tolerance = 1e-4;
var length = cpyArr.GetLength(0);
for (var i = 0; i < length-1; i++)
{
//convert vertex point pairs to complex numbers for simplified coding
var c2 = new Complex(cpyArr[i+1, 0], cpyArr[i+1, 1]);
var c1 = new Complex(cpyArr[i, 0], cpyArr[i, 1]);
//find the rotation angle from c1 to c2 when viewed from the origin
var phaseDiff = Complex.Divide(c2, c1).Phase;
//add the rotation angle to the sum
sum += phaseDiff;
//immediately exit the loop if the origin is on the edge of polygon or it is one of the vertices of the polygon
if (Math.Abs(Math.Abs(phaseDiff) - Math.PI) < tolerance || c1.Magnitude < tolerance || c2.Magnitude < tolerance)
{
sum = Math.PI * 2;
break;
}
}
return Math.Abs((Math.PI*2 ) - Math.Abs(sum)) < tolerance;
}
I recommend this wonderful 15-page paper by Kai Hormann (University of Erlangen) and Alexander Agathos (University of Athens). It consolidates all the best algorithms and will allow you to detect both winding and ray-casting solutions.
The Point in Polygon Problem for Arbitrary Polygons
The algorithm is interesting to implement, and well worth it. However, it is so complex that it is pointless for me to any portion of it directly. I'll instead stick with saying that if you want THE most efficient and versatile algorithm, I am certain this is it.
The algorithm gets complex because is is very highly optimized, so it will require a lot of reading to understand and implement. However, it combines the benefits of both the ray-cast and winding number algorithms and the result is a single number that provides both answers at once. If the result is greater than zero and odd, then the point is completely contained, but if the result is an even number, then the point is contained in a section of the polygon that folds back on itself.
Good luck.
This is an old question, but I optimized Saeed answer:
public static bool IsInPolygon(this List<Point> poly, Point point)
{
var coef = poly.Skip(1).Select((p, i) =>
(point.y - poly[i].y) * (p.x - poly[i].x)
- (point.x - poly[i].x) * (p.y - poly[i].y));
var coefNum = coef.GetEnumerator();
if (coef.Any(p => p == 0))
return true;
int lastCoef = coefNum.Current,
count = coef.Count();
coefNum.MoveNext();
do
{
if (coefNum.Current - lastCoef < 0)
return false;
lastCoef = coefNum.Current;
}
while (coefNum.MoveNext());
return true;
}
Using IEnumerators and IEnumerables.
If you are drawing Shapes on a Canvas this is a quick and easy Solution.
private void Canvas_MouseMove(object sender, MouseEventArgs e)
{
if (e.OriginalSource is Polygon)
{
//do something
}
}
"Polygon" can be any shape from System.Windows.Shapes.
Here's some modern C# code:
public record Point(double X, double Y);
public record Box(Point LowerLeft, Point UpperRight)
{
public Box(Point[] points)
: this(
new Point(
points.Select(x => x.X).Min(),
points.Select(x => x.Y).Min()),
new Point(
points.Select(x => x.X).Max(),
points.Select(x => x.Y).Max()))
{
}
public bool ContainsPoint(Point point)
{
return point.X >= LowerLeft.X
&& point.X <= UpperRight.X
&& point.Y >= LowerLeft.Y
&& point.Y <= UpperRight.Y;
}
}
public record Polygon(Point[] Points, Box Box)
{
public Polygon(Point[] points)
: this(points, new(points))
{
}
public bool ContainsPoint(Point point)
{
do
{
if (Box.ContainsPoint(point) == false)
{
break;
}
bool result = false;
int j = Points.Length - 1;
for (int i = 0; i < Points.Length; i++)
{
if ((Points[i].Y < point.Y && Points[j].Y >= point.Y)
|| (Points[j].Y < point.Y && Points[i].Y >= point.Y))
{
if (Points[i].X +
((point.Y - Points[i].Y) / (Points[j].Y - Points[i].Y) * (Points[j].X - Points[i].X))
< point.X)
{
result = !result;
}
}
j = i;
}
return result;
}
while (false);
return false;
}
}

Pick X points on a circle which are on screen in C#

I have a few sets of information available to me, currently:
position on screen (Point)
orientation of point (double, say 15 degrees)
distance (length from point)
What I want to do is pick X (less than 6 I think) number of points in an angular spread facing the same orientation as the position with a distance of distance so that they are always on screen.
I can do this, but my implementation does not take into account the fact that some of the points end up off the screen, and all my attempts are quite poor and overly complex.
Does anyone have a good way of doing this?
EDIT: Added graphic to show what im after...
A simple way of "projecting" a point (X1, Y1) along a trajectory/orientation for a distance to X2, Y2 you can use the following;
X2 = X1 + (int)(Math.Sin(orientation) * distance);
Y2 = Y1 + (int)(Math.Cos(orientation) * distance);
Where orientation is a radian double value. You may want to round the result since (int) is a brutal way to convert to int.
If you want to pick a point X/Y that is atleast distance d from point pX, pY then you know that the hypotenuse ( SquareRoot ( (X-pX)^2 + (Y-pY)^2 ) is less than d^2.
Is X/Y less than d from pX/pY?
bool isTooClose = ( ((X - pY)*(X - pY)) + ((Y - pY)*(Y - pY)) < (d*d));
If you know the screen size, then just check the boundaries.
bool isOnScreen = ( (pY > 0) && (pX > 0) && (pX < Screen.Width) && (pY < Screen.Height));
If you want to know that a circle is completely on screen, use the above isOnScreen and subtract/add the radius of the circle to the boundary. For example
bool isCircleOnScreen = ( (pY > r) && (pX > r) && (pX < (Screen.Width - r)) && (pY < (Screen.Height - r)));
To pick a point (X2, Y2) on a circle you can use the formula at the top.
This ended up being my implementation:
private static int[] DetermineTagPointsRange(Point location, double orientation, float distance)
{
const int screenWidth = 1024;
const int screenHieght = 768;
const int sampleCount = 10;
const int totalAngleSpread = 360;
const int angleInc = totalAngleSpread / sampleCount;
var lowestAngle = -1;
var highestAngle = -1;
for (var i = 0; i < sampleCount; i++)
{
var thisAngle = angleInc*i;
var endPointX = location.X + (int)(Math.Sin(ToRad(thisAngle)) * distance);
var endPointY = location.Y - (int)(Math.Cos(ToRad(thisAngle)) * distance);
bool isOnScreen = ((endPointY > 0) && (endPointX > 0) && (endPointX < screenWidth) && (endPointY < screenHieght));
if (isOnScreen)
{
if (thisAngle <= lowestAngle || lowestAngle == -1)
lowestAngle = thisAngle;
if (thisAngle >= highestAngle || highestAngle == -1)
highestAngle = thisAngle;
}
}
return new int[] {lowestAngle, highestAngle};
}
Thanks for the help

Categories

Resources