Determining if a Line intersects with a rotated rectangle on a canvas - c#

I have managed to find code that determines if a Line intersects with a rectangle. My problem is when the Rectangle is rotated. I have looked hi and low to find code that will give me the coordinates of the corners of the rectangle after the transform is performed with no luck. My rectangle when it is rotated is rotated around CenterX and CenterY of 0,0.
I'd appreciate any code that you may have that does this.
Thanks!
More information:
I am working on a program that I want to be able to select one or more rectangles on a canvas by drawing a line. The rectangles can be rotated.
I have tried the following code. The second function works properly for non rotated rectangles but not for rotated rectangles.:
public bool AdjustedIntersects(FrameworkElement elem, Line line)
{
double x = Canvas.GetLeft(elem);
double y = Canvas.GetTop(elem);
double X = x * Math.Cos(((RotateTransform)elem.RenderTransform).Angle)
- y * Math.Sin(((RotateTransform)elem.RenderTransform).Angle);
double Y = x * Math.Sin(((RotateTransform)elem.RenderTransform).Angle)
+ y * Math.Cos(((RotateTransform)elem.RenderTransform).Angle);
x = Canvas.GetLeft(elem) + elem.Width;
y = Canvas.GetTop(elem) + elem.Height;
double X2 = x * Math.Cos(((RotateTransform)elem.RenderTransform).Angle)
- y * Math.Sin(((RotateTransform)elem.RenderTransform).Angle);
double Y2 = x * Math.Sin(((RotateTransform)elem.RenderTransform).Angle)
+ y * Math.Cos(((RotateTransform)elem.RenderTransform).Angle);
return SegmentIntersectRectangle(X, Y,X2,Y2,
line.X1, line.Y1, line.X2, line.Y2);
}
public bool SegmentIntersectRectangle(
double rectangleMinX,
double rectangleMinY,
double rectangleMaxX,
double rectangleMaxY,
double p1X,
double p1Y,
double p2X,
double p2Y)
{
// Find min and max X for the segment
double minX = p1X;
double maxX = p2X;
if (p1X > p2X)
{
minX = p2X;
maxX = p1X;
}
// Find the intersection of the segment's and rectangle's x-projections
if (maxX > rectangleMaxX)
{
maxX = rectangleMaxX;
}
if (minX < rectangleMinX)
{
minX = rectangleMinX;
}
if (minX > maxX) // If their projections do not intersect return false
{
return false;
}
// Find corresponding min and max Y for min and max X we found before
double minY = p1Y;
double maxY = p2Y;
double dx = p2X - p1X;
if (Math.Abs(dx) > 0.0000001)
{
double a = (p2Y - p1Y) / dx;
double b = p1Y - a * p1X;
minY = a * minX + b;
maxY = a * maxX + b;
}
if (minY > maxY)
{
double tmp = maxY;
maxY = minY;
minY = tmp;
}
// Find the intersection of the segment's and rectangle's y-projections
if (maxY > rectangleMaxY)
{
maxY = rectangleMaxY;
}
if (minY < rectangleMinY)
{
minY = rectangleMinY;
}
if (minY > maxY) // If Y-projections do not intersect return false
{
return false;
}
return true;
}

A polygon intersects with a line if one of lines of the poligon overlap with the line. Then try this:
Find four conners of the rectangle by the above way.
Check if one of lines make from sequence of conners overlap with the line. I have a ideal for it:
private bool IsStraightLineOverlap(System.Windows.Shapes.Line line1, Line line2)
{
var line1Min_X = Math.Min(line1.X1, line1.X2);
var line1Max_X = Math.Max(line1.X1, line1.X2);
var line1Min_Y = Math.Min(line1.Y1, line1.Y2);
var line1Max_Y = Math.Max(line1.Y1, line1.Y2);
var line2Min_X = Math.Min(line2.X1, line2.X2);
var line2Max_X = Math.Max(line2.X1, line2.X2);
var line2Min_Y = Math.Min(line2.Y1, line2.Y2);
var line2Max_Y = Math.Max(line2.Y1, line2.Y2);
var isOverlap_X = (line1Min_X <= line2Max_X && line1Max_X >= line2Min_X);
var isOverlap_Y = (line1Min_Y <= line2Max_Y && line1Max_Y >= line2Min_Y);
return isOverlap_X && isOverlap_Y;
}

In our game suite I have to find rotated points for various purposes.
Here's an extension method:
public static class PointExtension
{
public static Point GetRotatedPoint(this Point point, Point centerPoint, double angleInDegrees)
{
double angleInRadians = angleInDegrees * (Math.PI / 180.0);
double cosTheta = Math.Cos(angleInRadians);
double sinTheta = Math.Sin(angleInRadians);
return new Point
{
X =
(int)
(cosTheta * (point.X - centerPoint.X) -
sinTheta * (point.Y - centerPoint.Y) + centerPoint.X),
Y =
(int)
(sinTheta * (point.X - centerPoint.X) +
cosTheta * (point.Y - centerPoint.Y) + centerPoint.Y)
};
}
}
I also have to work out the cells on a grid that a line passes through.
To do this I use a bresenham line algorithm.
You can google this and find various implementations.
Here's mine:
public static IEnumerable<Point> GetOrderedPointsOnLine(int x0, int y0, int x1, int y1)
{
bool steep = Math.Abs(y1 - y0) > Math.Abs(x1 - x0);
if (steep)
{
Swap<int>(ref x0, ref y0);
Swap<int>(ref x1, ref y1);
}
int dx = Math.Abs(x1 - x0);
int dy = Math.Abs(y1 - y0);
int error = (dx / 2);
int ystep = (y0 < y1 ? 1 : -1);
int xstep = (x0 < x1 ? 1 : -1);
int y = y0;
for (int x = x0; x != (x1 + xstep); x += xstep)
{
yield return new Point((steep ? y : x), (steep ? x : y));
error = error - dy;
if (error < 0)
{
y += ystep;
error += dx;
}
}
yield break;
}
The outline of a rectangle is of course 4 lines.
There is an edge case where both an edge and the line can be a variation of 4 degrees and intersection exactly picks cells don't match.
To obviate that you could use a bresenham variation which picks both cells the line partially passes through.
Or you can build two geometries and see if you get anything overlaps when you use the wpf library to detect intersection.
I'm not sure what happens if you apply a transform to a geometry and then use geometry.fillcontainswithdetail
https://learn.microsoft.com/en-us/dotnet/api/system.windows.media.geometry.fillcontainswithdetail?redirectedfrom=MSDN&view=net-5.0#overloads
There's also combinedgeometry to consider.
Pick the right options and it'll only give you what overlaps between 2 geometries.
I use neither because in game has to be very fast.
For pre game calculations I render offscreen and examine the bytes of the image I get. This is the fastest simplest way I've found to see which cells (px) an irregular shape occupies.
And another way you could approach this.

Solution 1: break the problem down into something that is easier to solve:
Pick any edge on the rectangle, represented by points P1 and P2.
Translate both the points in your rectangle and the points that define your line by -P1, so that the P1 point is now at the origin.
Calculate the angle of your edge i.e. Math.Atan2(deltaY, deltaX).
Rotate the points for both the rectangle and line in the opposite direction, so that you now have an axis-aligned rectangle.
Do your line/rectangle hit test between these new primitives, using the algorithm you already have for axis-aligned rectangles.
If you need the actual point of intersection in proper world space coordinates then rotate it forward by the angle and translate by +P1.
Solution 2: test the line against each line that forms the rectangle, your problem is now 4 line-to-line intersection tests.

Disclaimer: this is not c#. I only know javascript but the math should be the same.
The way I attack this is by creating an array for my shape that I use to store each vertex. I then use those points to determine where the objects boundaries are. I am assuming that translate and rotate work the same in c# and the objects coordinate are always axis-aligned.
When I draw my rectangle I draw it with the x (left) as -width/2 and y (top) as -height/2. This is because I am going to use translate to position it where I want it and also allow it to rotate from the center.
ctx.save();
ctx.translate(this.x, this.y)
ctx.rotate(this.rotation);
ctx.fillStyle = this.color;
ctx.fillRect(-this.width/2, -this.height/2, this.width, this.height)
ctx.restore();
I don't know if c# uses save and restore but it would be the same as just translating and rotating it back after i.e.
ctx.translate(this.x, this.y);
ctx.rotate(this.rotation);
ctx.fillStyle = this.color;
ctx.fillRect(-this.width/2, -this.height/2, this.width, this.height);
ctx.rotate(-this.rotation);
ctx.translate(-this.x, -this.y)
Also you'll notice in the snippet code that I created an array to store my x and y coordinates of each vertex
this.vertices = [];
for (let i = 0; i < 4; i++) {
//initially set to (0, 0) and updated in updateVertices()
this.vertices.push({ x: 0, y: 0 });
}
This is part of my rectangle object by the array could be global also.
The part that really matters is the math associated to updating the position of the vertices. In this snippet I use a function called updateVertices(). In this function I need to first calculate the sin and cos based on the current rotation.
let cos = Math.cos(this.rotation); //passing in radians in JS
let sin = Math.sin(this.rotation); //passing in radians in JS
Once I have that I just update the array of vertices that I created
//update Top Left Corner
this.vertices[0].x =
(this.x - this.centerX) * cos -
(this.y - this.centerY) * sin +
(this.centerX - this.width / 2);
this.vertices[0].y =
(this.x - this.centerX) * sin +
(this.y - this.centerY) * cos +
(this.centerY - this.height / 2);
Do that with all four corners. The math is slightly different for each vertex.
That's it. You have an array with all 4 vertices and can use them how you want. In this example I iterate over them passing two (adjacent) at time to my intersectLines() function to see if my vector lines intersects. Since I am testing 4 edges I use a loop to test all four sides against my vector, I do this in my passToIntersectFunction() function.
In this snippet you can use the mouse to move the vector around and see how the intersect points move.
let canvas = document.getElementById("canvas");
let ctx = canvas.getContext("2d");
canvas.width = 400;
canvas.height = 400;
let ptX, ptY;
let intersectPoints = [];
let mouse = {
x: null,
y: null
};
canvas.addEventListener("mousemove", (e) => {
mouse.x = e.x - canvas.getBoundingClientRect().x;
mouse.y = e.y - canvas.getBoundingClientRect().y;
});
class Square {
constructor() {
this.x = 100;
this.y = 100;
this.width = 50;
this.height = 50;
this.centerX = this.x + this.width / 2;
this.centerY = this.y + this.height / 2;
this.color = "red";
this.angle = 0;
this.rotation = (this.angle * Math.PI) / 180; //convert to rads
//used to store all four vertices
this.vertices = [];
for (let i = 0; i < 4; i++) {
//initially set to (0, 0) and updated in updateVertices()
this.vertices.push({ x: 0, y: 0 });
}
}
draw() {
this.angle += 0.5;
this.rotation = (this.angle * Math.PI) / 180;
ctx.translate(this.x, this.y);
ctx.rotate(this.rotation);
ctx.fillStyle = this.color;
ctx.fillRect(-this.width / 2, -this.height / 2, this.width, this.height);
ctx.rotate(-this.rotation);
ctx.translate(-this.x, -this.y);
}
drawIntersectPoint() {
ctx.fillStyle = "black";
ctx.beginPath();
ctx.arc(ptX, ptY, 3, 0, Math.PI * 2);
ctx.fill();
}
drawVertices() {
ctx.fillStyle = "blue";
ctx.beginPath();
for (let i = 0; i < this.vertices.length; i++) {
ctx.arc(this.vertices[i].x, this.vertices[i].y, 3, 0, Math.PI * 2);
ctx.fill();
ctx.closePath();
}
}
updateVertices() {
let cos = Math.cos(this.rotation);
let sin = Math.sin(this.rotation);
//update Top Left Corner
this.vertices[0].x =
(this.x - this.centerX) * cos -
(this.y - this.centerY) * sin +
(this.centerX - this.width / 2);
this.vertices[0].y =
(this.x - this.centerX) * sin +
(this.y - this.centerY) * cos +
(this.centerY - this.height / 2);
//updates Top Right Corner
this.vertices[1].x =
(this.x + this.width - this.centerX) * cos -
(this.y - this.centerY) * sin +
(this.centerX - this.width / 2);
this.vertices[1].y =
(this.x + this.width - this.centerX) * sin +
(this.y - this.centerY) * cos +
(this.centerY - this.height / 2);
//updates Bottom Right Corner
this.vertices[2].x =
(this.x + this.width - this.centerX) * cos -
(this.y + this.height - this.centerY) * sin +
(this.centerX - this.width / 2);
this.vertices[2].y =
(this.x + this.width - this.centerX) * sin +
(this.y + this.height - this.centerY) * cos +
(this.centerY - this.height / 2);
//updates Bottom Left Corner
this.vertices[3].x =
(this.x - this.centerX) * cos -
(this.y + this.height - this.centerY) * sin +
(this.centerX - this.width / 2);
this.vertices[3].y =
(this.x - this.centerX) * sin +
(this.y + this.height - this.centerY) * cos +
(this.centerY - this.height / 2);
}
}
let square = new Square();
class Vector {
constructor() {
this.x1 = 200;
this.y1 = 100;
this.x2 = mouse.x;
this.y2 = mouse.y;
}
draw() {
ctx.strokeStyle = "black";
ctx.beginPath();
ctx.moveTo(this.x1, this.y1);
ctx.lineTo(this.x2, this.y2);
ctx.stroke();
}
updateVector() {
this.x2 = mouse.x;
this.y2 = mouse.y;
this.draw();
}
}
let vector = new Vector();
function intersectLines(coord1, coord2, vector) {
//this if statement just keeps the array from constantly growing
if (intersectPoints.length > 2) {
intersectPoints.shift();
}
let x1 = coord1.x;
let x2 = coord2.x;
let y1 = coord1.y;
let y2 = coord2.y;
let x3 = vector.x1;
let x4 = vector.x2;
let y3 = vector.y1;
let y4 = vector.y2;
let d = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);
if (d == 0) {
return;
}
let t = ((x1 - x3) * (y3 - y4) - (y1 - y3) * (x3 - x4)) / d;
let u = ((x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3)) / d;
if (t > 0 && t < 1 && u > 0) {
intersectPoints.push({ x: x1 + t * (x2 - x1), y: y1 + t * (y2 - y1) });
}
return;
}
function passToIntersectFunction() {
for (let i = 0; i < square.vertices.length; i++) {
intersectLines(
square.vertices[i],
square.vertices[i + 1] ?? square.vertices[0],
vector
);
}
}
function drawIntersectPoints() {
for (let i = 0; i < intersectPoints.length; i++) {
ctx.fillStyle = "black";
ctx.beginPath();
ctx.arc(intersectPoints[i].x, intersectPoints[i].y, 3, 0, Math.PI * 2);
ctx.fill();
}
}
function animate() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
square.draw();
square.updateVertices();
square.drawIntersectPoint();
square.drawVertices();
vector.updateVector();
drawIntersectPoints();
passToIntersectFunction();
requestAnimationFrame(animate);
}
animate();
<canvas id="canvas"></canvas>
Sorry its not c# but maybe it can help.

Related

How to decrease and increase values as a curve

I am trying to do a digging tool for my game, I have x and y coordinates of point A and B, what I want to do is create a curve between these points, nothing graphical I just need loop through the coordinates (float x, float y).
I am not good at explaining so here is a visual example;
The first image is what's happen if I just use a for loop to decrease the y value until middle and then increase it from the middle to end.
//Very specific code for my example
//I wrote it just for this example so I am not sure if it works
float y;
float x;
public void Example(float startX, float endX, float startY, float endY, float depth)
{
y = startY;
x = startX;
float changeAmountOfY = depth / (endX - startX);
for (int i = (int)startX; i < (startX + endX) / 2; i++)
{
x++;
y -= changeAmountOfY;
}
for (int i = (int)(startX + endX) / 2; i < endX; i++)
{
x++;
y += changeAmountOfY;
}
}
public void ChangeCoordinates()
{
Example(100f, 200f, 100f, 100f, 50f);
}
The second image is what I need.
I am developing the game on unity and I am using Vector2 for the coordinates but it is not important.
Pure C# or even C++ is welcome.
It is also fine if someone can just explain the math behind what I am trying to do.
Maybe this can help:
// Calculate radius
int radius = (B.X - A.X) / 2;
// Calculate middle
int middle_x = A.X + radius;
int middle_y = A.Y;
// or
int middle_y = (A.Y + B.Y) / 2;
// Coordinates for a semicircle
// 0 to 180 degree
for (int i = 0; i <= 180; i++)
{
double x_coordinate = middle_x + radius * Math.Cos(i * Math.PI / 180);
// Opened to bottom
double y_coordinate = middle_y + radius * Math.Sin(i * Math.PI / 180);
// or opened to top
double y_coordinate = middle_y - radius * Math.Sin(i * Math.PI / 180);
}
Take a look at unit circle.

Rotate bunch of points thourgh an origin [duplicate]

This question already has answers here:
Rotating a point about another point (2D)
(6 answers)
Closed 9 years ago.
I have list of points containing x and y locations of a page. I want to apply rotation on all these points relative to any pivot point of page (currently lets assume its center).
var points = new List<Point>();
points.Add(1,1);
points.Add(15,18);
points.Add(25,2);
points.Add(160,175);
points.Add(150,97);
const int pageHeight = 300;
const int pageWidth = 400;
var pivotPoint = new Point(200, 150); //Center
var angle = 45; // its in degree.
// Apply rotation.
Do I need some formula here?
public static Point Rotate(Point point, Point pivot, double angleDegree)
{
double angle = angleDegree * Math.PI / 180;
double cos = Math.Cos(angle);
double sin = Math.Sin(angle);
int dx = point.X - pivot.X;
int dy = point.Y - pivot.Y;
double x = cos * dx - sin * dy + pivot.X;
double y = sin * dx + cos * dy + pivot.X;
Point rotated = new Point((int)Math.Round(x), (int)Math.Round(y));
return rotated;
}
static void Main(string[] args)
{
Console.WriteLine(Rotate(new Point(1, 1), new Point(0, 0), 45));
}
If you have a large number of points to rotate, you might want to precompute the rotation matrix…
[C -S U]
[S C V]
[0 0 1]
…where…
C = cos(θ)
S = sin(θ)
U = (1 - C) * pivot.x + S * pivot.y
V = (1 - C) * pivot.y - S * pivot.x
You then rotate each point as follows:
rotated.x = C * original.x - S * original.y + U;
rotated.x = S * original.x + C * original.y + V;
The above formula is the result of combining three transforms…
rotated = translate(pivot) * rotate(θ) * translate(-pivot) * original
…where…
translate([x y]) = [1 0 x]
[0 1 y]
[0 0 1]
rotate(θ) = [cos(θ) -sin(θ) 0]
[sin(θ) cos(θ) 0]
[ 0 0 1]
if u rotate a point(x,y) around point (x1,y1) by an angle some a then you need a formula...
x2 = cos(a) * (x-x1) - sin(a) * (y-y1) + x1
y2 = sin(a) * (x-x1) + cos(a) * (y-y1) + y1
Point newRotatedPoint = new Point(x2,y2)

Projection of a point on line defined by 2 points

I have been trying to figure this out for sometime now..
The problem to solve..
Say I have 3 Points..
P1 ---------- P2, and P3 can be anywhere around P1 and P2
What is the formula to calculate so that P3 is interpolated onto the line between P1 and P2?
I need a formula that calculates new X,Y coordinates for P3 that falls on the line between P1 and P2..
My code as of so far..
public Point lerp(Point P0, Point P1, Point P)
{
double y1 = P0.Y + (P1.Y - P0.Y) * ((P.X - P0.X) / (P1.X - P0.X));
double x1 = P.X;
double y2 = P.Y;
double x2 = P0.X + (P1.X - P0.X) * ((P.Y - P0.Y) / (P1.Y - P0.Y));
return new Point((x1 + x2) / 2, (y1 + y2) / 2);
}
And my reference..
http://en.wikipedia.org/wiki/Linear_interpolation
The above code gets it close, but its slightly off...
Here is the converted javascript code from Corey Ogburn
public Point _pointOnLine(Point pt1, Point pt2, Point pt)
{
bool isValid = false;
var r = new Point(0, 0);
if (pt1.Y == pt2.Y && pt1.X == pt2.X) { pt1.Y -= 0.00001; }
var U = ((pt.Y - pt1.Y) * (pt2.Y - pt1.Y)) + ((pt.X - pt1.X) * (pt2.X - pt1.X));
var Udenom = Math.Pow(pt2.Y - pt1.Y, 2) + Math.Pow(pt2.X - pt1.X, 2);
U /= Udenom;
r.Y = pt1.Y + (U * (pt2.Y - pt1.Y));
r.X = pt1.X + (U * (pt2.X - pt1.X));
double minx, maxx, miny, maxy;
minx = Math.Min(pt1.X, pt2.X);
maxx = Math.Max(pt1.X, pt2.X);
miny = Math.Min(pt1.Y, pt2.Y);
maxy = Math.Max(pt1.Y, pt2.Y);
isValid = (r.X >= minx && r.X <= maxx) && (r.Y >= miny && r.Y <= maxy);
return isValid ? r : new Point();
}
Here's some javascript code we've used here at work (a GIS company) to figure out the closest point on a line the mouse is next to in a situation where a user wants to split the line by adding a vertex to it. Should be easy to move over to C#:
function _pointOnLine(line1, line2, pt) {
var isValid = false;
var r = new Microsoft.Maps.Location(0, 0);
if (line1.latitude == line2.latitude && line1.longitude == line2.longitude) line1.latitude -= 0.00001;
var U = ((pt.latitude - line1.latitude) * (line2.latitude - line1.latitude)) + ((pt.longitude - line1.longitude) * (line2.longitude - line1.longitude));
var Udenom = Math.pow(line2.latitude - line1.latitude, 2) + Math.pow(line2.longitude - line1.longitude, 2);
U /= Udenom;
r.latitude = line1.latitude + (U * (line2.latitude - line1.latitude));
r.longitude = line1.longitude + (U * (line2.longitude - line1.longitude));
var minx, maxx, miny, maxy;
minx = Math.min(line1.latitude, line2.latitude);
maxx = Math.max(line1.latitude, line2.latitude);
miny = Math.min(line1.longitude, line2.longitude);
maxy = Math.max(line1.longitude, line2.longitude);
isValid = (r.latitude >= minx && r.latitude <= maxx) && (r.longitude >= miny && r.longitude <= maxy);
return isValid ? r : null;
}
line1 is a point with a latitude and longitude to represent one of the endpoints of the line, equivalent to your P1. line2 is the other endpoint: P2. pt is your P3. This will return the point on the line that P3 is perpendicular through. If P3 is past either end of the line, this will return null which means that one of the two end points is the closest point to P3.
For clarity:
The problem is that you Point has integer values for X and Y and therefore you are doing integer division. Try to cast your values into float or double, do the calculations and then return them back to the integers.
Note that when you are doing this:
(P1.Y - P0.Y) * ((P.X - P0.X) / (P1.X - P0.X))
you are actualy loosing the precision since the result of 5/2 is 2, not 2.5 but when your values are real numbers then 5.0/2.0 is indeed 2.5.
You should try this:
double y1 = P0.Y + (double)(P1.Y - P0.Y) * ((double)(P.X - P0.X) / (double)(P1.X - P0.X));
double x1 = P.X; //these two are implicit casts
double y2 = P.Y;
double x2 = P0.X + (double)(P1.X - P0.X) * ((double)(P.Y - P0.Y) / (double)(P1.Y - P0.Y));
return new Point((x1 + x2) / 2.0, (y1 + y2) / 2.0); //put 2.0 for any case even though x1+x2 is `double`
Also, then you are converting from double to int, decimal part of the number is automatically cut off so for instance 3.87 will become 3. Than your last line should be more precise if you could use this:
return new Point((x1 + x2) / 2.0 + 0.5, (y1 + y2) / 2.0 + 0.5);
which will effectively round double values to the closer integer value.
EDIT:
But if you just want to find the point p3 on the line between the two points, than it is easier to use this approach:
public Point lerp(Point P0, Point P1)
{
double x = ((double)P0.X + P1.X)/2.0;
double y = (double)P0.Y + (double)(P1.Y - P0.Y) * ((double)(x - P0.X) / (double)(P1.X - P0.X));
return new Point(x + 0.5, y + 0.5);
}
This is an old question and I found the Corey Ogburn solution quite useful. But I thought it might be helpful for others to post a less "map" version of the javascript code - which I used in canvas drawing.
export const pointOnLine = (p0, p1, q) => {
// p0 and p1 define the line segment
// q is the reference point (aka mouse)
// returns point on the line closest to px
if (p0.x == p1.x && p0.y == p1.y) p0.x -= 0.00001;
const Unumer = ((q.x - p0.x) * (p1.x - p0.x)) + ((q.y - p0.y) * (p1.y - p0.y));
const Udenom = Math.pow(p1.x - p0.x, 2) + Math.pow(p1.y - p0.y, 2);
const U = Unumer / Udenom;
const r = {
x: p0.x + (U * (p1.x - p0.x)),
y: p0.y + (U * (p1.y - p0.y))
}
const minx = Math.min(p0.x, p1.x);
const maxx = Math.max(p0.x, p1.x);
const miny = Math.min(p0.y, p1.y);
const maxy = Math.max(p0.y, p1.y);
const isValid = (r.x >= minx && r.x <= maxx) && (r.y >= miny && r.y <= maxy);
return isValid ? r : null;
}

Algorithm for finding a point in an irregular polygon

Imagagine I have a polygon like the following:
I am looking for a C# algorithm with whom I can find a point (could be the middlepoint or also a random point) inside any polygon.
For finding the center of mass I used the following algorithm:
private Point3d GetPolyLineCentroid(DBObject pObject, double pImageWidth, double pImageHeight)
{
Point2d[] pointArray = GetPointArrayOfRoomPolygon(pObject);
double centroidX = 0.0;
double centroidY = 0.0;
double signedArea = 0.0;
double x0 = 0.0; // Current vertex X
double y0 = 0.0; // Current vertex Y
double x1 = 0.0; // Next vertex X
double y1 = 0.0; // Next vertex Y
double a = 0.0; // Partial signed area
int i = 0;
for (i = 0; i < pointArray.Length - 1; ++i)
{
x0 = pointArray[i].X;
y0 = pointArray[i].Y;
x1 = pointArray[i + 1].X;
y1 = pointArray[i + 1].Y;
a = x0 * y1 - x1 * y0;
signedArea += a;
centroidX += (x0 + x1) * a;
centroidY += (y0 + y1) * a;
}
x0 = pointArray[i].X;
y0 = pointArray[i].Y;
x1 = pointArray[0].X;
y1 = pointArray[0].Y;
a = x0 * y1 - x1 * y0;
signedArea += a;
centroidX += (x0 + x1) * a;
centroidY += (y0 + y1) * a;
signedArea *= 0.5;
centroidX /= (6.0 * signedArea);
centroidY /= (6.0 * signedArea);
Point3d centroid = new Point3d(centroidX, centroidY, 0);
return centroid;
}
This works good with polygones like this:
But if my polygon has the form of a C or something like that this algorithmn does not work because the center off mass is outside the polygon.
Does anyone has an idea how to get always points inside any polygon?
You can use polygon triangulation to break your polygon apart into triangles.
One such algorithm is demonstrated using c# in this CodeProject article.
Once you have triangles, finding arbitrary points that lie within the triangle is easy. Any barycentric coordinate with a sum of 1.0 multiplied by the vertices of the triangle will give you a point inside the triangle.
The center can be derived using the barycentric coordinate [0.333333, 0.333333, 0.333333] :
float centerX = A.x * 0.333333 + B.x * 0.333333 + C.x * 0.3333333;
float centerY = A.y * 0.333333 + B.y * 0.333333 + C.y * 0.3333333;
or more simply:
float centerX = (A.x + B.x + C.x) / 3f;
float centerY = (A.y + B.y + C.y) / 3f;
Use This:
private Point getCentroid(pointArray)
{
double centroidX = 0.0;
double centroidY = 0.0;
for (int i = 0; i < pointArray.Length; i++)
{
centroidX += pointArray[i].X;
centroidY += pointArray[i].Y;`
}
centroidX /= pointArray.Length;
centroidY /= pointArray.Length;
return(new Point(centroidX ,centroidY));
}
this code is just to find Center of Mass of Polygon. To check whether a point is inside or outside polygon check this link http://bbs.dartmouth.edu/~fangq/MATH/download/source/Determining%20if%20a%20point%20lies%20on%20the%20interior%20of%20a%20polygon.htm

How to find a point where a line intersects an ellipse in 2D (C#)

I need to find a point where a line (its origin is ellipse' center) intersects an ellipse in 2D... I can easily find a point on a circle, because I know an angle F and the circle' radius (R):
x = x0 + R * cosF
y = y0 + R * sinF
However I just can't figure how am I supposed to deal with an ellipse... I know it's dimensions (A & B), but what is the way of finding parameter T?!
x = x0 + A * cosT
y = y0 + B * sinT
From what I understand the parameter T (T angle) is not far from the F angle (approximately +-15 degrees in some cases), but I just can't figure how to calculate it!!!
If there is a kind hearted soul, please help me with this problem...
The standard equation of an ellipse, stationed at 0,0, is:
1 = (x)^2 / (a) + (y)^2 / (b)
Where a is 1/2 the diameter on the horizontal axis, and b is 1/2 the diameter on the vertical axis.
you have a line, assuming an equation:
y = (m)(x - x0) + y0
So, let us plug-and-play!
1 = (x)^2 / (a) + (m(x - x0) + y0)^2 / (b)
1 = x^2 / a + (mx + (y0 - mx0))^2 / b
1 = x^2 / a + (m^2 * x^2 + 2mx*(y0 - mx0) + (y0 - mx0)^2) / b
1 = x^2 / a + (m^2 x^2) / b + (2mx*(y0 - mx0) + (y0^2 - 2y0mx0 + m^2*x0^2)) / b
1 = ((x^2 * b) / (a * b)) + ((m^2 * x^2 * a) / (a * b)) + (2mxy0 - 2m^2xx0)/b + (y0^2 - 2y0mx0 + m^2*x0^2)/b
1 = ((bx^2 + am^2x^2)/(ab)) + (x*(2my0 - 2m^2x0))/b + (y0^2 - 2y0mx0 + m^2*x0^2)/b
0 = x^2*((b + a*m^2)/(ab)) + x*((2my0 - 2m^2x0)/b) + (((y0^2 - 2y0mx0 + m^2*x0^2)/b) - 1)
That last equation follows the form of a standard quadratic equation.
So just use the quadratic formula, with:
((b + a*m^2)/(ab))
((2my0 - 2m^2x0)/b)
and
(((y0^2 - 2y0mx0 + m^2*x0^2)/b) - 1)
to get the X values at the intersections; Then, plug in those values into your original line equation to get the Y values.
Good luck!
Don't do it this way. Instead check the equation that forms an ellipse and that forming a line and solve the set:
The ellipse: (x/a)^2 + (y/b)^2 = 1
Your line: y = cx
You know a, b and c, so finding a solution is going to be easy. You'll find two solutions, because the line crosses the ellipse twice.
EDIT: Note I moved your ellipse's center to (0,0). It makes everything easier. Just add (x0,y0) to the solution.
public Hits<float2> EllipseLineIntersection ( float rx , float ry , float2 p1 , float2 p2 )
{
Hits<float2> hits = default(Hits<float2>);
float2 p3, p4;
Rect rect = default(Rect);
{
rect.xMin = math.min(p1.x,p2.x);
rect.xMax = math.max(p1.x,p2.x);
rect.yMin = math.min(p1.y,p2.y);
rect.yMax = math.max(p1.y,p2.y);
}
float s = ( p2.y - p1.y )/( p2.x - p1.x );
float si = p2.y - ( s * p2.x );
float a = ( ry*ry )+( rx*rx * s*s );
float b = 2f * rx*rx * si * s;
float c = rx*rx * si*si - rx*rx * ry*ry;
float radicand_sqrt = math.sqrt( ( b*b )-( 4f * a * c) );
p3.x = ( -b - radicand_sqrt )/( 2f*a );
p4.x = ( -b + radicand_sqrt )/( 2f*a );
p3.y = s*p3.x + si;
p4.y = s*p4.x + si;
if( rect.Contains(p3) ) hits.Push( p3 );
if( rect.Contains(p4) ) hits.Push( p4 );
return hits;
}
public struct Hits<T>
{
public byte count;
public T point0, point1;
public void Push ( T val )
{
if( count==0 ) { point0 = val; count ++; }
else if( count==1 ) { point1 = val; count ++; }
else print("This structure can only fit 2 values");
}
}
I wrote a C# code for your problem and I hope you can find it helpful. the distance function inside this code calculates euclidean distance between two points in space.
wX denotes horizontal radios of ellipse and wY denotes vertical radios.
private PointF LineIntersectEllipse(PointF A, PointF B, float wX, float wY)
{
double dx = B.X - A.X;
double dy = B.Y - A.Y;
double theta = Math.Atan2(dy, dx);
double r = distance(A, B) - ((wX * wY) / Math.Sqrt(Math.Pow(wY * Math.Cos(theta), 2) + Math.Pow(wX * Math.Sin(theta), 2)));
return PointF((float)(A.X + r * Math.Cos(theta)), (float)(A.Y + r * Math.Sin(theta)));
}
Andrew Łukasik posted a good and useful answer, however it is not using regular C# types. As I wrote in the comments, I converted the code using System.Drawing objects PointF and RectangleF. I found out that if the points given as parameters are aligned as a vertical or horizontal line, then "rect" will have a width or a height equal to 0. Then, rect.Contains(point) will return false even if the point is on this line.
I also modified the "Hits" structure to check if the point pushed is not already existing, which is the case if the line is perfectly tangent, then p3 and p4 will have same coordinates, as the exact tangent point is the only crossing point.
Here is the new code taking care of all the cases :
public static Hits<PointF> EllipseLineIntersection0(float rx, float ry, PointF p1, PointF p2)
{
Hits<PointF> hits = default(Hits<PointF>);
PointF p3 = new PointF();
PointF p4 = new PointF();
var rect = default(RectangleF);
rect.X = Math.Min(p1.X, p2.X);
rect.Width = Math.Max(p1.X, p2.X) - rect.X;
rect.Y = Math.Min(p1.Y, p2.Y);
rect.Height = Math.Max(p1.Y, p2.Y) - rect.Y;
float s = (p2.Y - p1.Y) / (p2.X - p1.X);
float si = p2.Y - (s * p2.X);
float a = (ry * ry) + (rx * rx * s * s);
float b = 2f * rx * rx * si * s;
float c = rx * rx * si * si - rx * rx * ry * ry;
float radicand_sqrt = (float)Math.Sqrt((b * b) - (4f * a * c));
p3.X = (-b - radicand_sqrt) / (2f * a);
p4.X = (-b + radicand_sqrt) / (2f * a);
p3.Y = s * p3.X + si;
p4.Y = s * p4.X + si;
if (rect.Width == 0)
{
if (p3.Y >= rect.Y && p3.Y <= rect.Y + rect.Height) hits.Push(p3);
if (p4.Y >= rect.Y && p4.Y <= rect.Y + rect.Height) hits.Push(p4);
}
else if (rect.Height == 0)
{
if (p3.X >= rect.X && p3.X <= rect.X + rect.Width) hits.Push(p3);
if (p4.X >= rect.X && p4.X <= rect.X + rect.Width) hits.Push(p4);
}
else
{
if (rect.Contains(p3)) hits.Push(p3);
if (rect.Contains(p4)) hits.Push(p4);
}
return hits;
}
public struct Hits<T>
{
public byte Count;
public T P0, P1;
public void Push(T val)
{
if (Count == 0) { P0 = val; Count++; }
else if (Count == 1) { if (!P0.Equals(val)) { P1 = val; Count++; } }
else throw new OverflowException("Structure Hits can only fit 2 values.");
}
}

Categories

Resources