2D Rotated rectangle contains point calculation - c#

My issue is that I've been trying to check if a rectangle that is rotated by a certain amount of degrees contain a point, but I wasn't able to calculate that after many attempts with the help of some code samples and examples that I've found online.
What I got is the rectangle (X, Y, Width, Height, Rotation) and the point (X, Y) and I've been trying to create a simple function that lets me instantly calculate that, which would be something something like this:
public bool Contains(Rect Rectangle, float RectangleRotation, Point PointToCheck);
But as I mentioned, I wasn't able to do so, those mathematical calculations that include some formulas I found online are way too much for me to understand.
Could someone help me with calculating this? If you could provide the calculation in C# code form (not formulas) then that would be great! Thanks.
PS: Using the 2D Physics Engine that is available in Unity3D is not a option, my rectangle is not associated with a gameobject that I could attach a 2D collision component to, I need to do this mathematically without the involvement of gameobjects or components.
Edit: I forgot to mention, the rectangle is being rotated by the middle of the rectangle (center/origin).

Rather than checking if the point is in a rotated rectangle, just apply the opposite of the rotation to the point and check if the point is in a normal rectangle. In other words, change your perspective by rotating everything by -RectangleRotation, so that the rectangle does not appear rotated at all.
public bool Contains(Rect rect, float rectAngle, Point point)
{
// rotate around rectangle center by -rectAngle
var s = Math.Sin(-rectAngle);
var c = Math.Cos(-rectAngle);
// set origin to rect center
var newPoint = point - rect.center;
// rotate
newPoint = new Point(newPoint.x * c - newPoint.y * s, newPoint.x * s + newPoint.y * c);
// put origin back
newPoint = newPoint + rect.center;
// check if our transformed point is in the rectangle, which is no longer
// rotated relative to the point
return newPoint.x >= rect.xMin && newPoint.x <= rect.xMax && newPoint.y >= rect.yMin && newPoint.y <= rect.yMax;
}

Related

Issue with trigonometry using Xamarin.Forms.Map.Position

I am working on a mobile app in C# using the Xamarin framework. I am trying to move a point by a fixed angle on a map like in the first part of the gif below. I believe I am using the right mathematical functions to compute the coordinates of the shifted points since in first part of the GIF, in GeoGebra, everything seems to be fine.
But when it comes to the actual in-app implementation, the results are quite weird : the angle is not consistent and the distance between the center and the points varies by moving the target.
The GIF showing the issue
I don't have a clue about what is wrong with the code. In the code below I use polylineOptions to draw the lines but I've tried with a Polygon and it displays the same results. Maybe it's because customMap.UserPin.Position returns the coordinates in Decimal Degree format (i.g. 34.00462, -4.512221) and the gap between two position is too small for a double.
Here are the two functions used to draw the lines.
// Add a cone's side to the variable coneLines
private void addConePolyline(double angle, CustomMap customMap, LatLng userPos)
{
// The coordinates of the end of the side to be drawn
LatLng conePoint = movePoint(angle, customMap.UserPin.Position, customMap.TargetPin.Position);
var polylineOptions = new PolylineOptions();
polylineOptions.InvokeWidth(10f);
polylineOptions.InvokeColor(Android.Graphics.Color.Argb(240, 255, 20, 147)); // Pink
polylineOptions.Add(userPos);
polylineOptions.Add(conePoint);
// Add the line to coneLines
coneLines.Add(map.AddPolyline(polylineOptions));
}
// Moves a point by the given angle on a circle of center rotationCenter with respect to p
private LatLng movePoint(double angle, Position rotationCenter, Position initialPoint)
{
// Compute the components of the translation vector between rotationCenter and initialPoint
double dx = initialPoint.Latitude - rotationCenter.Latitude;
double dy = initialPoint.Longitude - rotationCenter.Longitude;
// Compute the moved point's position
double x = rotationCenter.Latitude + Math.Cos(angle) * dx - Math.Sin(angle) * dy;
double y = rotationCenter.Longitude + Math.Sin(angle) * dx + Math.Cos(angle) * dy;
LatLng res = new LatLng(x, y);
return res;
}
I hope someone can help me with this!
Thank you.

Detect if a vector2 is between two other vector2

as the title suggests I am trying different ways but without solving.
I would need to check if the mouse is between two 2d points in PlayMode.
I thought of using rect to build a rectangle between two vector2 on the fly and check with rect.contains if the mouse position is inside the rectangle. This works if the rectangle is horizontal but the points on the screen are not aligned and it does not seem to me that there is a way to create an oblique rectangle.
I tried to calculate the distance between the two points, to create the rectangle and to move its center to rotate it but it doesn't seem to work. Also it seems that there is no way to display the rectangle on the screen and therefore it is difficult to understand if it retains its original shape after changing some parameters.
Do you have any suggestions? But I need the detection area to be rectangular and not on a single line. Using LineCast it is possible to solve but the detected area is too narrow.
I thought of a rectangle between the two vectors. Imagine a one-pixel line connecting the two vectors. Now increase the thickness of the line up to 10/20 pixels so that the mouse is detected a little before and a little after the original line. Maybe I'm wrong but I see the new rect as a good solution but as far as I looked at all its properties I can't understand how to build it between two vectors without too many lines of code.
You can do some vector math to:
Determine if the test point is not "outside" of the two points.
Calculate the distance from the line segment your point is, and if that's equal or below some threshold, then consider it to be "near" the segment between the two points.
If it's "near" the segment and not "outside" the two points, then consider it to be "between" the points.
For example:
Vector3 point1 = new Vector2(10f, 10f);
Vector3 point2 = new Vector2(50f, 50f);
float closeThreshold = 5f;
Vector3 testPoint = new Vector2(20f, 21f);
Vector3 segmentDirection = (point2-point1).normalized;
Vector3 point1ToTest = testPoint - point1;
Vector3 testToPoint2 = point2 - testPoint;
// determine if testPoint is not "outside" the points
if ( Vector3.Dot(segmentDirection, point1ToTest.normalized) >= 0f
&& Vector3.Dot(segmentDirection, testToPoint2.normalized) >= 0f)
{
Vector3 closestPointOnSegment = point1
+ segmentDirection * Vector3.Dot(point1ToTest, segmentDirection);
// determine if testPoint is close enough to the line segment point1->point2
if ((closestPointOnSegment - testPoint).magnitude <= closeThreshold)
{
// testPoint is "between" point1 and point2
}
}
This will work with Vector3 or Vector2, which will safely convert to Vector3 as shown above.
If you are only concerned with if the point is exactly between the two positions, take the directions from the point you're testing to either end of the line, and if the dot product between those directions is -1, then the point is between them:
Vector3 point1 = new Vector2(-1f,-1f);
Vector3 point2 = new Vector2(1f,1f);
Vector3 testPoint = new Vector2(0f,0f);
Vector3 testTo1 = (point1-testPoint).normalized;
Vector3 testTo2 = (point2-testPoint).normalized;
if (Mathf.Approximately(-1f, Vector3.Dot(testTo1, testTo2)))
{
// testPoint is between point1 and point2
}
This will work with Vector3 or Vector2, which will safely convert to Vector3 as above.

Finding points on a cylinder in 3d room c#

Example Image here
I am trying to find a way to calculate points on my cylinders top circle surface. My situation looks like this, I have a vector which is defining my cylinders direction in 3d room. Then I already calculated me a perpendicular vector with
Vector3.Cross(vector1, vector2)
Now I use the diameter/2 to calculate the point which is lying on the edge of the circular top surface of my cylinder. Now I want to rotate my vector always 90 degrees in order to get 4 points on the edge of the surface. All the 4 vectors defining them should be perpendicular to the cylinders direction. Can you help me how I can rotate the first perpendicular to achieve this?
I already tried:
Matrix4x4.CreateFromAxisAngle(vectorcylinderdirection, radiant)
Then I calculated again cross product but it doesnt work like I want to.
Edit:
public static void calculatePontsOnCylinder()
{
//Calculate Orthogonal Vector to Direction
Vector3 tCylinderDirection = new Vector3(1, 0, 0);
Vector3 tOrthogonal = Vector3.Cross(tCylinderDirection, new Vector3(-tCylinderDirection.Z,tCylinderDirection.X,tCylinderDirection.Y));
Vector3 tNormOrthogonal = Vector3.Normalize(tOrthogonal);
//Calculate point on surface circle of cylinder
//10mm radius
int tRadius = 10;
Vector3 tPointFinder = tNormOrthogonal * tRadius;
//tPointFinder add the cylinder start point
//not yet implemented
//now i need to rotate the vector always 90 degrees to find the 3 other points on the circular top surface of the cylinder
//don't know how to do this
// I thought this should do it
Matrix4x4.CreateFromAxisAngle(tCylinderDirection, (float)DegreeToRadian(90));
}
private static double DegreeToRadian(double angle)
{
return Math.PI * angle / 180.0;
}
In the picture you can see a example, the vector1 is what I need, always rotated 90 degrees and vector2 would be my cylinder direction vector
I possibly have found the correct formula:
Vector3 tFinal = Vector3.Multiply((float)Math.Cos(DegreeToRadian(90)), tPointFinder) + Vector3.Multiply((float)Math.Sin(DegreeToRadian(90)), Vector3.Cross(tCylinderDirection, tPointFinder));
Vector3 tFinal180 = Vector3.Multiply((float)Math.Cos(DegreeToRadian(180)), tPointFinder) + Vector3.Multiply((float)Math.Sin(DegreeToRadian(180)), Vector3.Cross(tCylinderDirection, tPointFinder));
Vector3 tFinal270= Vector3.Multiply((float)Math.Cos(DegreeToRadian(270)), tPointFinder) + Vector3.Multiply((float)Math.Sin(DegreeToRadian(270)), Vector3.Cross(tCylinderDirection, tPointFinder));
Interesting is that if I try it with (1,1,0) as cylinder direction it gives me correct directions but the length is different for 90 degrees and 270.
Here is the code that should solve your problem assuming that the input requirements are satisfied.
float zCutPlaneLocation = 20; // should not get bigger than cylinder length
float cylinderRadius = 100;
Vector3 cylinderCenter = new Vector3(0, 0, 0); // or whatever you got as cylinder center point, given as Vector3 since Point type is not defined
// will return 360 points on cylinder edge, corresponding to this z section (cut plane),
// another z section will give another 360 points and so on
List<Vector3> cylinderRotatedPointsIn3D = new List<Vector3>();
for (int angleToRotate = 0; angleToRotate < 360; angleToRotate++)
{
cylinderRotatedPointsIn3D.Add(GetRotatedPoint(zCutPlaneLocation, angleToRotate, cylinderRadius, cylinderCenter));
}
....
private static Vector3 GetRotatedPoint(
float zLocation, double rotationAngleInRadian, float cylinderRadius, Vector3 cylinderCenter)
{
Vector2 cylinderCenterInSection = new Vector2(cylinderCenter.X, cylinderCenter.Y);
float xOfRotatedPoint = cylinderRadius * (float)Math.Cos(rotationAngleInRadian);
float yOfRotatedPoint = cylinderRadius * (float)Math.Sin(rotationAngleInRadian);
Vector2 rotatedVector = new Vector2(xOfRotatedPoint, yOfRotatedPoint);
Vector2 rotatedSectionPointOnCylinder = rotatedVector + cylinderCenterInSection;
Vector3 rotatedPointOnCylinderIn3D = new Vector3(
rotatedSectionPointOnCylinder.X,
rotatedSectionPointOnCylinder.Y,
zLocation + cylinderCenter.Z);
return rotatedPointOnCylinderIn3D;
}
I just created a console app for this. First part of code should be added in main method.
Working with those matrices seems is not that easy. Also I am not sure if your solution works ok for any kind of angle.
Here the idea is that the rotated points from cylinder are calculated in a section of the cylinder so in 2D than the result is moved in 3D by just adding the z where the Z section was made on cylinder. I suppose that world axis and cylinder axis are on the same directions. Also if your cylinder gets along (increases) on the X axis, instead of Z axis as in example just switch in code the Z with X.
I attached also a picture for more details. This should work if you have the cylinder center, radius, rotation angle and you know the length of the cylinder so that you create valid Z sections on cylinder. This could get tricky for clockwise/counter clock wise cases but lets see how it works for you.
If you want to handle this with matrices or whatever else I think that you will end up having this kind of result. So I think that you cannot have "all" the rotated points in just a list for the entire cylinder surface, they would depend on something like the rotated points of a Z section on the cylinder.

Bezier along Terrain Surface

What’d be the best way to go, if i paint a bezier Curve (set start and endpoints) to Unity Terrain, and i want the curve to folow the ups and downs from the ground.
right now i partly achieve it like this,(need to connect the new Points from groundedPoints, as new Beziers)
int SegmentCount = Mathf.Floor(BezierLength / SegmentLength);
//Rounded to the next lower integer
var groundedPoints = new List<Vector3>();
for(int i =0; i<SegmentCount;i++){
Vector3 p = GetPoint(BezierPoints,i / SegmentCount);
p = p.RayCastDown();
//RayCasting Down to get the Point on the Terrain
if(i == 0 || i < SegmentCount -1){
groundedPoints.Add(p);
}else{
if(p.y != groundedPoints[groundedPoints.Count-1].y){
groundedPoints.Add(p);
}
}
}
it’s right now kind of not that accurate, but it, doesn’t have to be a real accurate solution.
Maybe someone can give me a hint? thanks
Firstly i would recommend using Centripetal Catmull–Rom spline because it follows points more strictly, and need less points to generate(also only draws between p1 and p2), but i dont know what you want to achieve so:
I would transform your bezier into a 2d bezier, and only work in 2d space with it, then when you draw(render it visually) you give it a Y value by using https://docs.unity3d.com/ScriptReference/Terrain.SampleHeight.html
I do this with my splines, and it gives a quite accurate spline in the end(road generation)
PLEASE NOTE!:
That the implicit Vector2 and Vector3 conversion will not fit your needs, you need to add an extension method to convert Vector3 to Vector2 :)
(Vector(x,y,z) will be Vector(x,y) but you need Vector(x,z))
Edit 1:
Codesample how to read out a terrain actual height, via Terrain.SampleHeight(); by a Vector2 coordinate that you are sure is above a terrain, if the Vector2 is not above the terrain it will give you back null or the closets terrain height to it im not sure which one atm(can't test it now) :)
public static float GetPoint_On_Terrain(Vector2 point){
float terrainHeightAtPoint = Terrain.activeTerrain.SampleHeight(new Vector3(point.x, 0, point.y));
return new Vector3(point.x, terrainHeightAtPoint,point.y);
}

Draw 3D arc given start, middle, and endpoint

I am trying to make a user-defined arc with the Helix 3D toolkit. The user selects 3 points on the arc (start, middle, end) and the program finds the center of the circle and draws the arc from start to end. My problem is I'm not good at math and I am having problems making this work. My main problem is getting the start and end angles and having it draw arcs of all sizes accurately. Any help is appreciated. Here is my code:
private void Draw_Arc(object sender, MouseButtonEventArgs e)
{
linept = new List<Point3D>();
linept.Add(startPoint);
linept.Add(endPoint);
linept.Add((Point3D)GetPoints(e));
LinesVisual3D line = new LinesVisual3D();
line.Thickness = 2;
line.Color = Colors.Blue;
line.Points = linept;
port.Children.Add(line);
double startAngle, sweepAngle;
Point3D center = GetCenterOfArc(linept.ElementAt(0), linept.ElementAt(1), linept.ElementAt(2));
GetAngles(linept.ElementAt(0), linept.ElementAt(1), linept.ElementAt(2), out startAngle, out sweepAngle);
circle = new PieSliceVisual3D();
double RadiusX = Math.Abs(startPoint.X - center.X);
double RadiusY = Math.Abs(startPoint.Y - center.Y);
circle.Center = center;
if (RadiusX >= RadiusY)
circle.OuterRadius = RadiusX;
else
circle.OuterRadius = RadiusY;
circle.InnerRadius = circle.OuterRadius + 3;
circle.StartAngle = (180 / Math.PI * Math.Atan2(startPoint.Y - circle.Center.Y, startPoint.X - circle.Center.X));
circle.EndAngle = (180 / Math.PI * Math.Atan2(linept.ElementAt(2).Y - circle.Center.Y, linept.ElementAt(2).X - circle.Center.X));
port.Children.Add(circle);
}
I think that you have to know the center of the circle in order to know the starting and ending angle of the arc.
Say that you just have three points, and you want to find a circle that goes through all three, you basically have three equations with three variables:
(x-x0)^2 + (y-y0)^2 = R^2
(x-x1)^2 + (y-y1)^2 = R^2
(x-x2)^2 + (y-y2)^2 = R^2
Solving that can get a little tricky if you try to program that on your own and have average knowledge in math, but you can do it fairly easily using matrices. Read here for a bit information.
After you've solved the three equations, you should have X, Y, R.
X and Y will be the center point of the circle, and R - it's radius.
Now, as far as I remember, they count the arc's degrees starting from the positive X axis, going upwards. So you would need to calculate the angle between two lines - the line that stretches between the center to your floating point, and the line that stretches from your center point to the "limitless" right. You may just Google "calculate angle between two lines". Repeating that process for both your starting point and your ending point, will give each their respective entering/exiting angle.
The middle point isn't really used anymore, but the radius is. You just set it to be the radius and you're good to go.
I haven't really implemented anything - just giving you a fair direction. (and I bet that there's a much cleaner and nicer-to-work-with solution)

Categories