Pick X points on a circle which are on screen in C# - 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

Related

C# - generating points along a fraction of a circles circumference

I have the below algorithm which generates a number of points along the circumference of a circle for collision events in my program. (This works perfectly as far as I can tell).
bool Collision_True = false;
for (int Angle = 0; Angle <= 359; Angle += 5)
{
int X = (CentreX + (Radius * Math.Cos(Angle));
int Y = (CentreY + (Radius * Math.Sin(Angle));
Point point = new Point(X, Y);
if (Collision_True == false)
{
Collision_True = Player_Collisions(point);
}
}
return Collision_True;
However I want to change this so it only generates points on the bottom third of the circle, I tried changing the values in the for loop as follows:
for (int Angle = 120; Angle <= 240; Angle += 5)
{
...
}
But the points generated are still around the complete circumference of the circle instead of just the bottom third.
Any ideas? Thanks.
You need to convert to radians
for (int angleDegrees = 120; angleDegrees <= 240; angleDegrees += 5)
{
double angleRadians = angleDegrees / 180 * Math.PI;
int X = (CentreX + (Radius * Math.Cos(angleRadians));
int Y = (CentreY + (Radius * Math.Sin(angleRadians));
...

is it possible to get all point of pixel from line c# drawing 2d

I have a line C# code with drawing graphichpath, how to get all value every line pixel. not just point (x1,y1) and (x2,y2) point but I want all pixel from (x1,y1) to (x2,y2)
Here's an algorithm that should give you an estimation of the pixels between your two Points. Note it will not match what is on screen perfectly (which looks antialiased).
public static IEnumerable<Tuple<int,int>> EnumerateLineNoDiagonalSteps(int x0, int y0, int x1, int y1)
{
int dx = Math.Abs(x1 - x0), sx = x0 < x1 ? 1 : -1;
int dy = -Math.Abs(y1 - y0), sy = y0 < y1 ? 1 : -1;
int err = dx + dy, e2;
while(true)
{
yield return Tuple.Create(x0, y0);
if (x0 == x1 && y0 == y1) break;
e2 = 2 * err;
// EITHER horizontal OR vertical step (but not both!)
if (e2 > dy)
{
err += dy;
x0 += sx;
}
else if (e2 < dx)
{ // <--- this "else" makes the difference
err += dx;
y0 += sy;
}
}
}
Replace the Tuple with Point.
For more information, see:
http://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm
or
http://en.wikipedia.org/wiki/Xiaolin_Wu%27s_line_algorithm

Moving rectangle is not moving according to coordinates given

I have a picturebox with a picture as a background (infact a map), and on it I am spawning rectangles. These rectangles are supposed to move by given points. The rectangle is moving with assigned speed, and after reaching (or getting close) to one of the points, it starts moving to the next one. My problem however is, the rectangle doesnt move directly to the given point, it is just proceeding to get close to only one of the coordinates, so there are situations where the Y coordinate of the rectangle, and the Y coordinate of the point are the same, but the rectangle is like 60 pixels of and wont move.
Below I am adding a picture as an example of the movement. Blue is expected route, red is the actual one. I have checked the coordinates like a hundred times, they are correct, the rectangle is just moving elsewhere. Note: this only happens at some of the points.
Here is the code I am using to count the movement of the rectangle relative to axis X and Y.
public void Move_calculate(Graphics g)
{
if (points[passed].X == 0 || points[passed].Y == 0) // this happens when the rectangle reaches it final point - it stays where it is (working fine)
{
Redraw(g);
return;
}
speed = randomNumbers.Next(7, 13);
if (points[passed].X > x_coordinate && points[passed].Y > y_coordinate)
{
Bx = points[passed].X;
By = points[passed].Y;
distanceForAlfaX = Bx - x_coordinate; // x_coordinate is the x coordinate of the rectangle
distanceForAlfaY = By - y_coordinate;
if (distanceForAlfaX <= 20 || distanceForAlfaY <= 20) speed = 5; // slowing down when approaching the point
if (distanceForAlfaX + distanceForAlfaY <= 15) passed += 1;
alpha = (distanceForAlfaY / distanceForAlfaX); // tangent alpha
x_change = (int)(speed * (Math.Cos(alpha))); // get distance moved relative to axis X
y_change = (int)Math.Sqrt(((speed * speed) + (x_change * x_change))); // again distance for axis Y, using Pythagoras theorem
x_coordinate += x_change;
y_coordinate += y_change;
}
else if (points[passed].X > x_coordinate && points[passed].Y < y_coordinate)
{
Bx = points[passed].X;
By = points[passed].Y;
distanceForAlfaX = Bx - x_coordinate;
distanceForAlfaY = y_coordinate - By;
if (distanceForAlfaX <= 20 || distanceForAlfaY <= 20) speed = 5;
if (distanceForAlfaX + distanceForAlfaY <= 15) passed += 1;
alpha = (distanceForAlfaY / distanceForAlfaX);
x_change = (int)(speed * (Math.Cos(alpha)));
y_change = (int)Math.Sqrt(((speed * speed) + (x_change * x_change)));
x_coordinate += x_change;
y_coordinate -= y_change;
}
else if (points[passed].X < x_coordinate && points[passed].Y > y_coordinate)
{
Bx = points[passed].X;
By = points[passed].Y;
distanceForAlfaX = x_coordinate - Bx;
distanceForAlfaY = By - y_coordinate;
if (distanceForAlfaX <= 20 || distanceForAlfaY <= 20) speed = 5;
if (distanceForAlfaX+distanceForAlfaY <= 15) passed += 1;
alpha = (distanceForAlfaY / distanceForAlfaX);
x_change = (int)(speed * (Math.Cos(alpha)));
y_change = (int)Math.Sqrt(((speed * speed) + (x_change * x_change)));
x_coordinate -= x_change;
y_coordinate += y_change;
}
else if (points[passed].X < x_coordinate && points[passed].Y < y_coordinate)
{
Bx = points[passed].X;
By = points[passed].Y;
distanceForAlfaX = x_coordinate - Bx;
distanceForAlfaY = y_coordinate - By;
if (distanceForAlfaX <= 20 || distanceForAlfaY <= 20) speed = 5;
if (distanceForAlfaX + distanceForAlfaY <= 15) passed += 1;
alpha = (distanceForAlfaY / distanceForAlfaX);
x_change = (int)(speed * (Math.Cos(alpha)));
y_change = (int)Math.Sqrt(((speed * speed) + (x_change * x_change)));
x_coordinate -= x_change;
y_coordinate -= y_change;
}
else
{
MessageBox.Show("Something went wrong"); // just notify me that it isnt working again..
}
Pen p = new Pen(Color.Turquoise, 2);
r = new Rectangle(x_coordinate, y_coordinate, 5, 5); // redraw the rectangle
g.DrawRectangle(p, r);
p.Dispose();
}
I have no idea why this is happening, could anyone help with this?
P.S.
There is absolutely no need for the movement to be smooth, the positions of rectangles will be updated once per two seconds using a Timer. For now it is temporarily set to a button.
EDIT:
Here is the foreach code. The labels are just the coordinates shown next to the PictureBox
foreach (aircraft acft in aircrafts) // aircraft is an array aircrafts[]
{
label2.Text = "xp" + acft.points[acft.passed].X;
label3.Text = "yp" + acft.points[acft.passed].Y;
label4.Text = acft.passed.ToString();
label5.Text = "y" + acft.y_coordinate.ToString();
//MessageBox.Show(acft.points[0].X.ToString());
acft.Move_calculate(e.Graphics);
spawn = string.Empty;
}
EDIT2: All variables in aircraft class
public string callsign;
public int speed;
public double heading;
public bool moving = false;
public Point[] points;
public double alpha;
public int x_change;
public int y_change;
public int x_coordinate;
public int y_coordinate;
public int Bx;
public int By;
public double distanceForAlfaX;
public double distanceForAlfaY;
public int passed = 0;
public Rectangle r;
I guess, there's a math mistake in
y_change = (int)Math.Sqrt(((speed * speed) + (x_change * x_change)));
Moreover...
// again distance for axis Y, using Pythagoras theorem
Let Mr.Pythagoras be, I would rather use the same as for X axis
y_change = (int)(speed * (Math.Sin(alpha)));
Have you checked your coordinate system? (https://web.archive.org/web/20140710074441/http://bobpowell.net/coordinatesystems.aspx)
Sorry, wrong link for your issue. Try debugging using Control.PointToClient to make sure all coordinates are expressed in client space. (https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.control.pointtoclient)
You could try:
Point cPoint = this.PointToClient(new Point(x_coordinate, y_coordinate));
Size cSize = new Size(5,5);
r = new Rectangle(cPoint, cSize); // redraw the rectangle
g.DrawRectangle(p, r);
Could you post the data types of your variables to clarify your code? You might be losing precision somewhere, especially if you're using integer division.
(Note for DJ KRAZE, a -= b; in C# can also mean a = a - b; Context matters.)

Render points of Both a filled and unfilled Ellipse to Array in C#

Does anyone know of any code to render an Ellipse to an array in C#? I had a look about, I couldn't find anything that answered my problem.
Given the following array:
bool[,] pixels = new bool[100, 100];
I'm looking for functions to render both a hollow and filled ellipse within a rectangular area. e.g:
public void Ellipse(bool[,] pixels, Rectangle area)
{
// fill pixels[x,y] = true here for the ellipse within area.
}
public void FillEllipse(bool[,] pixels, Rectangle area)
{
// fill pixels[x,y] = true here for the ellipse within area.
}
Ellipse(pixels, new Rectangle(20, 20, 60, 60));
FillEllipse(pixels, new Rectangle(40, 40, 20, 20));
Any help would be greatly appreciated.
Something like this should do the trick
public class EllipseDrawer
{
private static PointF GetEllipsePointFromX(float x, float a, float b)
{
//(x/a)^2 + (y/b)^2 = 1
//(y/b)^2 = 1 - (x/a)^2
//y/b = -sqrt(1 - (x/a)^2) --Neg root for upper portion of the plane
//y = b*-sqrt(1 - (x/a)^2)
return new PointF(x, b * -(float)Math.Sqrt(1 - (x * x / a / a)));
}
public static void Ellipse(bool[,] pixels, Rectangle area)
{
DrawEllipse(pixels, area, false);
}
public static void FillEllipse(bool[,] pixels, Rectangle area)
{
DrawEllipse(pixels, area, true);
}
private static void DrawEllipse(bool[,] pixels, Rectangle area, bool fill)
{
// Get the size of the matrix
var matrixWidth = pixels.GetLength(0);
var matrixHeight = pixels.GetLength(1);
var offsetY = area.Top;
var offsetX = area.Left;
// Figure out how big the ellipse is
var ellipseWidth = (float)area.Width;
var ellipseHeight = (float)area.Height;
// Figure out the radiuses of the ellipses
var radiusX = ellipseWidth / 2;
var radiusY = ellipseHeight / 2;
//Keep track of the previous y position
var prevY = 0;
var firstRun = true;
// Loop through the points in the matrix
for (var x = 0; x <= radiusX; ++x)
{
var xPos = x + offsetX;
var rxPos = (int)ellipseWidth - x - 1 + offsetX;
if (xPos < 0 || rxPos < xPos || xPos >= matrixWidth)
{
continue;
}
var pointOnEllipseBoundCorrespondingToXMatrixPosition = GetEllipsePointFromX(x - radiusX, radiusX, radiusY);
var y = (int) Math.Floor(pointOnEllipseBoundCorrespondingToXMatrixPosition.Y + (int)radiusY);
var yPos = y + offsetY;
var ryPos = (int)ellipseHeight - y - 1 + offsetY;
if (yPos >= 0)
{
if (xPos > -1 && xPos < matrixWidth && yPos > -1 && yPos < matrixHeight)
{
pixels[xPos, yPos] = true;
}
if(xPos > -1 && xPos < matrixWidth && ryPos > -1 && ryPos < matrixHeight)
{
pixels[xPos, ryPos] = true;
}
if (rxPos > -1 && rxPos < matrixWidth)
{
if (yPos > -1 && yPos < matrixHeight)
{
pixels[rxPos, yPos] = true;
}
if (ryPos > -1 && ryPos < matrixHeight)
{
pixels[rxPos, ryPos] = true;
}
}
}
//While there's a >1 jump in y, fill in the gap (assumes that this is not the first time we've tracked y, x != 0)
for (var j = prevY - 1; !firstRun && j > y - 1 && y > 0; --j)
{
var jPos = j + offsetY;
var rjPos = (int)ellipseHeight - j - 1 + offsetY;
if(jPos == rjPos - 1)
{
continue;
}
if(jPos > -1 && jPos < matrixHeight)
{
pixels[xPos, jPos] = true;
}
if(rjPos > -1 && rjPos < matrixHeight)
{
pixels[xPos, rjPos] = true;
}
if (rxPos > -1 && rxPos < matrixWidth)
{
if(jPos > -1 && jPos < matrixHeight)
{
pixels[rxPos, jPos] = true;
}
if(rjPos > -1 && rjPos < matrixHeight)
{
pixels[rxPos, rjPos] = true;
}
}
}
firstRun = false;
prevY = y;
var countTarget = radiusY - y;
for (var count = 0; fill && count < countTarget; ++count)
{
++yPos;
--ryPos;
// Set all four points in the matrix we just learned about
// also, make the indication that for the rest of this row, we need to fill the body of the ellipse
if(yPos > -1 && yPos < matrixHeight)
{
pixels[xPos, yPos] = true;
}
if(ryPos > -1 && ryPos < matrixHeight)
{
pixels[xPos, ryPos] = true;
}
if (rxPos > -1 && rxPos < matrixWidth)
{
if(yPos > -1 && yPos < matrixHeight)
{
pixels[rxPos, yPos] = true;
}
if(ryPos > -1 && ryPos < matrixHeight)
{
pixels[rxPos, ryPos] = true;
}
}
}
}
}
}
Although there already seems to be a perfectly valid answer with source code and all to this question, I just want to point out that the WriteableBitmapEx project also contains a lot of efficient source code for drawing and filling different polygon types (such as ellipses) in so-called WriteableBitmap objects.
This code can easily be adapted to the general scenario where a 2D-array (or 1D representation of a 2D-array) should be rendered in different ways.
For the ellipse case, pay special attention to the DrawEllipse... methods in the WriteableBitmapShapeExtensions.cs file and FillEllipse... methods in the WriteableBitmapFillExtensions.cs file, everything located in the trunk/Source/WriteableBitmapEx sub-folder.
This more applies to all languages in general, and I'm not sure why you're looking for things like this in particular rather than using a pre-existing graphics library (homework?), but for drawing an ellipse, I would suggest using the midpoint line drawing algorithm which can be adapted to an ellipse (also to a circle):
http://en.wikipedia.org/wiki/Midpoint_circle_algorithm
I'm not sure I fully agree that it's a generalisation of Bresenham's algorithm (certainly we were taught that Bresenham's and the Midpoint algorithm are different but proved to produce identical results), but that page should give you a start on it. See the link to the paper near the bottom for an algorithm specific to ellipses.
As for filling the ellipse, I'd say your best bet is to take a scanline approach - look at each row in turn, work out which pixels the lines on the left and right are at, and then fill every pixel inbetween.
The simplest thing to do would do is iterate over each element of your matrix, and check whether some ellipse equation evaluates to true
taken from http://en.wikipedia.org/wiki/Ellipse
What I would start with is something resembling
bool[,] pixels = new bool[100, 100];
double a = 30;
double b = 20;
for (int i = 0; i < 100; i++)
for (int j = 0; j < 100; j++ )
{
double x = i-50;
double y = j-50;
pixels[i, j] = (x / a) * (x / a) + (y / b) * (y / b) > 1;
}
and if your elipse is in reverse, than just change the > to <
For a hollow one, you can check whether the difference between (x / a) * (x / a) + (y / b) * (y / b) and 1 is within a certain threshold. If you just change the inequality to an equation, it will probably miss some pixels.
Now, I haven't actually tested this fully, so I don't know if the equation being applied correctly, but I just want to illustrate the concept.

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;
}
}

Categories

Resources